diff --git a/.travis.yml b/.travis.yml index 3888aaf00..377707af2 100644 --- a/.travis.yml +++ b/.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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b61acba5..b92e46104 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/Makefile b/Makefile index e85593758..b5f0cd211 100644 --- a/Makefile +++ b/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 \ diff --git a/README.md b/README.md index ce81ac087..0ccebf6d1 100644 --- a/README.md +++ b/README.md @@ -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.

diff --git a/docs/compile_guide.md b/docs/compile_guide.md index ab3832fe3..f85ce8a32 100644 --- a/docs/compile_guide.md +++ b/docs/compile_guide.md @@ -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 diff --git a/docs/use_make.md b/docs/use_make.md index f6deb28e8..ab249d941 100644 --- a/docs/use_make.md +++ b/docs/use_make.md @@ -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 diff --git a/make/photon/chartserver/builder b/make/photon/chartserver/builder index 9a5c0239a..0c48e949f 100755 --- a/make/photon/chartserver/builder +++ b/make/photon/chartserver/builder @@ -4,7 +4,7 @@ set +e usage(){ echo "Usage: builder " - 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 } diff --git a/make/photon/clair-adapter/Dockerfile.binary b/make/photon/clair-adapter/Dockerfile.binary index a1a808711..c5bc787e6 100644 --- a/make/photon/clair-adapter/Dockerfile.binary +++ b/make/photon/clair-adapter/Dockerfile.binary @@ -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/ diff --git a/make/photon/clair-adapter/builder b/make/photon/clair-adapter/builder index 50acd7c3b..4ab97c949 100755 --- a/make/photon/clair-adapter/builder +++ b/make/photon/clair-adapter/builder @@ -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 diff --git a/make/photon/clair/Dockerfile.binary b/make/photon/clair/Dockerfile.binary index 640962f47..d57ffc3fc 100644 --- a/make/photon/clair/Dockerfile.binary +++ b/make/photon/clair/Dockerfile.binary @@ -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/ diff --git a/make/photon/clair/builder b/make/photon/clair/builder index 818f3c0e6..e98fda245 100755 --- a/make/photon/clair/builder +++ b/make/photon/clair/builder @@ -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 diff --git a/make/photon/notary/binary.Dockerfile b/make/photon/notary/binary.Dockerfile index 7a4188fc1..f79f1ccb8 100644 --- a/make/photon/notary/binary.Dockerfile +++ b/make/photon/notary/binary.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.12.12 +FROM golang:1.13.4 ARG NOTARY_VERSION ARG MIGRATE_VERSION diff --git a/make/photon/registry/Dockerfile.binary b/make/photon/registry/Dockerfile.binary index 5f6311cd8..87ef5d085 100644 --- a/make/photon/registry/Dockerfile.binary +++ b/make/photon/registry/Dockerfile.binary @@ -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 diff --git a/src/core/api/project.go b/src/core/api/project.go index f345cc258..791d0834d 100644 --- a/src/core/api/project.go +++ b/src/core/api/project.go @@ -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 diff --git a/src/core/api/project_test.go b/src/core/api/project_test.go index 86ec25922..f673466c0 100644 --- a/src/core/api/project_test.go +++ b/src/core/api/project_test.go @@ -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) diff --git a/src/core/auth/authproxy/auth.go b/src/core/auth/authproxy/auth.go index e9a1414b9..b6be87304 100644 --- a/src/core/auth/authproxy/auth.go +++ b/src/core/auth/authproxy/auth.go @@ -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. diff --git a/src/core/filter/security.go b/src/core/filter/security.go index f6d6b887a..86c1a568f 100644 --- a/src/core/filter/security.go +++ b/src/core/filter/security.go @@ -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) diff --git a/src/pkg/authproxy/http.go b/src/pkg/authproxy/http.go index baeed17cf..61356fd3f 100644 --- a/src/pkg/authproxy/http.go +++ b/src/pkg/authproxy/http.go @@ -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 } diff --git a/src/pkg/authproxy/http_test.go b/src/pkg/authproxy/http_test.go new file mode 100644 index 000000000..24374043e --- /dev/null +++ b/src/pkg/authproxy/http_test.go @@ -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) + } + } + } + } +} diff --git a/src/portal/src/app/app.module.ts b/src/portal/src/app/app.module.ts index 11c4faea5..59c82b82e 100644 --- a/src/portal/src/app/app.module.ts +++ b/src/portal/src/app/app.module.ts @@ -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 diff --git a/src/portal/src/app/intercept-http.service.spec.ts b/src/portal/src/app/intercept-http.service.spec.ts new file mode 100644 index 000000000..4fe3c7b0f --- /dev/null +++ b/src/portal/src/app/intercept-http.service.spec.ts @@ -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); + } + }); + })); +}); diff --git a/src/portal/src/app/intercept-http.service.ts b/src/portal/src/app/intercept-http.service.ts new file mode 100644 index 000000000..ae08e35df --- /dev/null +++ b/src/portal/src/app/intercept-http.service.ts @@ -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, next: HttpHandler): Observable { + + 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); + })); + } +} + diff --git a/src/portal/src/app/project/create-project/create-project.component.html b/src/portal/src/app/project/create-project/create-project.component.html index b82fcb4a6..614206cc1 100644 --- a/src/portal/src/app/project/create-project/create-project.component.html +++ b/src/portal/src/app/project/create-project/create-project.component.html @@ -8,7 +8,7 @@
diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 58b36a9ba..184a879ab 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -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.", diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index 6318e2e5f..7cbdc0012 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -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.", diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index 19c9f8ef9..d9d0690a1 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -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.", diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index 589931139..2c935fae7 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -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.", diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json index 29118890b..d9c0fcf75 100644 --- a/src/portal/src/i18n/lang/tr-tr-lang.json +++ b/src/portal/src/i18n/lang/tr-tr-lang.json @@ -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.", diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index 13b094050..a28117e40 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -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": "创建项目时发生未知错误。", diff --git a/tests/integration.sh b/tests/integration.sh index 6abf1d2df..4b63128e5 100755 --- a/tests/integration.sh +++ b/tests/integration.sh @@ -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" diff --git a/tests/resources/Harbor-Pages/Project-Members.robot b/tests/resources/Harbor-Pages/Project-Members.robot index 821cf0a96..7d8ef63cb 100644 --- a/tests/resources/Harbor-Pages/Project-Members.robot +++ b/tests/resources/Harbor-Pages/Project-Members.robot @@ -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 diff --git a/tests/resources/Harbor-Util.robot b/tests/resources/Harbor-Util.robot index f3d74c8bf..fbb9f32df 100644 --- a/tests/resources/Harbor-Util.robot +++ b/tests/resources/Harbor-Util.robot @@ -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} diff --git a/tests/resources/Util.robot b/tests/resources/Util.robot index 67169c4d9..a373197ca 100644 --- a/tests/resources/Util.robot +++ b/tests/resources/Util.robot @@ -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} diff --git a/tests/robot-cases/Group1-Nightly/Common.robot b/tests/robot-cases/Group1-Nightly/Common.robot index e4de40ac8..372098c78 100644 --- a/tests/robot-cases/Group1-Nightly/Common.robot +++ b/tests/robot-cases/Group1-Nightly/Common.robot @@ -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} diff --git a/tests/travis/api_common_install.sh b/tests/travis/api_common_install.sh index ada0a5540..f0ac8a515 100644 --- a/tests/travis/api_common_install.sh +++ b/tests/travis/api_common_install.sh @@ -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 diff --git a/tests/travis/distro_installer.sh b/tests/travis/distro_installer.sh index c6baaf778..f3917ac10 100755 --- a/tests/travis/distro_installer.sh +++ b/tests/travis/distro_installer.sh @@ -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=