mirror of
https://github.com/goharbor/harbor.git
synced 2024-10-31 23:59:32 +01:00
Merge branch 'master' into remove-lib
This commit is contained in:
commit
a52b99e180
10
.travis.yml
10
.travis.yml
@ -1,23 +1,23 @@
|
||||
sudo: true
|
||||
language: go
|
||||
go:
|
||||
- 1.12.12
|
||||
- 1.13.4
|
||||
go_import_path: github.com/goharbor/harbor
|
||||
services:
|
||||
- docker
|
||||
dist: trusty
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.12.12
|
||||
- go: 1.13.4
|
||||
env:
|
||||
- UTTEST=true
|
||||
- go: 1.12.12
|
||||
- go: 1.13.4
|
||||
env:
|
||||
- APITEST_DB=true
|
||||
- go: 1.12.12
|
||||
- go: 1.13.4
|
||||
env:
|
||||
- APITEST_LDAP=true
|
||||
- go: 1.12.12
|
||||
- go: 1.13.4
|
||||
env:
|
||||
- OFFLINE=true
|
||||
- language: node_js
|
||||
|
@ -128,6 +128,8 @@ Harbor backend is written in [Go](http://golang.org/). If you don't have a Harbo
|
||||
| 1.7 | 1.9.2 |
|
||||
| 1.8 | 1.11.2 |
|
||||
| 1.9 | 1.12.12 |
|
||||
| 1.10 | 1.12.12 |
|
||||
| 1.11 | 1.13.4 |
|
||||
|
||||
Ensure your GOPATH and PATH have been configured in accordance with the Go environment instructions.
|
||||
|
||||
|
6
Makefile
6
Makefile
@ -9,7 +9,7 @@
|
||||
# compile_golangimage:
|
||||
# compile from golang image
|
||||
# for example: make compile_golangimage -e GOBUILDIMAGE= \
|
||||
# golang:1.12.12
|
||||
# golang:1.13.4
|
||||
# compile_core, compile_jobservice: compile specific binary
|
||||
#
|
||||
# build: build Harbor docker images from photon baseimage
|
||||
@ -138,7 +138,7 @@ GOINSTALL=$(GOCMD) install
|
||||
GOTEST=$(GOCMD) test
|
||||
GODEP=$(GOTEST) -i
|
||||
GOFMT=gofmt -w
|
||||
GOBUILDIMAGE=golang:1.12.12
|
||||
GOBUILDIMAGE=golang:1.13.4
|
||||
GOBUILDPATH=/harbor
|
||||
|
||||
# go build
|
||||
@ -323,7 +323,7 @@ build:
|
||||
-e CLAIRVERSION=$(CLAIRVERSION) -e CLAIRADAPTERVERSION=$(CLAIRADAPTERVERSION) -e VERSIONTAG=$(VERSIONTAG) \
|
||||
-e BUILDBIN=$(BUILDBIN) -e REDISVERSION=$(REDISVERSION) -e MIGRATORVERSION=$(MIGRATORVERSION) \
|
||||
-e CHARTMUSEUMVERSION=$(CHARTMUSEUMVERSION) -e DOCKERIMAGENAME_CHART_SERVER=$(DOCKERIMAGENAME_CHART_SERVER) \
|
||||
-e NPM_REGISTRY=$(NPM_REGISTRY) -e BASEIMAGETAG=${BASEIMAGETAG}
|
||||
-e NPM_REGISTRY=$(NPM_REGISTRY) -e BASEIMAGETAG=$(BASEIMAGETAG)
|
||||
|
||||
build_base_docker:
|
||||
@for name in chartserver clair clair-adapter core db jobservice log nginx notary-server notary-signer portal prepare redis registry registryctl; do \
|
||||
|
@ -12,10 +12,6 @@
|
||||
|![notification](docs/img/bell-outline-badged.svg)Community Meeting|
|
||||
|------------------|
|
||||
|The Harbor Project holds bi-weekly community calls in two different timezones. To join the community calls or to watch previous meeting notes and recordings, please visit the [meeting schedule](https://github.com/goharbor/community/blob/master/MEETING_SCHEDULE.md).|
|
||||
|
||||
We welcome you to join the below Harbor community events and meet with project maintainers and users:
|
||||
|
||||
**November 18-21, 2019**, [KubeCon US, San Diego](https://events19.linuxfoundation.org/events/kubecon-cloudnativecon-north-america-2019): Harbor Lunch & Learn led by Joe Beda, Intro and Deep-dive sessions.
|
||||
|
||||
</br> </br>
|
||||
|
||||
|
@ -43,25 +43,25 @@ You can compile the code by one of the three approaches:
|
||||
- Get official Golang image from docker hub:
|
||||
|
||||
```sh
|
||||
$ docker pull golang:1.12.12
|
||||
$ docker pull golang:1.13.4
|
||||
```
|
||||
|
||||
- Build, install and bring up Harbor without Notary:
|
||||
|
||||
```sh
|
||||
$ make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage
|
||||
$ make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage
|
||||
```
|
||||
|
||||
- Build, install and bring up Harbor with Notary:
|
||||
|
||||
```sh
|
||||
$ make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true
|
||||
$ make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true
|
||||
```
|
||||
|
||||
- Build, install and bring up Harbor with Clair:
|
||||
|
||||
```sh
|
||||
$ make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage CLAIRFLAG=true
|
||||
$ make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage CLAIRFLAG=true
|
||||
```
|
||||
|
||||
#### II. Compile code with your own Golang environment, then build Harbor
|
||||
|
@ -36,10 +36,10 @@ version | set harbor version
|
||||
#### EXAMPLE:
|
||||
|
||||
#### Build and run harbor from source code.
|
||||
make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true
|
||||
make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true
|
||||
|
||||
### Package offline installer
|
||||
make package_offline GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true
|
||||
make package_offline GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true
|
||||
|
||||
### Start harbor with notary
|
||||
make -e NOTARYFLAG=true start
|
||||
|
@ -4,7 +4,7 @@ set +e
|
||||
|
||||
usage(){
|
||||
echo "Usage: builder <golang image:version> <code path> <code release tag> <main.go path> <binary name>"
|
||||
echo "e.g: builder golang:1.11.2 github.com/helm/chartmuseum v0.9.0 cmd/chartmuseum chartm"
|
||||
echo "e.g: builder golang:1.13.4 https://github.com/helm/chartmuseum v0.9.0 cmd/chartmuseum chartm"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM golang:1.12.12
|
||||
FROM golang:1.13.4
|
||||
|
||||
ADD . /go/src/github.com/goharbor/harbor-scanner-clair/
|
||||
WORKDIR /go/src/github.com/goharbor/harbor-scanner-clair/
|
||||
|
@ -23,7 +23,7 @@ TEMP=`mktemp -d ${TMPDIR-/tmp}/clair-adapter.XXXXXX`
|
||||
git clone https://github.com/goharbor/harbor-scanner-clair.git $TEMP
|
||||
cd $TEMP; git checkout $VERSION; cd -
|
||||
|
||||
echo 'build the clair adapter binary bases on the golang:1.12.12'
|
||||
echo 'build the clair adapter binary bases on the golang:1.13.4'
|
||||
cp Dockerfile.binary $TEMP
|
||||
docker build -f $TEMP/Dockerfile.binary -t clair-adapter-golang $TEMP
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM golang:1.12.12
|
||||
FROM golang:1.13.4
|
||||
|
||||
ADD . /go/src/github.com/coreos/clair/
|
||||
WORKDIR /go/src/github.com/coreos/clair/
|
||||
|
@ -23,7 +23,7 @@ TEMP=`mktemp -d /$TMPDIR/clair.XXXXXX`
|
||||
git clone https://github.com/coreos/clair.git $TEMP
|
||||
cd $TEMP; git checkout $VERSION; cd -
|
||||
|
||||
echo 'build the clair binary bases on the golang:1.12.12'
|
||||
echo 'build the clair binary bases on the golang:1.13.4'
|
||||
cp Dockerfile.binary $TEMP
|
||||
docker build -f $TEMP/Dockerfile.binary -t clair-golang $TEMP
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM golang:1.12.12
|
||||
FROM golang:1.13.4
|
||||
|
||||
ARG NOTARY_VERSION
|
||||
ARG MIGRATE_VERSION
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM golang:1.12.12
|
||||
FROM golang:1.13.4
|
||||
|
||||
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
||||
ENV BUILDTAGS include_oss include_gcs
|
||||
|
@ -50,7 +50,7 @@ type ProjectAPI struct {
|
||||
}
|
||||
|
||||
const projectNameMaxLen int = 255
|
||||
const projectNameMinLen int = 2
|
||||
const projectNameMinLen int = 1
|
||||
const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*`
|
||||
|
||||
// Prepare validates the URL and the user
|
||||
|
@ -119,7 +119,7 @@ func TestAddProject(t *testing.T) {
|
||||
// case 4: response code = 400 : Project name is illegal in length
|
||||
fmt.Println("case 4 : response code = 400 : Project name is illegal in length ")
|
||||
|
||||
result, err = apiTest.ProjectsPost(*admin, apilib.ProjectReq{ProjectName: "t", Metadata: map[string]string{models.ProMetaPublic: "true"}})
|
||||
result, err = apiTest.ProjectsPost(*admin, apilib.ProjectReq{ProjectName: "", Metadata: map[string]string{models.ProMetaPublic: "true"}})
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
|
@ -34,7 +34,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/core/auth"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"github.com/goharbor/harbor/src/pkg/authproxy"
|
||||
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
|
||||
)
|
||||
|
||||
const refreshDuration = 2 * time.Second
|
||||
@ -101,31 +100,12 @@ func (a *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
||||
s := session{}
|
||||
err = json.Unmarshal(data, &s)
|
||||
if err != nil {
|
||||
log.Errorf("failed to read session %v", err)
|
||||
return nil, auth.NewErrAuth(fmt.Sprintf("failed to read session %v", err))
|
||||
}
|
||||
|
||||
reviewResponse, err := a.tokenReview(s.SessionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reviewResponse == nil {
|
||||
return nil, auth.ErrAuth{}
|
||||
}
|
||||
|
||||
// Attach user group ID information
|
||||
ugList := reviewResponse.Status.User.Groups
|
||||
log.Debugf("user groups %+v", ugList)
|
||||
if len(ugList) > 0 {
|
||||
userGroups := models.UserGroupsFromName(ugList, common.HTTPGroupType)
|
||||
groupIDList, err := group.PopulateGroup(userGroups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("current user's group ID list is %+v", groupIDList)
|
||||
user.GroupIDs = groupIDList
|
||||
if err := a.tokenReview(s.SessionID, user); err != nil {
|
||||
return nil, auth.NewErrAuth(err.Error())
|
||||
}
|
||||
return user, nil
|
||||
|
||||
} else if resp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, auth.ErrAuth{}
|
||||
} else {
|
||||
@ -134,17 +114,24 @@ func (a *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
||||
log.Warningf("Failed to read response body, error: %v", err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to authenticate, status code: %d, text: %s", resp.StatusCode, string(data))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (a *Auth) tokenReview(sessionID string) (*k8s_api_v1beta1.TokenReview, error) {
|
||||
func (a *Auth) tokenReview(sessionID string, user *models.User) error {
|
||||
httpAuthProxySetting, err := config.HTTPAuthProxySetting()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return authproxy.TokenReview(sessionID, httpAuthProxySetting)
|
||||
reviewStatus, err := authproxy.TokenReview(sessionID, httpAuthProxySetting)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u2, err := authproxy.UserFromReviewStatus(reviewStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.GroupIDs = u2.GroupIDs
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnBoardUser delegates to dao pkg to insert/update data in DB.
|
||||
|
@ -325,16 +325,16 @@ func (ap *authProxyReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||
log.Errorf("fail to get auth proxy settings, %v", err)
|
||||
return false
|
||||
}
|
||||
tokenReviewResponse, err := authproxy.TokenReview(proxyPwd, httpAuthProxyConf)
|
||||
tokenReviewStatus, err := authproxy.TokenReview(proxyPwd, httpAuthProxyConf)
|
||||
if err != nil {
|
||||
log.Errorf("fail to review token, %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !tokenReviewResponse.Status.Authenticated {
|
||||
log.Errorf("fail to auth user: %s", rawUserName)
|
||||
if rawUserName != tokenReviewStatus.User.Username {
|
||||
log.Errorf("user name doesn't match with token: %s", rawUserName)
|
||||
return false
|
||||
}
|
||||
|
||||
user, err := dao.GetUser(models.User{
|
||||
Username: rawUserName,
|
||||
})
|
||||
@ -346,11 +346,12 @@ func (ap *authProxyReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||
log.Errorf("User: %s has not been on boarded yet.", rawUserName)
|
||||
return false
|
||||
}
|
||||
if rawUserName != tokenReviewResponse.Status.User.Username {
|
||||
log.Errorf("user name doesn't match with token: %s", rawUserName)
|
||||
u2, err := authproxy.UserFromReviewStatus(tokenReviewStatus)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get user information from token review status, error: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
user.GroupIDs = u2.GroupIDs
|
||||
pm := config.GlobalProjectMgr
|
||||
log.Debug("creating local database security context for auth proxy...")
|
||||
securCtx := local.NewSecurityContext(user, pm)
|
||||
|
@ -2,6 +2,9 @@ package authproxy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/dao/group"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
|
||||
@ -13,8 +16,9 @@ import (
|
||||
)
|
||||
|
||||
// TokenReview ...
|
||||
func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_api_v1beta1.TokenReview, error) {
|
||||
func TokenReview(rawToken string, authProxyConfig *models.HTTPAuthProxy) (k8s_api_v1beta1.TokenReviewStatus, error) {
|
||||
|
||||
emptyStatus := k8s_api_v1beta1.TokenReviewStatus{}
|
||||
// Init auth client with the auth proxy endpoint.
|
||||
authClientCfg := &rest.Config{
|
||||
Host: authProxyConfig.TokenReviewEndpoint,
|
||||
@ -22,14 +26,14 @@ func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_
|
||||
GroupVersion: &schema.GroupVersion{},
|
||||
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
|
||||
},
|
||||
BearerToken: sessionID,
|
||||
BearerToken: rawToken,
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
Insecure: !authProxyConfig.VerifyCert,
|
||||
},
|
||||
}
|
||||
authClient, err := rest.RESTClientFor(authClientCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return emptyStatus, err
|
||||
}
|
||||
|
||||
// Do auth with the token.
|
||||
@ -39,27 +43,49 @@ func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_
|
||||
APIVersion: "authentication.k8s.io/v1beta1",
|
||||
},
|
||||
Spec: k8s_api_v1beta1.TokenReviewSpec{
|
||||
Token: sessionID,
|
||||
Token: rawToken,
|
||||
},
|
||||
}
|
||||
res := authClient.Post().Body(tokenReviewRequest).Do()
|
||||
err = res.Error()
|
||||
if err != nil {
|
||||
log.Errorf("fail to POST auth request, %v", err)
|
||||
return nil, err
|
||||
return emptyStatus, err
|
||||
}
|
||||
resRaw, err := res.Raw()
|
||||
if err != nil {
|
||||
log.Errorf("fail to get raw data of token review, %v", err)
|
||||
return nil, err
|
||||
return emptyStatus, err
|
||||
}
|
||||
// Parse the auth response, check the user name and authenticated status.
|
||||
tokenReviewResponse := &k8s_api_v1beta1.TokenReview{}
|
||||
err = json.Unmarshal(resRaw, &tokenReviewResponse)
|
||||
err = json.Unmarshal(resRaw, tokenReviewResponse)
|
||||
if err != nil {
|
||||
log.Errorf("fail to decode token review, %v", err)
|
||||
return nil, err
|
||||
return emptyStatus, err
|
||||
}
|
||||
return tokenReviewResponse, nil
|
||||
return tokenReviewResponse.Status, nil
|
||||
|
||||
}
|
||||
|
||||
// UserFromReviewStatus transform a review status to a user model.
|
||||
// Group entries will be populated if needed.
|
||||
func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus) (*models.User, error) {
|
||||
if !status.Authenticated {
|
||||
return nil, fmt.Errorf("failed to authenticate the token, error in status: %s", status.Error)
|
||||
}
|
||||
user := &models.User{
|
||||
Username: status.User.Username,
|
||||
}
|
||||
if len(status.User.Groups) > 0 {
|
||||
userGroups := models.UserGroupsFromName(status.User.Groups, common.HTTPGroupType)
|
||||
groupIDList, err := group.PopulateGroup(userGroups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("current user's group ID list is %+v", groupIDList)
|
||||
user.GroupIDs = groupIDList
|
||||
}
|
||||
return user, nil
|
||||
|
||||
}
|
||||
|
87
src/pkg/authproxy/http_test.go
Normal file
87
src/pkg/authproxy/http_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
package authproxy
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/dao/group"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/authentication/v1beta1"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dao.PrepareTestForPostgresSQL()
|
||||
result := m.Run()
|
||||
if result != 0 {
|
||||
os.Exit(result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserFromReviewStatus(t *testing.T) {
|
||||
type result struct {
|
||||
hasErr bool
|
||||
username string
|
||||
groupLen int
|
||||
}
|
||||
cases := []struct {
|
||||
input v1beta1.TokenReviewStatus
|
||||
expect result
|
||||
}{
|
||||
{
|
||||
input: v1beta1.TokenReviewStatus{
|
||||
Authenticated: false,
|
||||
Error: "connection error",
|
||||
},
|
||||
expect: result{
|
||||
hasErr: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: v1beta1.TokenReviewStatus{
|
||||
Authenticated: true,
|
||||
User: v1beta1.UserInfo{
|
||||
Username: "jack",
|
||||
UID: "u-1",
|
||||
},
|
||||
},
|
||||
expect: result{
|
||||
hasErr: false,
|
||||
username: "jack",
|
||||
groupLen: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: v1beta1.TokenReviewStatus{
|
||||
Authenticated: true,
|
||||
User: v1beta1.UserInfo{
|
||||
Username: "daniel",
|
||||
Groups: []string{"group1", "group2"},
|
||||
},
|
||||
Error: "",
|
||||
},
|
||||
expect: result{
|
||||
hasErr: false,
|
||||
username: "daniel",
|
||||
groupLen: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
u, err := UserFromReviewStatus(c.input)
|
||||
if c.expect.hasErr == true {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, c.expect.username, u.Username)
|
||||
assert.Equal(t, c.expect.groupLen, len(u.GroupIDs))
|
||||
}
|
||||
if u != nil {
|
||||
for _, gid := range u.GroupIDs {
|
||||
t.Logf("Deleting group %d", gid)
|
||||
if err := group.DeleteUserGroup(gid); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, APP_INITIALIZER, LOCALE_ID, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { AppComponent } from './app.component';
|
||||
import { InterceptHttpService } from './intercept-http.service';
|
||||
|
||||
import { BaseModule } from './base/base.module';
|
||||
import { HarborRoutingModule } from './harbor-routing.module';
|
||||
@ -43,6 +44,8 @@ import { InterrogationServicesComponent } from "./interrogation-services/interro
|
||||
import { LabelsComponent } from './labels/labels.component';
|
||||
import { ProjectQuotasComponent } from './project-quotas/project-quotas.component';
|
||||
import { HarborLibraryModule } from "../lib/harbor-library.module";
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
||||
|
||||
registerLocaleData(zh, 'zh-cn');
|
||||
registerLocaleData(es, 'es-es');
|
||||
@ -95,7 +98,9 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
||||
deps: [ AppConfigService, SkinableConfig],
|
||||
multi: true
|
||||
},
|
||||
{provide: LOCALE_ID, useValue: "en-US"}
|
||||
{provide: LOCALE_ID, useValue: "en-US"},
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: InterceptHttpService, multi: true }
|
||||
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
|
58
src/portal/src/app/intercept-http.service.spec.ts
Normal file
58
src/portal/src/app/intercept-http.service.spec.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { InterceptHttpService } from './intercept-http.service';
|
||||
import { CookieService } from 'ngx-cookie';
|
||||
import { HttpRequest, HttpResponse } from '@angular/common/http';
|
||||
import { of, throwError } from 'rxjs';
|
||||
|
||||
describe('InterceptHttpService', () => {
|
||||
let cookie = "fdsa|ds";
|
||||
const mockCookieService = {
|
||||
get: function () {
|
||||
return cookie;
|
||||
},
|
||||
set: function (cookieStr: string) {
|
||||
cookie = cookieStr;
|
||||
}
|
||||
};
|
||||
const mockRequest = new HttpRequest('PUT', "", {
|
||||
headers: new Map()
|
||||
});
|
||||
const mockHandle = {
|
||||
handle: (request) => {
|
||||
if (request.headers.has('X-Xsrftoken')) {
|
||||
return of(new HttpResponse({status: 200}));
|
||||
} else {
|
||||
return throwError(new HttpResponse( {
|
||||
status: 403
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
InterceptHttpService,
|
||||
{ provide: CookieService, useValue: mockCookieService }
|
||||
]
|
||||
});
|
||||
|
||||
});
|
||||
it('should be initialized', inject([InterceptHttpService], (service: InterceptHttpService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should be get right token and send right request when the cookie not exists', inject([InterceptHttpService],
|
||||
(service: InterceptHttpService) => {
|
||||
mockCookieService.set("fdsa|ds");
|
||||
service.intercept(mockRequest, mockHandle).subscribe(res => {
|
||||
if (res.status === 403) {
|
||||
expect(btoa(mockRequest.headers.get("X-Xsrftoken"))).toEqual(cookie.split("|")[0]);
|
||||
} else {
|
||||
expect(res.status).toEqual(200);
|
||||
}
|
||||
});
|
||||
}));
|
||||
});
|
28
src/portal/src/app/intercept-http.service.ts
Normal file
28
src/portal/src/app/intercept-http.service.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse } from '@angular/common/http';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { tap, catchError } from 'rxjs/operators';
|
||||
import { CookieService } from 'ngx-cookie';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class InterceptHttpService implements HttpInterceptor {
|
||||
|
||||
constructor(private cookie: CookieService) { }
|
||||
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
|
||||
|
||||
return next.handle(request).pipe(catchError(error => {
|
||||
if (error.status === 403) {
|
||||
let Xsrftoken = this.cookie.get("_xsrf") ? atob(this.cookie.get("_xsrf").split("|")[0]) : null;
|
||||
if (Xsrftoken && !request.headers.has('X-Xsrftoken')) {
|
||||
request = request.clone({ headers: request.headers.set('X-Xsrftoken', Xsrftoken) });
|
||||
return next.handle(request);
|
||||
}
|
||||
}
|
||||
return throwError(error);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="clr-control-container" [class.clr-error]="!isNameValid">
|
||||
<div class="clr-input-wrapper">
|
||||
<input type="text" id="create_project_name" [(ngModel)]="project.name" name="create_project_name" class="clr-input input-width"
|
||||
required pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" minlength="2" #projectName="ngModel" autocomplete="off"
|
||||
required pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" #projectName="ngModel" autocomplete="off"
|
||||
(keyup)='handleValidation()'>
|
||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
|
||||
|
@ -214,9 +214,8 @@
|
||||
"PUBLIC_PROJECTS": "Public Projects",
|
||||
"PROJECT": "Project",
|
||||
"NEW_PROJECT": "New Project",
|
||||
"NAME_TOOLTIP": "Project name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
|
||||
"NAME_TOOLTIP": "Project name should be 1~255 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
|
||||
"NAME_IS_REQUIRED": "Project name is required.",
|
||||
"NAME_MINIMUM_LENGTH": "Project name is too short, it should be greater than 2 characters.",
|
||||
"NAME_ALREADY_EXISTS": "Project name already exists.",
|
||||
"NAME_IS_ILLEGAL": "Project name is invalid.",
|
||||
"UNKNOWN_ERROR": "An unknown error occurred while creating the project.",
|
||||
|
@ -214,10 +214,9 @@
|
||||
"PUBLIC_PROJECTS": "Public Projects",
|
||||
"PROJECT": "Proyecto",
|
||||
"NEW_PROJECT": "Nuevo proyecto",
|
||||
"NAME_TOOLTIP": "Project name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
|
||||
"NAME_TOOLTIP": "Project name should be 1~255 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
|
||||
"DESTINATION_NAME_TOOLTIP": "Destination name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
|
||||
"NAME_IS_REQUIRED": "El nombre del proyecto es obligatorio.",
|
||||
"NAME_MINIMUM_LENGTH": "El nombre del proyecto es demasiado corto, debe ser mayor de 2 caracteres.",
|
||||
"NAME_ALREADY_EXISTS": "Ya existe un proyecto con ese nombre.",
|
||||
"NAME_IS_ILLEGAL": "El nombre del proyecto no es valido.",
|
||||
"UNKNOWN_ERROR": "Ha ocurrido un error al crear el proyecto.",
|
||||
|
@ -208,9 +208,8 @@
|
||||
"PUBLIC_PROJECTS": "Projets Publics",
|
||||
"PROJECT": "Projet",
|
||||
"NEW_PROJECT": "Nouveau Projet",
|
||||
"NAME_TOOLTIP": "Le nom du projet doit comporter au moins 2 caractères avec des minuscules, des chiffres et. _- et doit commencer par des caractères ou des chiffres.",
|
||||
"NAME_TOOLTIP": "Le nom du projet doit comporter 1~255 caractères avec des minuscules, des chiffres et. _- et doit commencer par des caractères ou des chiffres.",
|
||||
"NAME_IS_REQUIRED": "Le nom du projet est obligatoire.",
|
||||
"NAME_MINIMUM_LENGTH": "Le nom du projet est trop court, il doit être supérieur à 2 caractères.",
|
||||
"NAME_ALREADY_EXISTS": "Le nom du projet existe déjà.",
|
||||
"NAME_IS_ILLEGAL": "Le nom du projet est invalide.",
|
||||
"UNKNOWN_ERROR": "Une erreur inconnue s'est produite lors de la création du projet.",
|
||||
|
@ -212,9 +212,8 @@
|
||||
"PUBLIC_PROJECTS": "Projetos Públicos",
|
||||
"PROJECT": "Projeto",
|
||||
"NEW_PROJECT": "Novo Projeto",
|
||||
"NAME_TOOLTIP": "Nome do projeto deve conter ao menos 2 caracteres sendo minusculos, números e ._- e deve iniciar com letras ou números.",
|
||||
"NAME_TOOLTIP": "Nome do projeto deve conter 1~255 caracteres sendo minusculos, números e ._- e deve iniciar com letras ou números.",
|
||||
"NAME_IS_REQUIRED": "Nome do projeto é obrigatório.",
|
||||
"NAME_MINIMUM_LENGTH": "Nome do projeto é muito curto, deve conter ao menos 2 caracteres.",
|
||||
"NAME_ALREADY_EXISTS": "Nome do projeto já existe.",
|
||||
"NAME_IS_ILLEGAL": "Nome do projeto é inválido.",
|
||||
"UNKNOWN_ERROR": "Um erro desconhecido ocorreu ao criar o projeto.",
|
||||
|
@ -214,9 +214,8 @@
|
||||
"PUBLIC_PROJECTS": "Genel Projeler",
|
||||
"PROJECT": "Proje",
|
||||
"NEW_PROJECT": "Yeni Proje",
|
||||
"NAME_TOOLTIP": "Proje adı, en az 2 karakter uzunluğunda, küçük harf, rakam ve ._- ile yazılmalı ve karakter veya rakamlarla başlamalıdır.",
|
||||
"NAME_TOOLTIP": "Proje adı, en az 1~255 karakter uzunluğunda, küçük harf, rakam ve ._- ile yazılmalı ve karakter veya rakamlarla başlamalıdır.",
|
||||
"NAME_IS_REQUIRED": "Proje adı gerekli.",
|
||||
"NAME_MINIMUM_LENGTH": "Proje adı çok kısa, 2 karakterden büyük olmalıdır.",
|
||||
"NAME_ALREADY_EXISTS": "Proje adı zaten var.",
|
||||
"NAME_IS_ILLEGAL": "Proje adı geçersiz.",
|
||||
"UNKNOWN_ERROR": "Proje oluşturulurken bilinmeyen bir hata oluştu.",
|
||||
|
@ -213,9 +213,8 @@
|
||||
"PUBLIC_PROJECTS": "公开项目",
|
||||
"PROJECT": "项目",
|
||||
"NEW_PROJECT": "新建项目",
|
||||
"NAME_TOOLTIP": "项目名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
|
||||
"NAME_TOOLTIP": "项目名称由小写字符、数字和._-组成且至少1个字符并以字符或者数字开头。",
|
||||
"NAME_IS_REQUIRED": "项目名称为必填项。",
|
||||
"NAME_MINIMUM_LENGTH": "项目名称长度过短,至少多于2个字符。",
|
||||
"NAME_ALREADY_EXISTS": "项目名称已存在。",
|
||||
"NAME_IS_ILLEGAL": "项目名称非法。",
|
||||
"UNKNOWN_ERROR": "创建项目时发生未知错误。",
|
||||
|
@ -93,6 +93,14 @@ export Harbor_Assets_Version=$Harbor_Assets_Version
|
||||
export Harbor_Package_Version=$Harbor_Package_Version
|
||||
export NPM_REGISTRY=$NPM_REGISTRY
|
||||
|
||||
# release branch must have their own base image with branch name, master and others will use the dev as base.
|
||||
if [[ $DRONE_BRANCH == "release-"* ]]; then
|
||||
Harbor_Build_Base_Tag=$DRONE_BRANCH
|
||||
else
|
||||
Harbor_Build_Base_Tag=dev
|
||||
fi
|
||||
export Harbor_Build_Base_Tag=$Harbor_Build_Base_Tag
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
echo "Harbor Package version: $Harbor_Package_Version"
|
||||
echo "Harbor Assets version: $Harbor_Assets_Version"
|
||||
|
@ -26,9 +26,8 @@ Go Into Project
|
||||
\ Retry Wait Element ${search_input}
|
||||
\ Retry Clear Element Text ${search_input}
|
||||
\ Input Text ${search_input} ${project}
|
||||
\ Retry Wait Until Page Contains ${project}
|
||||
\ ${out} Run Keyword If ${has_image}==${false} Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//clr-dg-placeholder[contains(.,\"We couldn\'t find any repositories!\")]
|
||||
\ ... ELSE Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//project-detail//hbr-repository-gridview//clr-dg-cell[contains(.,'${project}/')]
|
||||
\ ${out} Run Keyword If ${has_image}==${false} Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//clr-dg-placeholder[contains(.,\"We couldn\'t find any repositories!\")] DoAssert=${false}
|
||||
\ ... ELSE Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//project-detail//hbr-repository-gridview//clr-dg-cell[contains(.,'${project}/')] DoAssert=${false}
|
||||
\ Log To Console ${out}
|
||||
\ Run Keyword If ${out} == 'PASS' Exit For Loop
|
||||
\ Sleep 1
|
||||
|
@ -55,7 +55,7 @@ Package Harbor Offline
|
||||
[Arguments] ${with_notary}=true ${with_clair}=true ${with_migrator}=true ${with_chartmuseum}=true
|
||||
Log To Console \nStart Docker Daemon
|
||||
Start Docker Daemon Locally
|
||||
Log To Console \n\nmake package_offline NPM_REGISTRY=%{NPM_REGISTRY} VERSIONTAG=%{Harbor_Assets_Version} PKGVERSIONTAG=%{Harbor_Package_Version} NOTARYFLAG=${with_notary} CLAIRFLAG=${with_clair} MIGRATORFLAG=${with_migrator} CHARTFLAG=${with_chartmuseum} HTTPPROXY=
|
||||
Log To Console \n\nmake package_offline BASEIMAGETAG=%{Harbor_Build_Base_Tag} NPM_REGISTRY=%{NPM_REGISTRY} VERSIONTAG=%{Harbor_Assets_Version} PKGVERSIONTAG=%{Harbor_Package_Version} NOTARYFLAG=${with_notary} CLAIRFLAG=${with_clair} MIGRATORFLAG=${with_migrator} CHARTFLAG=${with_chartmuseum} HTTPPROXY=
|
||||
${rc} ${output}= Run And Return Rc And Output make package_offline NPM_REGISTRY=%{NPM_REGISTRY} VERSIONTAG=%{Harbor_Assets_Version} PKGVERSIONTAG=%{Harbor_Package_Version} NOTARYFLAG=${with_notary} CLAIRFLAG=${with_clair} MIGRATORFLAG=${with_migrator} CHARTFLAG=${with_chartmuseum} HTTPPROXY=
|
||||
Log To Console ${rc}
|
||||
Log To Console ${output}
|
||||
|
@ -246,7 +246,7 @@ Retry Keyword When Return Value Mismatch
|
||||
Should Be Equal As Strings ${status} 'PASS'
|
||||
|
||||
Retry Double Keywords When Error
|
||||
[Arguments] ${keyword1} ${element1} ${keyword2} ${element2}
|
||||
[Arguments] ${keyword1} ${element1} ${keyword2} ${element2} ${DoAssert}=${true}
|
||||
:For ${n} IN RANGE 1 5
|
||||
\ Log To Console Trying ${keyword1} and ${keyword2} ${n} times ...
|
||||
\ ${out1} Run Keyword And Ignore Error ${keyword1} ${element1}
|
||||
@ -257,8 +257,8 @@ Retry Double Keywords When Error
|
||||
\ Log To Console Return value is ${out1[0]} ${out2[0]}
|
||||
\ Exit For Loop If '${out2[0]}'=='PASS'
|
||||
\ Sleep 1
|
||||
Return From Keyword If ${DoAssert} == ${false} '${out2[0]}'
|
||||
Should Be Equal As Strings '${out2[0]}' 'PASS'
|
||||
[Return] 'PASS'
|
||||
|
||||
Run Curl And Return Json
|
||||
[Arguments] ${curl_cmd}
|
||||
|
@ -499,7 +499,7 @@ Test Case - Project Storage Quotas Dispaly And Control
|
||||
${storage_quota_unit}= Set Variable MB
|
||||
${image_a}= Set Variable redis
|
||||
${image_b}= Set Variable logstash
|
||||
${image_a_size}= Set Variable 34.16MB
|
||||
${image_a_size}= Set Variable 34.15MB
|
||||
${image_b_size}= Set Variable 321.03MB
|
||||
${image_a_ver}= Set Variable 5.0
|
||||
${image_b_ver}= Set Variable 6.8.3
|
||||
@ -508,11 +508,16 @@ Test Case - Project Storage Quotas Dispaly And Control
|
||||
Push Image With Tag ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_b} tag=${image_b_ver} tag1=${image_b_ver}
|
||||
${storage_quota_ret}= Get Project Storage Quota Text From Project Quotas List project${d}
|
||||
Should Be Equal As Strings ${storage_quota_ret} ${image_b_size} of ${storage_quota}${storage_quota_unit}
|
||||
Cannot Push image ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_a}:${image_a_ver} err_msg=Quota exceeded when processing the request of adding 25.9 MiB of storage resource, which when updated to current usage of 329.3 MiB will exceed the configured upper limit of 330.0 MiB
|
||||
Cannot Push image ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_a}:${image_a_ver} err_msg=Quota exceeded when processing the request of adding 25.8 MiB of storage resource, which when updated to current usage of 329.3 MiB will exceed the configured upper limit of 330.0 MiB
|
||||
Go Into Project project${d}
|
||||
Delete Repo project${d}/${image_b}
|
||||
Push Image With Tag ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_a} tag=${image_a_ver} tag1=${image_a_ver}
|
||||
${storage_quota_ret}= Get Project Storage Quota Text From Project Quotas List project${d}
|
||||
${storage_quota_ret_str_left} Fetch From Left ${storage_quota_ret} 25.
|
||||
Log ${storage_quota_ret_str_left}
|
||||
${storage_quota_ret_str_right} Fetch From Left ${storage_quota_ret} 25.
|
||||
Log ${storage_quota_ret_str_right}
|
||||
Log ${storage_quota_ret_str_left}${storage_quota_ret_str_right}
|
||||
Should Be Equal As Strings ${storage_quota_ret} ${image_a_size} of ${storage_quota}${storage_quota_unit}
|
||||
Close Browser
|
||||
|
||||
@ -603,7 +608,7 @@ Test Case - Update Webhook
|
||||
Create An New Project project${d}
|
||||
Go Into Project project${d} has_image=${false}
|
||||
Switch To Project Webhooks
|
||||
Create A New Webhook ${HARBOR_URL} auth_header=auth_header${d}
|
||||
Create A New Webhook ${HARBOR_URL} auth_header=auth_header${d}
|
||||
Sleep 3
|
||||
${d1}= Get Current Date
|
||||
Update A Webhook 101.17.109.20 auth_header=auth_header${d1}
|
||||
|
@ -24,5 +24,5 @@ sudo curl -o /home/travis/gopath/src/github.com/goharbor/harbor/tests/apitests/p
|
||||
sudo apt-get update && sudo apt-get install -y --no-install-recommends python-dev openjdk-7-jdk libssl-dev && sudo apt-get autoremove -y && sudo rm -rf /var/lib/apt/lists/*
|
||||
sudo wget https://bootstrap.pypa.io/get-pip.py && sudo python ./get-pip.py && sudo pip install --ignore-installed urllib3 chardet requests && sudo pip install robotframework==3.0.4 robotframework-httplibrary requests dbbot robotframework-pabot --upgrade
|
||||
sudo make swagger_client
|
||||
sudo make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage CLARITYIMAGE=goharbor/harbor-clarity-ui-builder:1.6.0 NOTARYFLAG=true CLAIRFLAG=true CHARTFLAG=true
|
||||
sudo make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage CLARITYIMAGE=goharbor/harbor-clarity-ui-builder:1.6.0 NOTARYFLAG=true CLAIRFLAG=true CHARTFLAG=true
|
||||
sleep 10
|
||||
|
@ -2,5 +2,5 @@
|
||||
|
||||
set -e
|
||||
|
||||
sudo make package_online VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
|
||||
sudo make package_offline VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
|
||||
sudo make package_online VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
|
||||
sudo make package_offline VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
|
||||
|
Loading…
Reference in New Issue
Block a user