diff --git a/.travis.yml b/.travis.yml index 6db7ed3f7..8357bfd80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,7 +86,7 @@ script: - docker-compose -f make/docker-compose.test.yml down - - docker-compose -f make/docker-compose.yml up -d + - docker-compose -f make/dev/docker-compose.yml up -d - docker ps - go run tests/startuptest.go http://localhost/ diff --git a/Makefile b/Makefile index 6e99596d6..b410a2b47 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ # # package_online: # prepare online install package -# for example: make package_online -e \ +# for example: make package_online -e DEVFLAG=false\ # REGISTRYSERVER=reg-bj.eng.vmware.com \ # REGISTRYPROJECTNAME=harborrelease # @@ -33,13 +33,13 @@ # prepare offline install package # # pushimage: push Harbor images to specific registry server -# for example: make pushimage -e REGISTRYUSER=admin \ +# for example: make pushimage -e DEVFLAG=false REGISTRYUSER=admin \ # REGISTRYPASSWORD=***** \ # REGISTRYSERVER=reg-bj.eng.vmware.com/ \ # REGISTRYPROJECTNAME=harborrelease # note**: need add "/" on end of REGISTRYSERVER. If not setting \ # this value will push images directly to dockerhub. -# make pushimage -e REGISTRYUSER=vmware \ +# make pushimage -e DEVFLAG=false REGISTRYUSER=vmware \ # REGISTRYPASSWORD=***** \ # REGISTRYPROJECTNAME=vmware # @@ -63,7 +63,7 @@ # files with specific TAG. # By default DEVFLAG=true, if you want to release new version of Harbor, \ # should setting the flag to false. -# make XXXX -e DEVFLAG=flase +# make XXXX -e DEVFLAG=false SHELL := /bin/bash BUILDPATH=$(CURDIR) @@ -99,11 +99,13 @@ GODEP=$(GOTEST) -i GOFMT=gofmt -w GOBUILDIMAGE=reg.mydomain.com/library/harborgo[:tag] GOBUILDPATH=$(GOBASEPATH)/harbor -GOBUILDPATH_UI=$(GOBUILDPATH)/ui -GOBUILDPATH_JOBSERVICE=$(GOBUILDPATH)/jobservice +GOIMAGEBUILDCMD=/usr/local/go/bin/go +GOIMAGEBUILD=$(GOIMAGEBUILDCMD) build +GOBUILDPATH_UI=$(GOBUILDPATH)/src/ui +GOBUILDPATH_JOBSERVICE=$(GOBUILDPATH)/src/jobservice GOBUILDMAKEPATH=$(GOBUILDPATH)/make -GOBUILDMAKEPATH_UI=$(GOBUILDMAKEPATH)/ui -GOBUILDMAKEPATH_JOBSERVICE=$(GOBUILDMAKEPATH)/jobservice +GOBUILDMAKEPATH_UI=$(GOBUILDMAKEPATH)/dev/ui +GOBUILDMAKEPATH_JOBSERVICE=$(GOBUILDMAKEPATH)/dev/jobservice # binary UISOURCECODE=$(SRCPATH)/ui @@ -139,6 +141,7 @@ DOCKERIMAGENAME_DB=vmware/harbor-db # docker-compose files DOCKERCOMPOSEFILEPATH=$(MAKEPATH) +DOCKERCOMPOSETPLFILENAME=docker-compose.tpl DOCKERCOMPOSEFILENAME=docker-compose.yml # version prepare @@ -166,10 +169,10 @@ PUSHSCRIPTNAME=pushimage.sh REGISTRYUSER=user REGISTRYPASSWORD=default - - version: - @$(SEDCMD) -i 's/version=\"{{.Version}}\"/version=\"$(VERSIONTAG)\"/' -i $(VERSIONFILEPATH)/$(VERSIONFILENAME) + if [ "$(DEVFLAG)" = "false" ] ; then \ + $(SEDCMD) -i 's/version=\"{{.Version}}\"/version=\"$(VERSIONTAG)\"/' -i $(VERSIONFILEPATH)/$(VERSIONFILENAME) ; \ + fi check_environment: @$(MAKEPATH)/$(CHECKENVCMD) @@ -194,11 +197,11 @@ compile_golangimage: @echo "compiling binary for ui (golang image)..." @echo $(GOBASEPATH) @echo $(GOBUILDPATH) - $(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_UI) $(GOBUILDIMAGE) $(GOBUILD) -v -o $(GOBUILDMAKEPATH_UI)/$(UIBINARYNAME) + $(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_UI) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_UI)/$(UIBINARYNAME) @echo "Done." @echo "compiling binary for jobservice (golang image)..." - $(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_JOBSERVICE) $(GOBUILDIMAGE) $(GOBUILD) -v -o $(GOBUILDMAKEPATH_JOBSERVICE)/$(JOBSERVICEBINARYNAME) + $(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_JOBSERVICE) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_JOBSERVICE)/$(JOBSERVICEBINARYNAME) @echo "Done." compile:check_environment $(COMPILETAG) @@ -207,15 +210,11 @@ prepare: @echo "preparing..." $(MAKEPATH)/$(PREPARECMD) -conf $(CONFIGPATH)/$(CONFIGFILE) -build_common: prepare version +build_common: version @echo "buildging db container for photon..." cd $(DOCKERFILEPATH_DB) && $(DOCKERBUILD) -f $(DOCKERFILENAME_DB) -t $(DOCKERIMAGENAME_DB):$(VERSIONTAG) . @echo "Done." - - @echo "pulling nginx and registry..." - $(DOCKERPULL) registry:2.5.0 - $(DOCKERPULL) nginx:1.9 - + build_photon: build_common make -f $(MAKEFILEPATH_PHOTON)/Makefile build -e DEVFLAG=$(DEVFLAG) @@ -223,16 +222,15 @@ build_ubuntu: build_common make -f $(MAKEFILEPATH_UBUNTU)/Makefile build -e DEVFLAG=$(DEVFLAG) build: build_$(BASEIMAGE) - modify_composefile: @echo "preparing tag:$(VERSIONTAG) docker-compose file..." - @cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) $(DOCKERCOMPOSEFILEPATH)/docker-compose.$(VERSIONTAG).yml - @$(SEDCMD) -i 's/image\: vmware.*/&:$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/docker-compose.$(VERSIONTAG).yml + @cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) + @$(SEDCMD) -i 's/image\: vmware.*/&:$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -install: compile build modify_composefile +install: compile build prepare modify_composefile @echo "loading harbor images..." - $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/docker-compose.$(VERSIONTAG).yml up -d + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) up -d @echo "Install complete. You can visit harbor now." package_online: modify_composefile @@ -247,7 +245,7 @@ package_online: modify_composefile @$(TARCMD) -zcvf harbor-online-installer-$(VERSIONTAG).tgz \ --exclude=$(HARBORPKG)/common/db --exclude=$(HARBORPKG)/ubuntu \ --exclude=$(HARBORPKG)/photon --exclude=$(HARBORPKG)/kubernetes \ - --exclude=$(HARBORPKG)/dev --exclude=docker-compose.yml \ + --exclude=$(HARBORPKG)/dev --exclude=$(DOCKERCOMPOSETPLFILENAME) \ --exclude=$(HARBORPKG)/checkenv.sh \ --exclude=$(HARBORPKG)/jsminify.sh \ --exclude=$(HARBORPKG)/pushimage.sh \ @@ -262,18 +260,23 @@ package_offline: compile build modify_composefile @cp LICENSE $(HARBORPKG)/LICENSE @cp NOTICE $(HARBORPKG)/NOTICE + + @echo "pulling nginx and registry..." + $(DOCKERPULL) registry:2.5.0 + $(DOCKERPULL) nginx:1.9 + @echo "saving harbor docker image" $(DOCKERSAVE) -o $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ - nginx:1.9.0 registry:2.5.0 + nginx:1.9 registry:2.5.0 @$(TARCMD) -zcvf harbor-offline-installer-$(VERSIONTAG).tgz \ --exclude=$(HARBORPKG)/common/db --exclude=$(HARBORPKG)/ubuntu \ --exclude=$(HARBORPKG)/photon --exclude=$(HARBORPKG)/kubernetes \ - --exclude=$(HARBORPKG)/dev --exclude=docker-compose.yml \ + --exclude=$(HARBORPKG)/dev --exclude=$(DOCKERCOMPOSETPLFILENAME) \ --exclude=$(HARBORPKG)/checkenv.sh \ --exclude=$(HARBORPKG)/jsminify.sh \ --exclude=$(HARBORPKG)/pushimage.sh \ @@ -343,9 +346,17 @@ cleanpackage: then rm $(BUILDPATH)/harbor-online-installer-$(VERSIONTAG).tgz ; fi @if [ -f $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ] ; \ then rm $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ; fi - -.PHONY: clean -clean: cleanbinary cleanimage cleandockercomposefile cleanversiontag cleanpackage + +.PHONY: cleanall +cleanall: cleanbinary cleanimage cleandockercomposefile cleanversiontag cleanpackage + +clean: + @echo " make cleanall: remove binary, Harbor images, specific version docker-compose" + @echo " file, specific version tag, online and offline install package" + @echo " make cleanbinary: remove ui and jobservice binary" + @echo " make cleanimage: remove Harbor images" + @echo " make cleandockercomposefile: remove specific version docker-compose" + @echo " make cleanversiontag: cleanpackageremove specific version tag" + @echo " make cleanpackage: remove online and offline install package" all: install - diff --git a/README.md b/README.md index f9af3073e..65b41c49e 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,14 @@ On an Internet connected host, Harbor can be easily installed via docker-compose 2. Edit the file **make/harbor.cfg**, make necessary configuration changes such as hostname, admin password and mail server. Refer to [Installation and Configuration Guide](docs/installation_guide.md) for more info. -3. Install Harbor with the following commands. Note that the docker-compose process can take a while. +3. Install Harbor with the following methods. Note that the build container images process can take a while. + + I: Automation Install + ```sh + $ make install + ``` + + II: Manual Install ```sh $ cd make @@ -42,6 +49,8 @@ On an Internet connected host, Harbor can be easily installed via docker-compose Generated configuration file: ./config/registry/config.yml Generated configuration file: ./config/db/env + $ cd dev + $ docker-compose up -d ``` diff --git a/docs/image_pulling_chinese_user.md b/docs/image_pulling_chinese_user.md deleted file mode 100644 index 37a78562c..000000000 --- a/docs/image_pulling_chinese_user.md +++ /dev/null @@ -1,11 +0,0 @@ -### A faster way to pull images for Chinese Harbor users -By default, Harbor not only build images according to Dockerfile but also pull images from Docker Hub. For the reason we all know, it is difficult for Chinese Harbor users to pull images from the Docker Hub. We put images on daocloud.io platform, we'll put images on other platforms later. If you have difficulty to pull images from Docker Hub, or you think it wastes too much time to build images. We recommend you to use the following way to accelerate the pulling procedure(make sure you're in the harbor diectory): -``` -$ cd contrib -$ cp docker-compose.yml.daocloud ../make -$ cd ../make -$ mv docker-compose.yml docker-compose.yml.bak -$ mv docker-compose.yml.daocloud docker-compose.yml -$ docker-compose up -d -``` -Then you'll see docker pulling imges faster than before. diff --git a/docs/img/ova/edit_settings.png b/docs/img/ova/edit_settings.png new file mode 100644 index 000000000..7656f212f Binary files /dev/null and b/docs/img/ova/edit_settings.png differ diff --git a/docs/img/ova/ova01.png b/docs/img/ova/ova01.png new file mode 100644 index 000000000..d55464a69 Binary files /dev/null and b/docs/img/ova/ova01.png differ diff --git a/docs/img/ova/ova02.png b/docs/img/ova/ova02.png new file mode 100644 index 000000000..4254af0b6 Binary files /dev/null and b/docs/img/ova/ova02.png differ diff --git a/docs/img/ova/ova03.png b/docs/img/ova/ova03.png new file mode 100644 index 000000000..f729ef74c Binary files /dev/null and b/docs/img/ova/ova03.png differ diff --git a/docs/img/ova/ova04.png b/docs/img/ova/ova04.png new file mode 100644 index 000000000..4e39e6666 Binary files /dev/null and b/docs/img/ova/ova04.png differ diff --git a/docs/img/ova/ova05.png b/docs/img/ova/ova05.png new file mode 100644 index 000000000..1628270b4 Binary files /dev/null and b/docs/img/ova/ova05.png differ diff --git a/docs/img/ova/ova06.png b/docs/img/ova/ova06.png new file mode 100644 index 000000000..ca4435567 Binary files /dev/null and b/docs/img/ova/ova06.png differ diff --git a/docs/img/ova/ova07.png b/docs/img/ova/ova07.png new file mode 100644 index 000000000..d743a7b7a Binary files /dev/null and b/docs/img/ova/ova07.png differ diff --git a/docs/img/ova/ova08.png b/docs/img/ova/ova08.png new file mode 100644 index 000000000..816bdd838 Binary files /dev/null and b/docs/img/ova/ova08.png differ diff --git a/docs/img/ova/vapp_options.png b/docs/img/ova/vapp_options.png new file mode 100644 index 000000000..057b830d1 Binary files /dev/null and b/docs/img/ova/vapp_options.png differ diff --git a/docs/installation_guide_ova.md b/docs/installation_guide_ova.md new file mode 100644 index 000000000..a0f8fddbe --- /dev/null +++ b/docs/installation_guide_ova.md @@ -0,0 +1,86 @@ +# Install and Configure Harbor on vSphere using OVA +This guide takes you through the steps about installing and configuring Harbor on vSphere using OVA. + +## Installation +1.Get URL or download the OVA file to your local disk from [release page](https://github.com/vmware/harbor/releases). + +2.Login vSphere web client. Right click on the datacenter, cluster or host which Harbor will be deployed on. Select "Deploy OVF Template" and open the import wizard. + + + +3.Paste the URL of OVA file or select it from local disk and click "Next". + + + +4.Review the OVF template details and click "Next". + + + +5.Spefify a name and location for the deployed template. + + + +6.Select the storage and virtual disk format, click "Next". + + + +7.Configure the networks the deployed template should use. + + + +8.Customize the properties of Harbor. The properties are described below. Note that at the very least, you just need to set the **Root Password**, **Harbor Admin Password** and **Database Password** properties. + + + +* Application + * **Root Password**: The password of the root user. (8-128 characters) + * **Harbor Admin Password**: The initial password of Harbor admin. It only works for the first time when Harbor starts. It has no effect after the first launch of Harbor. Change the admin password from UI after launching Harbor. (8-20 characters) + * **Database Password**: The password of the root user of MySQL database. (8-128 characters) + * **Authentication Mode**: The default authentication mode is db_auth, i.e. the credentials are stored in a local database. Set it to ldap_auth if you want to verify the user's credential against an LDAP/AD server. + * **LDAP URL**: The URL of an LDAP/AD server. + * **LDAP Search DN**: A user's DN who has the permission to search the LDAP/AD server. If your LDAP/AD server does not support anonymous search, you should configure this DN and LDAP Seach Password. + * **LDAP Search Password**: The password of the user for LDAP search. + * **LDAP Base DN**: The base DN from which to look up a user in LDAP/AD. + * **LDAP UID**: The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD server. + * **Email Server**: The mail server to send out emails to reset password. + * **Email Server Port**: The port of mail server. + * **Email Username**: The user from whom the password reset email is sent. + * **Email Password**: The password of the user from whom the password reset email is sent. + * **Email From**: The name of the email sender. + * **Email SSL**: Whether to enabled secure mail transmission. + * **SSL Cert**: Paste in the content of a certificate file. If SSL Cert and SSL Cert Key are both set, HTTPS will be used. + * **SSL Cert Key**: Paste in the content of certificate key file. If SSL Cert and SSL Cert Key are both set, HTTPS will be used. + * **Self Registration**: Determine whether the self-registration is allowed or not when the authentication mode is database. Set this to off to disable a user's self-registration in Harbor. + * **Verify Remote Cert**: Determine whether the image replication should verify the SSL certificate when it connects to a remote registry. Set this flag to off when the remote registry uses a self-signed or untrusted certificate. + * **Garbage Collection**: When setting this to true, Harbor performs garbage collection everytime it boots up. + +* Networking properties + * **Default Gateway**: The default gateway address for this VM. Leave blank if DHCP is desired. + * **Domain Name**: The domain name of this VM. Leave blank if DHCP is desired. + * **Domain Search Path**: The domain search path(comma or space separated domain names) for this VM. Leave blank if DHCP is desired. + * **Domain Name Servers**: The domain name server IP Address for this VM(comma separated). Leave blank if DHCP is desired. + * **Network 1 IP Adress**: The IP address of this interface. Leave blank if DHCP is desired. + * **Network 1 Netmask**: The netmask or prefix for this interface. Leave blank if DHCP is desired. + +**Notes:** If you want to enable HTTPS with a self-signed certificate and have no idea how to generate it, refer to the "Getting a certificate" part of this [guide](https://github.com/vmware/harbor/blob/master/docs/configure_https.md#getting-a-certificate). + +After you complete the properties, click "Next". + +9.Review your settings and click "Finish" to complete the installation. + + + +## Reconfiguration +If you want to reconfigure the properties of Harbor, follow the steps: +1.Power off the VM which Harbor is deployed on. +2.Right click on the VM and select "Edit Settings". + + + +3.Click the "vApp Options" tab, reconfigure the properties and click "OK". + + + +4.Power on the VM. + +**Notes:** "Harbor Admin Password" and all networking properties can not be modified using this method after Harbor launched. Change the admin password from UI and change the networking properties in the OS level manually. \ No newline at end of file diff --git a/docs/migration_guide.md b/docs/migration_guide.md index 45871f68d..2015e5335 100644 --- a/docs/migration_guide.md +++ b/docs/migration_guide.md @@ -28,11 +28,11 @@ When upgrading your existing Habor instance to a newer version, you may need to ``` 4. Before upgrading Harbor, perform database migration first. -The directory **migration/** contains the tool for migration. The first step is to update values of `db_username`, `db_password`, `db_port`, `db_name` in **migration.cfg** so that they match your system's configuration. +The directory **tools/migration/** contains the tool for migration. The first step is to update values of `db_username`, `db_password`, `db_port`, `db_name` in **migration.cfg** so that they match your system's configuration. 5. The migration tool is delivered as a container, so you should build the image from its Dockerfile: ``` - cd migration/ + cd tools/migration/ docker build -t migrate-tool . ``` diff --git a/docs/user_guide.md b/docs/user_guide.md index 084da17c3..b304399c7 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -67,6 +67,9 @@ You can update or remove a member by clicking the icon on the right. ##Replicating images If you are a system administrator, you can replicate images to a remote registry, which is called destination in Harbor. Only Harbor instance is supported as a destination for now. + +**Note:** The replication feature is incompatible between Harbor instance before version 0.3.5(included) and after version 0.3.5. + Click "Add New Policy" on the "Replication" tab, fill the necessary fields and click "OK", a policy for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately, and when a new repository is pushed to this project or an existing repository is deleted from this project, the same operation will also be replicated to the destination.  @@ -169,4 +172,4 @@ $ docker-compose start Option "--dry-run" will print the progress without removing any data. -About the details of GC, please see [GC](https://github.com/docker/distribution/blob/master/docs/garbage-collection.md). \ No newline at end of file +About the details of GC, please see [GC](https://github.com/docker/docker.github.io/blob/master/registry/garbage-collection.md). \ No newline at end of file diff --git a/make/common/log/logrotate_docker.conf b/make/common/log/logrotate_docker.conf deleted file mode 100644 index 6a953d4c3..000000000 --- a/make/common/log/logrotate_docker.conf +++ /dev/null @@ -1,7 +0,0 @@ -# Logrotate configuartion file for docker. - -/var/log/docker/*/*.log { - rotate 100 - size 10M - copytruncate -} diff --git a/make/common/log/rotate.sh b/make/common/log/rotate.sh new file mode 100755 index 000000000..3f96df9bc --- /dev/null +++ b/make/common/log/rotate.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e +echo "Log rotate starting..." + +#The logs n days before will be compressed. +n=14 +path=/var/log/docker + +list="" +n_days_before=$(($(date +%s) - 3600*24*$n)) +for dir in $(ls $path | grep -v "tar.gz"); +do + if [ $(date --date=$dir +%s) -lt $n_days_before ] + then + echo "$dir will be compressed" + list="$list $dir" + fi +done + +if [ -n "$list" ] +then + cd $path + tar --remove-files -zcvf $(date -d @$n_days_before +%F)-.tar.gz $list +fi + +echo "Log rotate finished." diff --git a/make/common/templates/nginx/nginx.https.conf b/make/common/templates/nginx/nginx.https.conf index 4f527ec87..f862fcc20 100644 --- a/make/common/templates/nginx/nginx.https.conf +++ b/make/common/templates/nginx/nginx.https.conf @@ -32,7 +32,7 @@ http { # Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html ssl_protocols TLSv1.1 TLSv1.2; - ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; + ssl_ciphers '!aNULL:kECDH+AESGCM:ECDH+AESGCM:RSA+AESGCM:kECDH+AES:ECDH+AES:RSA+AES:'; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; diff --git a/make/docker-compose.yml b/make/docker-compose.tpl similarity index 98% rename from make/docker-compose.yml rename to make/docker-compose.tpl index fd7945edb..178c175fe 100644 --- a/make/docker-compose.yml +++ b/make/docker-compose.tpl @@ -75,7 +75,7 @@ services: syslog-address: "tcp://127.0.0.1:1514" tag: "jobservice" proxy: - image: nginx:1.9.0 + image: nginx:1.9 container_name: nginx restart: always volumes: diff --git a/make/photon/Makefile b/make/photon/Makefile index 81fac2acd..0a1aeaaad 100644 --- a/make/photon/Makefile +++ b/make/photon/Makefile @@ -2,7 +2,7 @@ # # Targets: # -# build: build harbor ubuntu images +# build: build harbor photon images # clean: clean ui and jobservice harbor images # common @@ -29,8 +29,8 @@ JOBSERVICESOURCECODE=$(SRCPATH)/jobservice JOBSERVICEBINARYPATH=$(MAKEDEVPATH)/jobservice JOBSERVICEBINARYNAME=harbor_jobservice -# ubuntu dockerfile -DOCKERFILEPATH=$(MAKEPATH)/ubuntu +# photon dockerfile +DOCKERFILEPATH=$(MAKEPATH)/photon DOCKERFILEPATH_UI=$(DOCKERFILEPATH)/ui DOCKERFILENAME_UI=Dockerfile DOCKERIMAGENAME_UI=vmware/harbor-ui @@ -55,21 +55,21 @@ endif check_environment: @$(MAKEPATH)/$(CHECKENVCMD) -build: - @echo "building ui container for ubuntu..." +build: + @echo "building ui container for photon..." $(DOCKERBUILD) -f $(DOCKERFILEPATH_UI)/$(DOCKERFILENAME_UI) -t $(DOCKERIMAGENAME_UI):$(VERSIONTAG) . @echo "Done." - @echo "building jobservice container for ubuntu..." + @echo "building jobservice container for photon..." $(DOCKERBUILD) -f $(DOCKERFILEPATH_JOBSERVICE)/$(DOCKERFILENAME_JOBSERVICE) -t $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) . @echo "Done." - @echo "building log container for ubuntu..." + @echo "building log container for photon..." $(DOCKERBUILD) -f $(DOCKERFILEPATH_LOG)/$(DOCKERFILENAME_LOG) -t $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) . @echo "Done." cleanimage: - @echo "cleaning image for ubuntu..." + @echo "cleaning image for photon..." - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_UI):$(VERSIONTAG) - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) diff --git a/make/photon/log/Dockerfile b/make/photon/log/Dockerfile index 8ae03e95c..ea0759427 100644 --- a/make/photon/log/Dockerfile +++ b/make/photon/log/Dockerfile @@ -1,8 +1,7 @@ FROM library/photon:latest # run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception -RUN tdnf install -y cronie rsyslog logrotate shadow\ - && mv /etc/cron.daily/logrotate /etc/cron.hourly/ \ +RUN tdnf install -y cronie rsyslog shadow tar gzip \ && mkdir /etc/rsyslog.d/ \ && mkdir /var/spool/rsyslog \ && groupadd syslog \ @@ -10,15 +9,13 @@ RUN tdnf install -y cronie rsyslog logrotate shadow\ ADD make/common/log/rsyslog.conf /etc/rsyslog.conf -COPY make/photon/log/logrotate.conf.photon /etc/logrotate.conf - -# logrotate configuration file for docker -ADD make/common/log/logrotate_docker.conf /etc/logrotate.d/ +# rotate logs weekly +# notes: file name cannot contain dot, or the script will not run +ADD make/common/log/rotate.sh /etc/cron.weekly/rotate # rsyslog configuration file for docker ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/ - VOLUME /var/log/docker/ EXPOSE 514 diff --git a/make/photon/log/logrotate.conf.photon b/make/photon/log/logrotate.conf.photon deleted file mode 100644 index c28d55b62..000000000 --- a/make/photon/log/logrotate.conf.photon +++ /dev/null @@ -1,35 +0,0 @@ -# see "man logrotate" for details -# rotate log files weekly -weekly - -# keep 4 weeks worth of backlogs -rotate 4 - -# create new (empty) log files after rotating old ones -create - -# use date as a suffix of the rotated file -dateext - -# uncomment this if you want your log files compressed -#compress - -# RPM packages drop log rotation information into this directory -include /etc/logrotate.d - -# no packages own wtmp and btmp -- we'll rotate them here -#/var/log/wtmp { -# monthly -# create 0664 root utmp -# minsize 1M -# rotate 1 -#} - -/var/log/btmp { - missingok - monthly - create 0600 root utmp - rotate 1 -} - -# system-specific logs may be also be configured here. diff --git a/make/ubuntu/log/Dockerfile b/make/ubuntu/log/Dockerfile index f91da3469..3e1c7520e 100644 --- a/make/ubuntu/log/Dockerfile +++ b/make/ubuntu/log/Dockerfile @@ -1,13 +1,12 @@ FROM library/ubuntu:14.04 -# run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception -RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/ \ - && rm /etc/rsyslog.d/* \ - && rm /etc/rsyslog.conf +RUN rm /etc/rsyslog.d/* && rm /etc/rsyslog.conf + ADD make/common/log/rsyslog.conf /etc/rsyslog.conf -# logrotate configuration file for docker -ADD make/common/log/logrotate_docker.conf /etc/logrotate.d/ +# rotate logs weekly +# notes: file name cannot contain dot, or the script will not run +ADD make/common/log/rotate.sh /etc/cron.weekly/rotate # rsyslog configuration file for docker ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/ diff --git a/src/common/dao/dao_test.go b/src/common/dao/dao_test.go index ebb03b990..c276f3bf6 100644 --- a/src/common/dao/dao_test.go +++ b/src/common/dao/dao_test.go @@ -428,7 +428,13 @@ func TestResetUserPassword(t *testing.T) { } func TestChangeUserPassword(t *testing.T) { - err := ChangeUserPassword(models.User{UserID: currentUser.UserID, Password: "NewHarborTester12345", Salt: currentUser.Salt}) + user := models.User{UserID: currentUser.UserID} + query, err := GetUser(user) + if err != nil { + t.Errorf("Error occurred when get user salt") + } + currentUser.Salt = query.Salt + err = ChangeUserPassword(models.User{UserID: currentUser.UserID, Password: "NewHarborTester12345", Salt: currentUser.Salt}) if err != nil { t.Errorf("Error occurred in ChangeUserPassword: %v", err) } @@ -444,7 +450,14 @@ func TestChangeUserPassword(t *testing.T) { } func TestChangeUserPasswordWithOldPassword(t *testing.T) { - err := ChangeUserPassword(models.User{UserID: currentUser.UserID, Password: "NewerHarborTester12345", Salt: currentUser.Salt}, "NewHarborTester12345") + user := models.User{UserID: currentUser.UserID} + query, err := GetUser(user) + if err != nil { + t.Errorf("Error occurred when get user salt") + } + currentUser.Salt = query.Salt + + err = ChangeUserPassword(models.User{UserID: currentUser.UserID, Password: "NewerHarborTester12345", Salt: currentUser.Salt}, "NewHarborTester12345") if err != nil { t.Errorf("Error occurred in ChangeUserPassword: %v", err) } diff --git a/src/common/dao/user.go b/src/common/dao/user.go index 49011ada0..765a504db 100644 --- a/src/common/dao/user.go +++ b/src/common/dao/user.go @@ -137,11 +137,12 @@ func ChangeUserPassword(u models.User, oldPassword ...string) (err error) { o := GetOrmer() var r sql.Result + salt := utils.GenerateRandomString() if len(oldPassword) == 0 { //In some cases, it may no need to check old password, just as Linux change password policies. - r, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID).Exec() + r, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, salt), salt, u.UserID).Exec() } else { - r, err = o.Raw(`update user set password=?, salt=? where user_id=? and password = ?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID, utils.Encrypt(oldPassword[0], u.Salt)).Exec() + r, err = o.Raw(`update user set password=?, salt=? where user_id=? and password = ?`, utils.Encrypt(u.Password, salt), salt, u.UserID, utils.Encrypt(oldPassword[0], u.Salt)).Exec() } if err != nil { diff --git a/src/common/models/user.go b/src/common/models/user.go index ad93d0e5e..2b46accaa 100644 --- a/src/common/models/user.go +++ b/src/common/models/user.go @@ -35,7 +35,7 @@ type User struct { // RoleList []Role `json:"role_list"` HasAdminRole int `orm:"column(sysadmin_flag)" json:"has_admin_role"` ResetUUID string `orm:"column(reset_uuid)" json:"reset_uuid"` - Salt string `orm:"column(salt)"` + Salt string `orm:"column(salt)" json:"-"` CreationTime time.Time `orm:"creation_time" json:"creation_time"` UpdateTime time.Time `orm:"update_time" json:"update_time"` } diff --git a/src/ui/api/dataprepare_test.go b/src/ui/api/dataprepare_test.go index feb4bbc1f..928c517a1 100644 --- a/src/ui/api/dataprepare_test.go +++ b/src/ui/api/dataprepare_test.go @@ -34,8 +34,8 @@ func CommonAddUser() { commonUser := models.User{ Username: TestUserName, - Email: TestUserPwd, - Password: TestUserEmail, + Password: TestUserPwd, + Email: TestUserEmail, } _, _ = dao.Register(commonUser) diff --git a/src/ui/api/harborapi_test.go b/src/ui/api/harborapi_test.go index a2acecda4..3169a6ed9 100644 --- a/src/ui/api/harborapi_test.go +++ b/src/ui/api/harborapi_test.go @@ -6,14 +6,15 @@ import ( "encoding/json" "fmt" "io/ioutil" + "log" "net/http/httptest" "path/filepath" "runtime" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" - "github.com/vmware/harbor/tests/apitests/apilib" "github.com/vmware/harbor/src/common/utils" + "github.com/vmware/harbor/tests/apitests/apilib" // "strconv" // "strings" @@ -90,6 +91,11 @@ func init() { _ = updateInitPassword(1, "Harbor12345") + //syncRegistry + if err := SyncRegistry(); err != nil { + log.Fatalf("failed to sync repositories from registry: %v", err) + } + //Init user Info admin = &usrInfo{adminName, adminPwd} unknownUsr = &usrInfo{"unknown", "unknown"} @@ -119,8 +125,10 @@ func request(_sling *sling.Sling, acceptHeader string, authInfo ...usrInfo) (int //The response includes the project and repository list in a proper display order. //@param q Search parameter for project and repository name. //@return []Search -//func (a testapi) SearchGet (q string) (apilib.Search, error) { -func (a testapi) SearchGet(q string) (apilib.Search, error) { +func (a testapi) SearchGet(q string, authInfo ...usrInfo) (int, apilib.Search, error) { + var httpCode int + var body []byte + var err error _sling := sling.New().Get(a.basePath) @@ -134,10 +142,15 @@ func (a testapi) SearchGet(q string) (apilib.Search, error) { _sling = _sling.QueryStruct(&QueryParams{Query: q}) - _, body, err := request(_sling, jsonAcceptHeader) + if len(authInfo) > 0 { + httpCode, body, err = request(_sling, jsonAcceptHeader, authInfo[0]) + } else { + httpCode, body, err = request(_sling, jsonAcceptHeader) + } + var successPayload = new(apilib.Search) err = json.Unmarshal(body, &successPayload) - return *successPayload, err + return httpCode, *successPayload, err } //Create a new project. @@ -285,7 +298,7 @@ func (a testapi) ProjectsGetByPID(projectID string) (int, apilib.Project, error) } //Search projects by projectName and isPublic -func (a testapi) ProjectsGet(projectName string, isPublic int32) (int, []apilib.Project, error) { +func (a testapi) ProjectsGet(projectName string, isPublic int32, authInfo ...usrInfo) (int, []apilib.Project, error) { _sling := sling.New().Get(a.basePath) //create api path @@ -299,7 +312,15 @@ func (a testapi) ProjectsGet(projectName string, isPublic int32) (int, []apilib. var successPayload []apilib.Project - httpStatusCode, body, err := request(_sling, jsonAcceptHeader) + var httpStatusCode int + var err error + var body []byte + if len(authInfo) > 0 { + httpStatusCode, body, err = request(_sling, jsonAcceptHeader, authInfo[0]) + } else { + httpStatusCode, body, err = request(_sling, jsonAcceptHeader) + } + if err == nil && httpStatusCode == 200 { err = json.Unmarshal(body, &successPayload) } diff --git a/src/ui/api/log_test.go b/src/ui/api/log_test.go index 593491be0..bda4d9279 100644 --- a/src/ui/api/log_test.go +++ b/src/ui/api/log_test.go @@ -21,7 +21,7 @@ func TestLogGet(t *testing.T) { project.ProjectName = "my_project" project.Public = 1 now := fmt.Sprintf("%v", time.Now().Unix()) - statusCode, result, err := apiTest.LogGet(*admin, "0", now, "") + statusCode, result, err := apiTest.LogGet(*admin, "0", now, "1000") if err != nil { t.Error("Error while get log information", err.Error()) t.Log(err) @@ -30,8 +30,7 @@ func TestLogGet(t *testing.T) { } logNum := len(result) - logID := result[0].LogId - fmt.Println(result) + fmt.Println("result", result) //add the project first. fmt.Println("add the project first.") reply, err := apiTest.ProjectsPost(*admin, project) @@ -42,20 +41,24 @@ func TestLogGet(t *testing.T) { assert.Equal(int(201), reply, "Case 2: Project creation status should be 201") } //case 1: right parameters, expect the right output + now = fmt.Sprintf("%v", time.Now().Unix()) statusCode, result, err = apiTest.LogGet(*admin, "0", now, "1000") if err != nil { t.Error("Error while get log information", err.Error()) t.Log(err) } else { assert.Equal(logNum+1, len(result), "lines of logs should be equal") - assert.Equal(int32(logID+1), result[0].LogId, "LogId should be equal") - assert.Equal("my_project/", result[0].RepoName, "RepoName should be equal") - assert.Equal("N/A", result[0].RepoTag, "RepoTag should be equal") - assert.Equal("create", result[0].Operation, "Operation should be equal") + num, index := getLog(result) + if num != 1 { + assert.Equal(1, num, "add my_project log number should be 1") + } else { + assert.Equal("my_project/", result[index].RepoName, "RepoName should be equal") + assert.Equal("N/A", result[index].RepoTag, "RepoTag should be equal") + assert.Equal("create", result[index].Operation, "Operation should be equal") + } } - + fmt.Println("log ", result) //case 2: wrong format of start_time parameter, expect the wrong output - now = fmt.Sprintf("%v", time.Now().Unix()) statusCode, result, err = apiTest.LogGet(*admin, "ss", now, "3") if err != nil { t.Error("Error occured while get log information since the format of start_time parameter is not right.", err.Error()) @@ -74,7 +77,6 @@ func TestLogGet(t *testing.T) { } //case 4: wrong format of lines parameter, expect the wrong output - now = fmt.Sprintf("%v", time.Now().Unix()) statusCode, result, err = apiTest.LogGet(*admin, "0", now, "s") if err != nil { t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error()) @@ -84,7 +86,6 @@ func TestLogGet(t *testing.T) { } //case 5: wrong format of lines parameter, expect the wrong output - now = fmt.Sprintf("%v", time.Now().Unix()) statusCode, result, err = apiTest.LogGet(*admin, "0", now, "-5") if err != nil { t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error()) @@ -103,13 +104,17 @@ func TestLogGet(t *testing.T) { if logNum+1 >= 10 { logNum = 10 } else { - logNum += 1 + logNum++ } assert.Equal(logNum, len(result), "lines of logs should be equal") - assert.Equal(int32(logID+1), result[0].LogId, "LogId should be equal") - assert.Equal("my_project/", result[0].RepoName, "RepoName should be equal") - assert.Equal("N/A", result[0].RepoTag, "RepoTag should be equal") - assert.Equal("create", result[0].Operation, "Operation should be equal") + num, index := getLog(result) + if num != 1 { + assert.Equal(1, num, "add my_project log number should be 1") + } else { + assert.Equal("my_project/", result[index].RepoName, "RepoName should be equal") + assert.Equal("N/A", result[index].RepoTag, "RepoTag should be equal") + assert.Equal("create", result[index].Operation, "Operation should be equal") + } } //get the project @@ -136,5 +141,15 @@ func TestLogGet(t *testing.T) { } fmt.Printf("\n") - +} + +func getLog(result []apilib.AccessLog) (int, int) { + var num, index int + for i := 0; i < len(result); i++ { + if result[i].RepoName == "my_project/" { + num++ + index = i + } + } + return num, index } diff --git a/src/ui/api/member.go b/src/ui/api/member.go index 63cdc7cde..b528872c5 100644 --- a/src/ui/api/member.go +++ b/src/ui/api/member.go @@ -16,13 +16,14 @@ package api import ( + "fmt" "net/http" "strconv" + "github.com/vmware/harbor/src/common/api" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" - "github.com/vmware/harbor/src/common/api" ) // ProjectMemberAPI handles request to /api/projects/{}/members/{} @@ -98,6 +99,11 @@ func (pma *ProjectMemberAPI) Get() { log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } + + if len(roleList) == 0 { + pma.CustomAbort(http.StatusNotFound, fmt.Sprintf("user %d is not a member of the project", pma.memberID)) + } + //return empty role list to indicate if a user is not a member result := make(map[string]interface{}) user, err := dao.GetUser(models.User{UserID: pma.memberID}) diff --git a/src/ui/api/member_test.go b/src/ui/api/member_test.go index 4b6618e9b..8dbeae8fe 100644 --- a/src/ui/api/member_test.go +++ b/src/ui/api/member_test.go @@ -4,9 +4,10 @@ import ( "fmt" "testing" + "strconv" + "github.com/stretchr/testify/assert" "github.com/vmware/harbor/tests/apitests/apilib" - "strconv" ) func TestMemGet(t *testing.T) { @@ -51,8 +52,19 @@ func TestMemGet(t *testing.T) { assert.Equal(int(404), httpStatusCode, "Case 3: Project creation status should be 404") } - fmt.Printf("\n") + //------------case 4: Response Code=404, member does not exist-----------// + fmt.Println("case 4: Response Code=404, member does not exist") + projectID = "1" + memberID := "10000" + httpStatusCode, err = apiTest.GetMemByPIDUID(*admin, projectID, memberID) + if err != nil { + t.Fatalf("failed to get member %s of project %s: %v", memberID, projectID, err) + } + assert.Equal(int(404), httpStatusCode, + fmt.Sprintf("response status code should be 404 other than %d", httpStatusCode)) + + fmt.Printf("\n") } /** diff --git a/src/ui/api/project.go b/src/ui/api/project.go index c58941ebc..9905630ec 100644 --- a/src/ui/api/project.go +++ b/src/ui/api/project.go @@ -20,10 +20,10 @@ import ( "net/http" "regexp" + "github.com/vmware/harbor/src/common/api" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" - "github.com/vmware/harbor/src/common/api" "strconv" "time" @@ -192,7 +192,8 @@ func (p *ProjectAPI) Delete() { if err := dao.AddAccessLog(models.AccessLog{ UserID: userID, ProjectID: p.projectID, - RepoName: p.projectName, + RepoName: p.projectName + "/", + RepoTag: "N/A", Operation: "delete", }); err != nil { log.Errorf("failed to add access log: %v", err) @@ -286,6 +287,13 @@ func (p *ProjectAPI) List() { if public != 1 { if isAdmin { projectList[i].Role = models.PROJECTADMIN + } else { + roles, err := dao.GetUserProjectRoles(p.userID, projectList[i].ProjectID) + if err != nil { + log.Errorf("failed to get user's project role: %v", err) + p.CustomAbort(http.StatusInternalServerError, "") + } + projectList[i].Role = roles[0].RoleID } if projectList[i].Role == models.PROJECTADMIN { projectList[i].Togglable = true diff --git a/src/ui/api/project_test.go b/src/ui/api/project_test.go index 2b164da6a..8a43d1d3c 100644 --- a/src/ui/api/project_test.go +++ b/src/ui/api/project_test.go @@ -13,7 +13,7 @@ var addProject *apilib.ProjectReq var addPID int func InitAddPro() { - addProject = &apilib.ProjectReq{"test_project", 1} + addProject = &apilib.ProjectReq{"add_project", 1} } func TestAddProject(t *testing.T) { @@ -106,7 +106,49 @@ func TestProGetByName(t *testing.T) { } else { assert.Equal(int(401), httpStatusCode, "httpStatusCode should be 200") } - fmt.Printf("\n") + + //-------------------case 3 : check admin project role------------------------// + httpStatusCode, result, err = apiTest.ProjectsGet(addProject.ProjectName, 0, *admin) + if err != nil { + t.Error("Error while search project by proName and isPublic", err.Error()) + t.Log(err) + } else { + assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200") + assert.Equal(addProject.ProjectName, result[0].ProjectName, "Project name is wrong") + assert.Equal(int32(1), result[0].Public, "Public is wrong") + assert.Equal(int32(1), result[0].CurrentUserRoleId, "User project role is wrong") + } + + //-------------------case 4 : add project member and check his role ------------------------// + CommonAddUser() + roles := &apilib.RoleParam{[]int32{2}, TestUserName} + projectID := strconv.Itoa(addPID) + httpStatusCode, err = apiTest.AddProjectMember(*admin, projectID, *roles) + if err != nil { + t.Error("Error whihle add project role member", err.Error()) + t.Log(err) + } else { + assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200") + } + httpStatusCode, result, err = apiTest.ProjectsGet(addProject.ProjectName, 0, *testUser) + if err != nil { + t.Error("Error while search project by proName and isPublic", err.Error()) + t.Log(err) + } else { + assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200") + assert.Equal(addProject.ProjectName, result[0].ProjectName, "Project name is wrong") + assert.Equal(int32(1), result[0].Public, "Public is wrong") + assert.Equal(int32(2), result[0].CurrentUserRoleId, "User project role is wrong") + } + id := strconv.Itoa(CommonGetUserID()) + httpStatusCode, err = apiTest.DeleteProjectMember(*admin, projectID, id) + if err != nil { + t.Error("Error whihle add project role member", err.Error()) + t.Log(err) + } else { + assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200") + } + CommonDelUser() } //Get project by proID diff --git a/src/ui/api/search_test.go b/src/ui/api/search_test.go index 1eecd1009..ac05f74f2 100644 --- a/src/ui/api/search_test.go +++ b/src/ui/api/search_test.go @@ -14,19 +14,29 @@ func TestSearch(t *testing.T) { apiTest := newHarborAPI() var result apilib.Search - result, err := apiTest.SearchGet("library") - //fmt.Printf("%+v\n", result) + + //-------------case 1 : Response Code = 200, Not sysAdmin --------------// + httpStatusCode, result, err := apiTest.SearchGet("library") if err != nil { t.Error("Error while search project or repository", err.Error()) t.Log(err) } else { - assert.Equal(result.Projects[0].Id, int64(1), "Project id should be equal") - assert.Equal(result.Projects[0].Name, "library", "Project name should be library") - assert.Equal(result.Projects[0].Public, int32(1), "Project public status should be 1 (true)") - //t.Log(result) + assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200") + assert.Equal(int64(1), result.Projects[0].Id, "Project id should be equal") + assert.Equal("library", result.Projects[0].Name, "Project name should be library") + assert.Equal(int32(1), result.Projects[0].Public, "Project public status should be 1 (true)") + } + + //--------case 2 : Response Code = 200, sysAdmin and search repo--------// + httpStatusCode, result, err = apiTest.SearchGet("docker", *admin) + if err != nil { + t.Error("Error while search project or repository", err.Error()) + t.Log(err) + } else { + assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200") + assert.Equal("library", result.Repositories[0].ProjectName, "Project name should be library") + assert.Equal("library/docker", result.Repositories[0].RepositoryName, "Repository name should be library/docker") + assert.Equal(int32(1), result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)") } - //if result.Response.StatusCode != 200 { - // t.Log(result.Response) - //} } diff --git a/src/ui/api/statistic_test.go b/src/ui/api/statistic_test.go index 17e2b9f5a..37400bea4 100644 --- a/src/ui/api/statistic_test.go +++ b/src/ui/api/statistic_test.go @@ -10,10 +10,6 @@ import ( ) func TestStatisticGet(t *testing.T) { - if err := SyncRegistry(); err != nil { - t.Fatalf("failed to sync repositories from registry: %v", err) - } - fmt.Println("Testing Statistic API") assert := assert.New(t) diff --git a/src/ui/api/user.go b/src/ui/api/user.go index d75ba7d3e..17def3bf1 100644 --- a/src/ui/api/user.go +++ b/src/ui/api/user.go @@ -23,10 +23,10 @@ import ( "strconv" "strings" + "github.com/vmware/harbor/src/common/api" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" - "github.com/vmware/harbor/src/common/api" ) // UserAPI handles request to /api/users/{} @@ -152,7 +152,7 @@ func (ua *UserAPI) Put() { ua.DecodeJSONReq(&user) err := commonValidate(user) if err != nil { - log.Warning("Bad request in change user profile: %v", err) + log.Warningf("Bad request in change user profile: %v", err) ua.RenderError(http.StatusBadRequest, "change user profile error:"+err.Error()) return } @@ -200,7 +200,7 @@ func (ua *UserAPI) Post() { ua.DecodeJSONReq(&user) err := validate(user) if err != nil { - log.Warning("Bad request in Register: %v", err) + log.Warningf("Bad request in Register: %v", err) ua.RenderError(http.StatusBadRequest, "register error:"+err.Error()) return } @@ -241,6 +241,12 @@ func (ua *UserAPI) Delete() { return } + // TODO read from conifg + authMode := os.Getenv("AUTH_MODE") + if authMode == "ldap_auth" { + ua.CustomAbort(http.StatusForbidden, "user can not be deleted in LDAP authentication mode") + } + if ua.currentUserID == ua.userID { ua.CustomAbort(http.StatusForbidden, "can not delete yourself") } diff --git a/src/ui/api/user_test.go b/src/ui/api/user_test.go index 7ea546990..9be52f6ef 100644 --- a/src/ui/api/user_test.go +++ b/src/ui/api/user_test.go @@ -110,7 +110,7 @@ func TestUsersPost(t *testing.T) { //case 9: register a new user with admin auth, but bad user comment, expect 400 testUser0002.Realname = "testUser0002" testUser0002.Comment = "vmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" - fmt.Println("Register user with admin auth, but bad user comment format") + fmt.Println("Register user with admin auth, but user comment length is illegal") code, err = apiTest.UsersPost(testUser0002, *admin) if err != nil { t.Error("Error occured while add a user", err.Error()) @@ -222,7 +222,7 @@ func TestUsersGetByID(t *testing.T) { //case 3: Get user that does not exist with user2 auth, expect 404 not found. code, user, err = apiTest.UsersGetByID(testUser0002.Username, *testUser0002Auth, 1000) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while get users", err.Error()) t.Log(err) } else { assert.Equal(404, code, "Get users status should be 404") @@ -230,7 +230,7 @@ func TestUsersGetByID(t *testing.T) { // Get user3ID in order to delete at the last of the test code, users, err := apiTest.UsersGet(testUser0003.Username, *admin) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while get users", err.Error()) t.Log(err) } else { assert.Equal(200, code, "Get users status should be 200") @@ -250,7 +250,7 @@ func TestUsersPut(t *testing.T) { t.Error("Error occured while change user profile", err.Error()) t.Log(err) } else { - assert.Equal(403, code, "Get users status should be 403") + assert.Equal(403, code, "Change user profile status should be 403") } //case 2: change user2 profile with user2 auth, but bad parameters format. code, err = apiTest.UsersPut(testUser0002ID, profile, *testUser0002Auth) @@ -258,7 +258,7 @@ func TestUsersPut(t *testing.T) { t.Error("Error occured while change user profile", err.Error()) t.Log(err) } else { - assert.Equal(400, code, "Get users status should be 400") + assert.Equal(400, code, "Change user profile status should be 400") } //case 3: change user2 profile with user2 auth, but duplicate email. profile.Realname = "test user" @@ -269,7 +269,7 @@ func TestUsersPut(t *testing.T) { t.Error("Error occured while change user profile", err.Error()) t.Log(err) } else { - assert.Equal(409, code, "Get users status should be 409") + assert.Equal(409, code, "Change user profile status should be 409") } //case 4: change user2 profile with user2 auth, right parameters format. profile.Realname = "test user" @@ -280,7 +280,8 @@ func TestUsersPut(t *testing.T) { t.Error("Error occured while change user profile", err.Error()) t.Log(err) } else { - assert.Equal(200, code, "Get users status should be 200") + assert.Equal(200, code, "Change user profile status should be 200") + testUser0002.Email = profile.Email } } @@ -291,18 +292,18 @@ func TestUsersToggleAdminRole(t *testing.T) { //case 1: toggle user2 admin role without admin auth code, err := apiTest.UsersToggleAdminRole(testUser0002ID, *testUser0002Auth, int32(1)) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while toggle user admin role", err.Error()) t.Log(err) } else { - assert.Equal(403, code, "Get users status should be 403") + assert.Equal(403, code, "Toggle user admin role status should be 403") } //case 2: toggle user2 admin role with admin auth code, err = apiTest.UsersToggleAdminRole(testUser0002ID, *admin, int32(1)) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while toggle user admin role", err.Error()) t.Log(err) } else { - assert.Equal(200, code, "Get users status should be 200") + assert.Equal(200, code, "Toggle user admin role status should be 200") } } func TestUsersUpdatePassword(t *testing.T) { @@ -321,39 +322,59 @@ func TestUsersUpdatePassword(t *testing.T) { //case 2: update user2 password with admin auth, but oldpassword is empty code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while update user password", err.Error()) t.Log(err) } else { - assert.Equal(400, code, "Get users status should be 400") + assert.Equal(400, code, "Update user password status should be 400") } //case 3: update user2 password with admin auth, but oldpassword is wrong password.OldPassword = "000" code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while update user password", err.Error()) t.Log(err) } else { - assert.Equal(403, code, "Get users status should be 403") + assert.Equal(403, code, "Update user password status should be 403") } //case 4: update user2 password with admin auth, but newpassword is empty password.OldPassword = "testUser0002" code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while update user password", err.Error()) t.Log(err) } else { - assert.Equal(400, code, "Get users status should be 400") + assert.Equal(400, code, "Update user password status should be 400") } //case 5: update user2 password with admin auth, right parameters password.NewPassword = "TestUser0002" code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) if err != nil { - t.Error("Error occured while change user profile", err.Error()) + t.Error("Error occured while update user password", err.Error()) t.Log(err) } else { - assert.Equal(200, code, "Get users status should be 200") + assert.Equal(200, code, "Update user password status should be 200") testUser0002.Password = password.NewPassword testUser0002Auth.Passwd = password.NewPassword + //verify the new password takes effect + code, user, err := apiTest.UsersGetByID(testUser0002.Username, *testUser0002Auth, testUser0002ID) + if err != nil { + t.Error("Error occured while get users", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "Get users status should be 200") + assert.Equal(testUser0002.Username, user.Username, "Get users username should be equal") + assert.Equal(testUser0002.Email, user.Email, "Get users email should be equal") + } + + } + //case 6: update user2 password setting the new password same as the old + password.OldPassword = password.NewPassword + code, err = apiTest.UsersUpdatePassword(testUser0002ID, password, *admin) + if err != nil { + t.Error("Error occured while update user password", err.Error()) + t.Log(err) + } else { + assert.Equal(200, code, "When new password is same as old, update user password status should be 200") } } @@ -366,33 +387,33 @@ func TestUsersDelete(t *testing.T) { //case 1:delete user without admin auth code, err := apiTest.UsersDelete(testUser0002ID, *testUser0003Auth) if err != nil { - t.Error("Error occured while delete a testUser", err.Error()) + t.Error("Error occured while delete test user", err.Error()) t.Log(err) } else { - assert.Equal(403, code, "Delete testUser status should be 403") + assert.Equal(403, code, "Delete test user status should be 403") } //case 2: delete user with admin auth, user2 has already been toggled to admin, but can not delete himself code, err = apiTest.UsersDelete(testUser0002ID, *testUser0002Auth) if err != nil { - t.Error("Error occured while delete a testUser", err.Error()) + t.Error("Error occured while delete test user", err.Error()) t.Log(err) } else { - assert.Equal(403, code, "Delete testUser status should be 403") + assert.Equal(403, code, "Delete test user status should be 403") } //case 3: delete user with admin auth code, err = apiTest.UsersDelete(testUser0002ID, *admin) if err != nil { - t.Error("Error occured while delete a testUser", err.Error()) + t.Error("Error occured while delete test user", err.Error()) t.Log(err) } else { - assert.Equal(200, code, "Delete testUser status should be 200") + assert.Equal(200, code, "Delete test user status should be 200") } //delete user3 with admin auth code, err = apiTest.UsersDelete(testUser0003ID, *admin) if err != nil { - t.Error("Error occured while delete a testUser", err.Error()) + t.Error("Error occured while delete test user", err.Error()) t.Log(err) } else { - assert.Equal(200, code, "Delete testUser status should be 200") + assert.Equal(200, code, "Delete test user status should be 200") } } diff --git a/src/ui/controllers/accountsetting.go b/src/ui/controllers/accountsetting.go index 5d49e60d7..612015455 100644 --- a/src/ui/controllers/accountsetting.go +++ b/src/ui/controllers/accountsetting.go @@ -7,5 +7,14 @@ type AccountSettingController struct { // Get renders the account settings page func (asc *AccountSettingController) Get() { - asc.Forward("page_title_account_setting", "account-settings.htm") + var isAdminForLdap bool + sessionUserID, ok := asc.GetSession("userId").(int) + if ok && sessionUserID == 1 { + isAdminForLdap = true + } + if asc.AuthMode == "db_auth" || isAdminForLdap { + asc.Forward("page_title_account_setting", "account-settings.htm") + } else { + asc.Redirect("/dashboard", 302) + } } diff --git a/src/ui/controllers/adminoption.go b/src/ui/controllers/adminoption.go index d0a10c1fa..69cc48828 100644 --- a/src/ui/controllers/adminoption.go +++ b/src/ui/controllers/adminoption.go @@ -1,5 +1,10 @@ package controllers +import ( + "github.com/vmware/harbor/src/common/dao" + "github.com/vmware/harbor/src/common/utils/log" +) + // AdminOptionController handles requests to /admin_option type AdminOptionController struct { BaseController @@ -7,5 +12,16 @@ type AdminOptionController struct { // Get renders the admin options page func (aoc *AdminOptionController) Get() { - aoc.Forward("page_title_admin_option", "admin-options.htm") + sessionUserID, ok := aoc.GetSession("userId").(int) + if ok { + isAdmin, err := dao.IsAdminRole(sessionUserID) + if err != nil { + log.Errorf("Error occurred in IsAdminRole: %v", err) + } + if isAdmin { + aoc.Forward("page_title_admin_option", "admin-options.htm") + return + } + } + aoc.Redirect("/dashboard", 302) } diff --git a/src/ui/controllers/base.go b/src/ui/controllers/base.go index 5bbba0557..c8d2c7a39 100644 --- a/src/ui/controllers/base.go +++ b/src/ui/controllers/base.go @@ -8,10 +8,10 @@ import ( "github.com/astaxie/beego" "github.com/beego/i18n" - "github.com/vmware/harbor/src/ui/auth" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" + "github.com/vmware/harbor/src/ui/auth" ) // BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers. diff --git a/src/ui/controllers/changepassword.go b/src/ui/controllers/changepassword.go index 0ecddb29c..0873db73b 100644 --- a/src/ui/controllers/changepassword.go +++ b/src/ui/controllers/changepassword.go @@ -6,6 +6,15 @@ type ChangePasswordController struct { } // Get renders the change password page -func (asc *ChangePasswordController) Get() { - asc.Forward("page_title_change_password", "change-password.htm") +func (cpc *ChangePasswordController) Get() { + var isAdminForLdap bool + sessionUserID, ok := cpc.GetSession("userId").(int) + if ok && sessionUserID == 1 { + isAdminForLdap = true + } + if cpc.AuthMode == "db_auth" || isAdminForLdap { + cpc.Forward("page_title_change_password", "change-password.htm") + } else { + cpc.Redirect("/dashboard", 302) + } } diff --git a/src/ui/controllers/controllers_test.go b/src/ui/controllers/controllers_test.go index b4cb4fe2c..46e239be6 100644 --- a/src/ui/controllers/controllers_test.go +++ b/src/ui/controllers/controllers_test.go @@ -113,19 +113,16 @@ func TestMain(t *testing.T) { w = httptest.NewRecorder() beego.BeeApp.Handlers.ServeHTTP(w, r) assert.Equal(int(200), w.Code, "'/account_setting' httpStatusCode should be 200") - assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_account_setting</title>"), "http respond should have '<title>page_title_account_setting</title>'") r, _ = http.NewRequest("GET", "/change_password", nil) w = httptest.NewRecorder() beego.BeeApp.Handlers.ServeHTTP(w, r) assert.Equal(int(200), w.Code, "'/change_password' httpStatusCode should be 200") - assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_change_password</title>"), "http respond should have '<title>page_title_change_password</title>'") r, _ = http.NewRequest("GET", "/admin_option", nil) w = httptest.NewRecorder() beego.BeeApp.Handlers.ServeHTTP(w, r) - assert.Equal(int(200), w.Code, "'/admin_option' httpStatusCode should be 200") - assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_admin_option</title>"), "http respond should have '<title>page_title_admin_option</title>'") + assert.Equal(int(302), w.Code, "'/admin_option' httpStatusCode should be 302") r, _ = http.NewRequest("GET", "/forgot_password", nil) w = httptest.NewRecorder() diff --git a/src/ui/controllers/optionalmenu.go b/src/ui/controllers/optionalmenu.go index 80524d55f..fe0bb2b78 100644 --- a/src/ui/controllers/optionalmenu.go +++ b/src/ui/controllers/optionalmenu.go @@ -20,6 +20,9 @@ func (omc *OptionalMenuController) Get() { var hasLoggedIn bool var allowAddNew bool + var isAdminForLdap bool + var allowSettingAccount bool + if sessionUserID != nil { hasLoggedIn = true userID := sessionUserID.(int) @@ -34,6 +37,14 @@ func (omc *OptionalMenuController) Get() { } omc.Data["Username"] = u.Username + if userID == 1 { + isAdminForLdap = true + } + + if omc.AuthMode == "db_auth" || isAdminForLdap { + allowSettingAccount = true + } + isAdmin, err := dao.IsAdminRole(sessionUserID.(int)) if err != nil { log.Errorf("Error occurred in IsAdminRole: %v", err) @@ -45,6 +56,7 @@ func (omc *OptionalMenuController) Get() { } } omc.Data["AddNew"] = allowAddNew + omc.Data["SettingAccount"] = allowSettingAccount omc.Data["HasLoggedIn"] = hasLoggedIn omc.TplName = "optional-menu.htm" omc.Render() diff --git a/src/ui/controllers/signup.go b/src/ui/controllers/signup.go index 6ed75b01b..d3b7bdc90 100644 --- a/src/ui/controllers/signup.go +++ b/src/ui/controllers/signup.go @@ -12,7 +12,7 @@ type SignUpController struct { // Get renders sign up page func (suc *SignUpController) Get() { if suc.AuthMode != "db_auth" || !suc.SelfRegistration { - suc.CustomAbort(http.StatusUnauthorized, "Status unauthorized.") + suc.CustomAbort(http.StatusForbidden, "") } suc.Data["AddNew"] = false suc.Forward("page_title_sign_up", "sign-up.htm") diff --git a/src/ui/service/notification.go b/src/ui/service/notification.go index 33ef0ba7a..d378f74b4 100644 --- a/src/ui/service/notification.go +++ b/src/ui/service/notification.go @@ -20,12 +20,12 @@ import ( "regexp" "strings" - "github.com/vmware/harbor/src/ui/api" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" - "github.com/vmware/harbor/src/ui/service/cache" "github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/common/utils/log" + "github.com/vmware/harbor/src/ui/api" + "github.com/vmware/harbor/src/ui/service/cache" "github.com/astaxie/beego" ) @@ -35,7 +35,8 @@ type NotificationHandler struct { beego.Controller } -const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json` +const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+(json|prettyjws)` +const vicPrefix = "vic/" // Post handles POST request, and records audit log or refreshes cache based on event. func (n *NotificationHandler) Post() { @@ -102,8 +103,8 @@ func filterEvents(notification *models.Notification) ([]*models.Event, error) { events := []*models.Event{} for _, event := range notification.Events { - log.Debugf("receive an event: ID-%s, target-%s:%s, digest-%s, action-%s", event.ID, event.Target.Repository, event.Target.Tag, - event.Target.Digest, event.Action) + log.Debugf("receive an event: \n----ID: %s \n----target: %s:%s \n----digest: %s \n----action: %s \n----mediatype: %s \n----user-agent: %s", event.ID, event.Target.Repository, + event.Target.Tag, event.Target.Digest, event.Action, event.Target.MediaType, event.Request.UserAgent) isManifest, err := regexp.MatchString(manifestPattern, event.Target.MediaType) if err != nil { @@ -115,8 +116,9 @@ func filterEvents(notification *models.Notification) ([]*models.Event, error) { continue } - //pull and push manifest by docker-client - if strings.HasPrefix(event.Request.UserAgent, "docker") && (event.Action == "pull" || event.Action == "push") { + //pull and push manifest by docker-client or vic + if (strings.HasPrefix(event.Request.UserAgent, "docker") || strings.HasPrefix(event.Request.UserAgent, vicPrefix)) && + (event.Action == "pull" || event.Action == "push") { events = append(events, &event) log.Debugf("add event to collect: %s", event.ID) continue diff --git a/src/ui/static/resources/js/components/details/retrieve-projects.directive.js b/src/ui/static/resources/js/components/details/retrieve-projects.directive.js index 3d5641b11..4101898a6 100644 --- a/src/ui/static/resources/js/components/details/retrieve-projects.directive.js +++ b/src/ui/static/resources/js/components/details/retrieve-projects.directive.js @@ -29,6 +29,7 @@ vm.isOpen = false; vm.isProjectMember = false; vm.target = $location.path().substr(1) || 'repositories'; + vm.roleId = 0; vm.isPublic = Number(getParameterByName('is_public', $location.absUrl())); @@ -87,7 +88,6 @@ function getProjectSuccess(response) { - var partialProjects = response.data || []; for(var i in partialProjects) { vm.projects.push(partialProjects[i]); @@ -113,11 +113,13 @@ } } - $location.search('project_id', vm.selectedProject.project_id); - vm.checkProjectMember(vm.selectedProject.project_id); - + if(vm.selectedProject) { + $location.search('project_id', vm.selectedProject.project_id); + vm.checkProjectMember(vm.selectedProject.project_id); + } + vm.resultCount = vm.projects.length; - + $scope.$watch('vm.filterInput', function(current, origin) { vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'name').length; }); @@ -152,6 +154,9 @@ function getCurrentProjectMemberSuccess(data, status) { console.log('Successful get current project member:' + status); vm.isProjectMember = true; + if(data && data['roles'] && data['roles'].length > 0) { + vm.roleId = data['roles'][0]['role_id']; + } } function getCurrentProjectMemberFailed(data, status) { @@ -171,7 +176,8 @@ 'isOpen': '=', 'selectedProject': '=', 'isPublic': '=', - 'isProjectMember': '=' + 'isProjectMember': '=', + 'roleId': '=' }, link: link, controller: RetrieveProjectsController, diff --git a/src/ui/static/resources/js/components/project-member/edit-project-member.directive.html b/src/ui/static/resources/js/components/project-member/edit-project-member.directive.html index 4f83d1a13..1d1b48f47 100644 --- a/src/ui/static/resources/js/components/project-member/edit-project-member.directive.html +++ b/src/ui/static/resources/js/components/project-member/edit-project-member.directive.html @@ -14,7 +14,7 @@ --> <td width="30%">//vm.username//</td> <td width="45%"><switch-role roles="vm.roles" edit-mode="vm.editMode" user-id="vm.userId" role-name="vm.roleName"></switch-role></td> -<td width="25%"> +<td width="25%" ng-if="vm.currentRoleId == 1"> <a ng-show="vm.userId != vm.currentUserId" href="javascript:void(0);" ng-click="vm.updateProjectMember({projectId: vm.projectId, userId: vm.userId, roleId: vm.roleId})"> <span ng-if="!vm.editMode" class="glyphicon glyphicon-pencil" title="// 'edit' | tr //"></span><span ng-if="vm.editMode" class="glyphicon glyphicon-ok" title="// 'confirm' | tr //"> </a> diff --git a/src/ui/static/resources/js/components/project-member/edit-project-member.directive.js b/src/ui/static/resources/js/components/project-member/edit-project-member.directive.js index f1e73b48e..b2f7d9c49 100644 --- a/src/ui/static/resources/js/components/project-member/edit-project-member.directive.js +++ b/src/ui/static/resources/js/components/project-member/edit-project-member.directive.js @@ -87,7 +87,8 @@ 'roleName': '=', 'projectId': '=', 'delete': '&', - 'reload': '&' + 'reload': '&', + 'currentRoleId': '@' }, 'controller': EditProjectMemberController, 'controllerAs': 'vm', diff --git a/src/ui/static/resources/js/components/project-member/list-project-member.directive.html b/src/ui/static/resources/js/components/project-member/list-project-member.directive.html index befef1d01..31a5af950 100644 --- a/src/ui/static/resources/js/components/project-member/list-project-member.directive.html +++ b/src/ui/static/resources/js/components/project-member/list-project-member.directive.html @@ -21,23 +21,23 @@ <button class="btn btn-primary" type="button" ng-click="vm.search({projectId: vm.projectId, username: vm.username})"><span class="glyphicon glyphicon-search"></span></button> </span> </div> - <button ng-if="!vm.isOpen" class="btn btn-success" type="button" ng-click="vm.addProjectMember()"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button> - <button ng-if="vm.isOpen" class="btn btn-default" disabled="disabled" type="button"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button> + <button ng-if="vm.roleId == 1 && !vm.isOpen" class="btn btn-success" type="button" ng-click="vm.addProjectMember()"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button> + <button ng-if="vm.roleId == 1 && vm.isOpen" class="btn btn-default" disabled="disabled" type="button"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button> </div> - <add-project-member ng-show="vm.isOpen" is-open="vm.isOpen" project-id="//vm.projectId//" reload='vm.search({projectId: vm.projectId, username: vm.username})'></add-project-member> + <add-project-member ng-if="vm.isOpen" is-open="vm.isOpen" project-id="//vm.projectId//" reload='vm.search({projectId: vm.projectId, username: vm.username})'></add-project-member> <div class="search-pane"> <div class="sub-pane"> <div class="table-head-container"> <table class="table table-pane table-header"> <thead> - <th width="30%">// 'username' | tr //</th><th width="45%">// 'role' | tr //</th><th width="25%">// 'operation' | tr //</th> + <th width="30%">// 'username' | tr //</th><th width="45%">// 'role' | tr //</th><th width="25%" ng-if="vm.roleId == 1">// 'operation' | tr //</th> </thead> </table> </div> <div class="table-body-container"> <table class="table table-pane"> <tbody> - <tr ng-repeat="pr in vm.projectMembers" edit-project-member username="pr.username" project-id="vm.projectId" user-id="pr.user_id" delete="vm.deleteProjectMember({projectId: vm.projectId, userId: pr.user_id})" current-user-id="vm.user.user_id" role-name="pr.role_name" reload='vm.search({projectId: vm.projectId, username: vm.username})'></tr> + <tr ng-repeat="pr in vm.projectMembers" edit-project-member username="pr.username" project-id="vm.projectId" user-id="pr.user_id" delete="vm.deleteProjectMember({projectId: vm.projectId, userId: pr.user_id})" current-user-id="vm.user.user_id" role-name="pr.role_name" reload='vm.search({projectId: vm.projectId, username: vm.username})' current-role-id="//vm.roleId//"></tr> </tbody> </table> </div> diff --git a/src/ui/static/resources/js/components/project-member/list-project-member.directive.js b/src/ui/static/resources/js/components/project-member/list-project-member.directive.js index 992b1d598..6fa330532 100644 --- a/src/ui/static/resources/js/components/project-member/list-project-member.directive.js +++ b/src/ui/static/resources/js/components/project-member/list-project-member.directive.js @@ -56,11 +56,7 @@ } function addProjectMember() { - if(vm.isOpen) { - vm.isOpen = false; - }else{ - vm.isOpen = true; - } + vm.isOpen = !vm.isOpen; } function deleteProjectMember(e) { @@ -105,7 +101,8 @@ 'restrict': 'E', 'templateUrl': '/static/resources/js/components/project-member/list-project-member.directive.html', 'scope': { - 'sectionHeight': '=' + 'sectionHeight': '=', + 'roleId': '@' }, 'link': link, 'controller': ListProjectMemberController, diff --git a/src/ui/static/resources/js/components/project/publicity-button.directive.html b/src/ui/static/resources/js/components/project/publicity-button.directive.html index 05a4688b0..7c9038831 100644 --- a/src/ui/static/resources/js/components/project/publicity-button.directive.html +++ b/src/ui/static/resources/js/components/project/publicity-button.directive.html @@ -12,5 +12,5 @@ See the License for the specific language governing permissions and limitations under the License. --> -<button ng-if="vm.isPublic" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button> -<button ng-if="!vm.isPublic" class="btn btn-danger" ng-click="vm.toggle()">// 'button_off' | tr //</button> \ No newline at end of file +<button ng-if="vm.isPublic" class="btn btn-success" ng-disabled="vm.roleId != 1" ng-click="vm.toggle()">// 'button_on' | tr //</button> +<button ng-if="!vm.isPublic" class="btn btn-danger" ng-disabled="vm.roleId != 1" ng-click="vm.toggle()">// 'button_off' | tr //</button> \ No newline at end of file diff --git a/src/ui/static/resources/js/components/project/publicity-button.directive.js b/src/ui/static/resources/js/components/project/publicity-button.directive.js index 7f108b880..1af415ecc 100644 --- a/src/ui/static/resources/js/components/project/publicity-button.directive.js +++ b/src/ui/static/resources/js/components/project/publicity-button.directive.js @@ -60,7 +60,8 @@ 'templateUrl': '/static/resources/js/components/project/publicity-button.directive.html', 'scope': { 'isPublic': '=', - 'projectId': '=' + 'projectId': '=', + 'roleId': '@' }, 'link': link, 'controller': PublicityButtonController, diff --git a/src/ui/static/resources/js/components/repository/list-repository.directive.html b/src/ui/static/resources/js/components/repository/list-repository.directive.html index 42b02744c..c8f666306 100644 --- a/src/ui/static/resources/js/components/repository/list-repository.directive.html +++ b/src/ui/static/resources/js/components/repository/list-repository.directive.html @@ -23,10 +23,10 @@ <a ng-if="vm.tagCount[repo] === 0" role="button" style="text-decoration: none;" data-toggle="collapse" data-parent="" aria-expanded="true" aria-controls="collapse//$index+1//"> <span class="glyphicon glyphicon-book"></span> //repo// <span class="badge">//vm.tagCount[repo]//</span> </a> - <a ng-show="vm.tagCount[repo] > 0" class="pull-right" style="margin-right: 75px;" href="javascript:void(0)" ng-click="vm.deleteByRepo(repo)" title="// 'delete_repo' | tr //" loading-progress hide-target="true" toggle-in-progress="vm.toggleInProgress[repo + '|']"><span class="glyphicon glyphicon-trash"></span></a> + <a ng-show="vm.tagCount[repo] > 0 && vm.roleId == 1" class="pull-right" style="margin-right: 75px;" href="javascript:void(0)" ng-click="vm.deleteByRepo(repo)" title="// 'delete_repo' | tr //" loading-progress hide-target="true" toggle-in-progress="vm.toggleInProgress[repo + '|']"><span class="glyphicon glyphicon-trash"></span></a> </h4> </div> - <list-tag ng-show="vm.tagCount[repo] > 0" associate-id="$index + 1" repo-name="repo" tag-count="vm.tagCount" toggle-in-progress="vm.toggleInProgress" delete-by-tag="vm.deleteByTag()"></list-tag> + <list-tag ng-show="vm.tagCount[repo] > 0" associate-id="$index + 1" repo-name="repo" tag-count="vm.tagCount" toggle-in-progress="vm.toggleInProgress" delete-by-tag="vm.deleteByTag()" role-id="//vm.roleId//"></list-tag> </div> </div> </div> diff --git a/src/ui/static/resources/js/components/repository/list-repository.directive.js b/src/ui/static/resources/js/components/repository/list-repository.directive.js index 655ac6764..918f80eac 100644 --- a/src/ui/static/resources/js/components/repository/list-repository.directive.js +++ b/src/ui/static/resources/js/components/repository/list-repository.directive.js @@ -19,14 +19,14 @@ .module('harbor.repository') .directive('listRepository', listRepository); - ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName']; + ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName', '$window']; - function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName) { + function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName, $window) { $scope.subsTabPane = 30; var vm = this; - + vm.sectionHeight = {'min-height': '579px'}; vm.filterInput = ''; @@ -104,7 +104,23 @@ } function getRepositoryFailed(response) { - console.log('Failed to list repositories:' + response); + var errorMessage = ''; + if(response.status === 404) { + errorMessage = $filter('tr')('project_does_not_exist'); + }else{ + errorMessage = $filter('tr')('failed_to_get_project'); + } + $scope.$emit('modalTitle', $filter('tr')('error')); + $scope.$emit('modalMessage', errorMessage); + var emitInfo = { + 'confirmOnly': true, + 'contentType': 'text/html', + 'action' : function() { + $window.location.href = '/dashboard'; + } + }; + $scope.$emit('raiseInfo', emitInfo); + console.log('Failed to list repositories:' + response.data); } function searchRepo() { @@ -155,6 +171,7 @@ function deleteRepositorySuccess(data, status) { vm.toggleInProgress[vm.repoName + '|' + vm.tag] = false; vm.retrieve(); + $scope.$broadcast('refreshTags', true); } function deleteRepositoryFailed(data, status) { @@ -180,7 +197,8 @@ 'restrict': 'E', 'templateUrl': '/static/resources/js/components/repository/list-repository.directive.html', 'scope': { - 'sectionHeight': '=' + 'sectionHeight': '=', + 'roleId': '@' }, 'link': link, 'controller': ListRepositoryController, diff --git a/src/ui/static/resources/js/components/repository/list-tag.directive.html b/src/ui/static/resources/js/components/repository/list-tag.directive.html index 94fc5dab0..029b02a2d 100644 --- a/src/ui/static/resources/js/components/repository/list-tag.directive.html +++ b/src/ui/static/resources/js/components/repository/list-tag.directive.html @@ -5,7 +5,7 @@ <th width="20%"><span class="glyphicon glyphicon-tags"></span> // 'tag' | tr //</th> <th width="15%" style="text-align: center;">// 'image_details' | tr //</th> <th width="40%">// 'pull_command' | tr //</th> - <th width="15%" style="text-align: center;">// 'operation' | tr //</th> + <th width="15%" ng-if="vm.roleId == 1" style="text-align: center;">// 'operation' | tr //</th> </thead> <tbody> <tr ng-repeat="tag in vm.tags"> @@ -14,7 +14,7 @@ <td> <pull-command repo-name="//vm.repoName//" tag="//tag//"></pull-command> </td> - <td style="text-align: center;"><a href="javascript:void(0);" ng-click="vm.deleteTag({repoName: vm.repoName, tag: tag})" title="// 'delete_tag' | tr //" loading-progress hide-target="true" toggle-in-progress="vm.toggleInProgress[vm.repoName +'|'+ tag]"><span class="glyphicon glyphicon-trash"></span></a></td> + <td style="text-align: center;"><a ng-if="vm.roleId == 1" href="javascript:void(0);" ng-click="vm.deleteTag({repoName: vm.repoName, tag: tag})" title="// 'delete_tag' | tr //" loading-progress hide-target="true" toggle-in-progress="vm.toggleInProgress[vm.repoName +'|'+ tag]"><span class="glyphicon glyphicon-trash"></span></a></td> </tr> </tbody> </table> diff --git a/src/ui/static/resources/js/components/repository/list-tag.directive.js b/src/ui/static/resources/js/components/repository/list-tag.directive.js index f47b0b917..279446500 100644 --- a/src/ui/static/resources/js/components/repository/list-tag.directive.js +++ b/src/ui/static/resources/js/components/repository/list-tag.directive.js @@ -85,7 +85,8 @@ 'associateId': '=', 'repoName': '=', 'toggleInProgress': '=', - 'deleteByTag': '&' + 'deleteByTag': '&', + 'roleId': '@' }, 'replace': true, 'controller': ListTagController, diff --git a/src/ui/static/resources/js/components/user-log/user-log.directive.html b/src/ui/static/resources/js/components/user-log/user-log.directive.html index 4fd762d5e..9764fc7af 100644 --- a/src/ui/static/resources/js/components/user-log/user-log.directive.html +++ b/src/ui/static/resources/js/components/user-log/user-log.directive.html @@ -32,7 +32,7 @@ </tr> <tr ng-if="vm.integratedLogs.length > 0" ng-repeat="t in vm.integratedLogs"> <td width="18%">//t.username//</td> - <td width="28%"><a href="javascript:void(0);" ng-click="vm.gotoLog(t.project_id, t.username)">//t.repo_name//</a></td> + <td width="28%"><a href="javascript:void(0);" ng-click="vm.gotoRepo(t.project_id, t.repo_name)">//t.repo_name//</a></td> <td width="15%">//t.repo_tag//</td> <td width="14%">//t.operation//</td> <td width="25%">//t.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td> diff --git a/src/ui/static/resources/js/components/user-log/user-log.directive.js b/src/ui/static/resources/js/components/user-log/user-log.directive.js index ceb86a778..0a462c466 100644 --- a/src/ui/static/resources/js/components/user-log/user-log.directive.js +++ b/src/ui/static/resources/js/components/user-log/user-log.directive.js @@ -29,7 +29,7 @@ .success(listIntegratedLogSuccess) .error(listIntegratedLogFailed); - vm.gotoLog = gotoLog; + vm.gotoRepo = gotoRepo; function listIntegratedLogSuccess(data) { vm.integratedLogs = data || []; @@ -42,8 +42,8 @@ console.log('Failed to get user logs:' + data); } - function gotoLog(projectId, username) { - $window.location.href = '/repository#/logs?project_id=' + projectId + '#' + encodeURIComponent(username); + function gotoRepo(projectId, repoName) { + $window.location.href = '/repository#/repositories?project_id=' + projectId + '#' + encodeURIComponent(repoName); } } diff --git a/src/ui/static/resources/js/components/user/list-user.directive.html b/src/ui/static/resources/js/components/user/list-user.directive.html index 31e540295..10147219e 100644 --- a/src/ui/static/resources/js/components/user/list-user.directive.html +++ b/src/ui/static/resources/js/components/user/list-user.directive.html @@ -31,7 +31,7 @@ <th width="20%">// 'email' | tr //</th> <th width="35%">// 'registration_time' | tr //</th> <th width="15%">// 'administrator' | tr //</th> - <th width="20%">// 'operation' | tr //</th> + <th width="20%" ng-if="vm.authMode === 'db_auth'">// 'operation' | tr //</th> </thead> </table> </div> @@ -46,7 +46,7 @@ <td width="15%"> <toggle-admin current-user="vm.currentUser" has-admin-role="u.has_admin_role" user-id="//u.user_id//"></toggle-admin> </td> - <td width="20%"> + <td width="20%" ng-if="vm.authMode === 'db_auth'"> <a ng-if="vm.currentUser.user_id != u.user_id" href="javascript:void(0)" ng-click="vm.confirmToDelete(u.user_id, u.username)"><span class="glyphicon glyphicon-trash"></span></a> </td> </tr> diff --git a/src/ui/static/resources/js/components/user/list-user.directive.js b/src/ui/static/resources/js/components/user/list-user.directive.js index 18cd60a2c..77b1903db 100644 --- a/src/ui/static/resources/js/components/user/list-user.directive.js +++ b/src/ui/static/resources/js/components/user/list-user.directive.js @@ -98,6 +98,9 @@ 'restrict': 'E', 'templateUrl': '/static/resources/js/components/user/list-user.directive.html', 'link': link, + 'scope': { + 'authMode': '@' + }, 'controller': ListUserController, 'controllerAs': 'vm', 'bindToController': true diff --git a/src/ui/static/resources/js/components/user/toggle-admin.directive.js b/src/ui/static/resources/js/components/user/toggle-admin.directive.js index c332823f5..b4d3845ab 100644 --- a/src/ui/static/resources/js/components/user/toggle-admin.directive.js +++ b/src/ui/static/resources/js/components/user/toggle-admin.directive.js @@ -26,35 +26,26 @@ var vm = this; vm.isAdmin = (vm.hasAdminRole === 1); - vm.enabled = vm.isAdmin ? 0 : 1; vm.toggle = toggle; vm.editable = (vm.currentUser.user_id !== Number(vm.userId)); function toggle() { - ToggleAdminService(vm.userId, vm.enabled) + ToggleAdminService(vm.userId, vm.isAdmin ? 0 : 1) .success(toggleAdminSuccess) .error(toggleAdminFailed); } - + function toggleAdminSuccess(data, status) { - if(vm.isAdmin) { - vm.isAdmin = false; - }else{ - vm.isAdmin = true; - } console.log('Toggled userId:' + vm.userId + ' to admin:' + vm.isAdmin); + vm.isAdmin = !vm.isAdmin; } function toggleAdminFailed(data, status) { + console.log('Failed to toggle admin:' + data); + vm.isAdmin = !vm.isAdmin; $scope.$emit('modalTitle', $filter('tr')('error')); $scope.$emit('modalMessage', $filter('tr')('failed_to_toggle_admin')); $scope.$emit('raiseError', true); - if(vm.isAdmin) { - vm.isAdmin = false; - }else{ - vm.isAdmin = true; - } - console.log('Failed to toggle admin:' + data); } } diff --git a/src/ui/static/resources/js/components/validator/email.validator.js b/src/ui/static/resources/js/components/validator/email.validator.js new file mode 100644 index 000000000..c7421bfa8 --- /dev/null +++ b/src/ui/static/resources/js/components/validator/email.validator.js @@ -0,0 +1,44 @@ +/* + Copyright (c) 2016 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. +*/ +(function() { + + 'use strict'; + + angular + .module('harbor.validator') + .directive('email', email); + + email.$inject = ['EMAIL_REGEXP']; + + function email(EMAIL_REGEXP) { + var directive = { + 'require' : 'ngModel', + 'link': link + }; + return directive; + + function link (scope, element, attrs, ctrl) { + + ctrl.$validators.email = validator; + + function validator(modelValue, viewValue) { + + return EMAIL_REGEXP.test(modelValue); + + } + } + } + +})(); \ No newline at end of file diff --git a/src/ui/static/resources/js/components/validator/validator.config.js b/src/ui/static/resources/js/components/validator/validator.config.js index 187f55008..08e892cdc 100644 --- a/src/ui/static/resources/js/components/validator/validator.config.js +++ b/src/ui/static/resources/js/components/validator/validator.config.js @@ -20,5 +20,7 @@ .module('harbor.validator') .constant('INVALID_CHARS', [",","~","#", "$", "%"]) .constant('PASSWORD_REGEXP', /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$/) - .constant('PROJECT_REGEXP', /^[a-z0-9](?:-*[a-z0-9])*(?:[._][a-z0-9](?:-*[a-z0-9])*)*$/); + .constant('PROJECT_REGEXP', /^[a-z0-9](?:-*[a-z0-9])*(?:[._][a-z0-9](?:-*[a-z0-9])*)*$/) + .constant('EMAIL_REGEXP', /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/); + })(); \ No newline at end of file diff --git a/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js b/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js index ca2654210..96e44203b 100644 --- a/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js +++ b/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js @@ -234,6 +234,7 @@ var locale_messages = { 'failed_to_get_project_member': 'Failed to get current project member.', 'failed_to_delete_repo': 'Failed to delete repository. ', 'failed_to_delete_repo_insuffient_permissions': 'Failed to delete repository, insuffient permissions.', + 'failed_to_get_repo': 'Failed to get repositories.', 'failed_to_get_tag': 'Failed to get tag.', 'failed_to_get_log': 'Failed to get logs.', 'failed_to_get_project': 'Failed to get projects.', @@ -264,6 +265,7 @@ var locale_messages = { 'failed_to_update_destination': 'Failed to update destination.', 'failed_to_toggle_publicity_insuffient_permissions': 'Failed to toggle project publicity, insuffient permissions.', 'failed_to_toggle_publicity': 'Failed to toggle project publicity.', + 'project_does_not_exist': 'Project does not exist.', 'project_admin': 'Project Admin', 'developer': 'Developer', 'guest': 'Guest', diff --git a/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js b/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js index c91801d5f..2a83787ed 100644 --- a/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js +++ b/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js @@ -234,6 +234,7 @@ var locale_messages = { 'failed_to_get_project_member': '无法获取当前项目成员。', 'failed_to_delete_repo': '无法删除镜像仓库。', 'failed_to_delete_repo_insuffient_permissions': '无法删除镜像仓库,权限不足。', + 'failed_to_get_repo': '获取镜像仓库数据失败。', 'failed_to_get_tag': '获取标签数据失败。', 'failed_to_get_log': '获取日志数据失败。', 'failed_to_get_project': '获取项目数据失败。', @@ -264,6 +265,7 @@ var locale_messages = { 'failed_to_update_destination': '修改目标失败。', 'failed_to_toggle_publicity_insuffient_permissions': '切换项目公开性失败,权限不足。', 'failed_to_toggle_publicity': '切换项目公开性失败。', + 'project_does_not_exist': '项目不存在。', 'project_admin': '项目管理员', 'developer': '开发人员', 'guest': '访客', diff --git a/src/ui/static/resources/js/session/session.current-user.js b/src/ui/static/resources/js/session/session.current-user.js index 9e51af4cd..a02355c35 100644 --- a/src/ui/static/resources/js/session/session.current-user.js +++ b/src/ui/static/resources/js/session/session.current-user.js @@ -20,9 +20,9 @@ .module('harbor.session') .controller('CurrentUserController', CurrentUserController); - CurrentUserController.$inject = ['$scope', 'CurrentUserService', 'currentUser', '$window', '$document']; + CurrentUserController.$inject = ['$scope', 'CurrentUserService', 'currentUser', '$window', '$document', 'LogOutService']; - function CurrentUserController($scope, CurrentUserService, currentUser, $window, $document) { + function CurrentUserController($scope, CurrentUserService, currentUser, $window, $document, LogOutService) { var vm = this; @@ -32,16 +32,27 @@ function getCurrentUserComplete(response) { if(angular.isDefined(response)) { - currentUser.set(response.data); - if(location.pathname === '/') { + currentUser.set(response.data); + if(location.pathname === '/') { $window.location.href = '/dashboard'; } } } function getCurrentUserFailed(e){ - console.log('No session of current user.'); + console.log('Failed to get current user:' + e); + LogOutService() + .success(logOutSuccess) + .error(logOutFailed); } + + function logOutSuccess(data, status) { + currentUser.unset(); + } + + function logOutFailed(data, status) { + console.log('Failed to log out:' + data); + } } })(); \ No newline at end of file diff --git a/src/ui/views/account-settings.htm b/src/ui/views/account-settings.htm index 7389ca51a..55b7c0d24 100644 --- a/src/ui/views/account-settings.htm +++ b/src/ui/views/account-settings.htm @@ -29,7 +29,7 @@ <div class="form-group"> <label for="email" class="col-sm-3 control-label">// 'email' | tr //:</label> <div class="col-sm-7"> - <input type="email" class="form-control" id="email" ng-model="user.email" name="uEmail" required> + <input type="text" class="form-control" id="email" ng-model="user.email" name="uEmail" required email> <div class="error-message" ng-messages="form.uEmail.$touched && form.uEmail.$error"> <span ng-message="required">// 'email_is_required' | tr //</span> <span ng-message="email">// 'email_content_illegal' | tr //</span> diff --git a/src/ui/views/admin-options.htm b/src/ui/views/admin-options.htm index 68c225e0b..8e57a8ff0 100644 --- a/src/ui/views/admin-options.htm +++ b/src/ui/views/admin-options.htm @@ -24,7 +24,7 @@ <span ng-if="vm.toggle">// 'system_management' | tr //</span> <a ng-if="!vm.toggle" href="#/destinations" class="title-color" ng-click="vm.toggleAdminOption({target: 'system_management'})">// 'system_management' | tr //</a> </h4> - <list-user ng-if="vm.target === 'users'"></list-user> + <list-user ng-if="vm.target === 'users'" auth-mode="{{ .AuthMode }}"></list-user> <system-management ng-if="vm.target === 'system_management'"></system-management> </div> </div> diff --git a/src/ui/views/forgot-password.htm b/src/ui/views/forgot-password.htm index 389c981b1..ddbbff452 100644 --- a/src/ui/views/forgot-password.htm +++ b/src/ui/views/forgot-password.htm @@ -24,7 +24,7 @@ <div class="form-group"> <label for="email" class="col-sm-3 control-label">// 'email' | tr //:</label> <div class="col-sm-7"> - <input type="email" class="form-control" id="email" ng-model="user.email" ng-model-options="{ debounce: 500 }" ng-change="vm.reset()" name="uEmail" required data-target="email"> + <input type="text" class="form-control" id="email" ng-model="user.email" ng-model-options="{ debounce: 500 }" ng-change="vm.reset()" name="uEmail" required data-target="email" email> <div class="error-message"> <div ng-messages="(form.$submitted || form.uEmail.$touched) && form.uEmail.$error"> <span ng-message="required">// 'email_is_required' | tr //</span> diff --git a/src/ui/views/optional-menu.htm b/src/ui/views/optional-menu.htm index b32ec201f..56d78b9a1 100644 --- a/src/ui/views/optional-menu.htm +++ b/src/ui/views/optional-menu.htm @@ -21,7 +21,9 @@ {{ if eq .AddNew true }} <li><a href="/add_new"><span class="glyphicon glyphicon-plus"></span> // 'add_new_title' | tr //</a></li> {{ end }} + {{ if eq .SettingAccount true }} <li><a href="/account_setting"><span class="glyphicon glyphicon-pencil"></span> // 'account_setting' | tr //</a></li> + {{ end }} <li class="dropdown-submenu"> <a tabindex="-1" href="#"><span class="glyphicon glyphicon-globe"></span> //vm.languageName//</a> <ul class="dropdown-menu"> diff --git a/src/ui/views/project.htm b/src/ui/views/project.htm index 7d7d32480..7be0c4593 100644 --- a/src/ui/views/project.htm +++ b/src/ui/views/project.htm @@ -47,8 +47,8 @@ <th width="15%">// 'repositories' | tr //</th> <th width="15%" ng-if="!vm.isPublic">// 'role' | tr //</th> <th width="20%">// 'creation_time' | tr //</th> - <th width="15%">// 'publicity' | tr //</th> - <th width="10%">// 'operation' | tr //</th> + <th width="15%" ng-if="!vm.isPublic">// 'publicity' | tr //</th> + <th width="10%" ng-if="!vm.isPublic">// 'operation' | tr //</th> </thead> </table> </div> @@ -63,9 +63,9 @@ <td width="15%">//p.repo_count//</td> <td width="15%" ng-if="vm.isPublic === 0">//vm.getProjectRole(p.current_user_role_id) | tr//</td> <td width="20%">//p.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td> - <td width="15%"><publicity-button is-public="p.public" project-id="p.project_id"></publicity-button></td> - <td width="10%"> - <a href="javascript:void(0)" ng-click="vm.confirmToDelete(p.project_id, p.name)"><span class="glyphicon glyphicon-trash"></span></a> + <td width="15%" ng-if="!vm.isPublic"><publicity-button is-public="p.public" project-id="p.project_id" role-id="//p.current_user_role_id//"></publicity-button></td> + <td width="10%" ng-if="!vm.isPublic"> + <a ng-if="p.current_user_role_id == 1" href="javascript:void(0)" ng-click="vm.confirmToDelete(p.project_id, p.name)"><span class="glyphicon glyphicon-trash"></span></a> </td> </tr> </tbody> diff --git a/src/ui/views/repository.htm b/src/ui/views/repository.htm index 0ee168ccc..7a9f94d45 100644 --- a/src/ui/views/repository.htm +++ b/src/ui/views/repository.htm @@ -30,13 +30,13 @@ <navigation-details target="vm.target" ng-show="vm.isProjectMember"></navigation-details> </span> </div> - <retrieve-projects target="vm.target" is-open="vm.isOpen" selected-project="vm.selectedProject" is-project-member="vm.isProjectMember" is-public="vm.isPublic"></retrieve-projects> + <retrieve-projects target="vm.target" is-open="vm.isOpen" selected-project="vm.selectedProject" role-id="vm.roleId" is-project-member="vm.isProjectMember" is-public="vm.isPublic"></retrieve-projects> <!-- Tab panes --> <div class="tab-content" ng-click="vm.closeRetrievePane()"> <input type="hidden" id="HarborRegUrl" value="{{.HarborRegUrl}}"> - <list-repository ng-if="vm.target === 'repositories'" section-height="vm.sectionHeight"></list-repository> + <list-repository ng-if="vm.target === 'repositories'" section-height="vm.sectionHeight" role-id="//vm.roleId//"></list-repository> <list-replication ng-if="vm.target === 'replication'" section-height="vm.sectionHeight"></list-replication> - <list-project-member ng-if="vm.target === 'users'" section-height="vm.sectionHeight"></list-project-member> + <list-project-member ng-if="vm.target === 'users'" section-height="vm.sectionHeight" role-id="//vm.roleId//"></list-project-member> <list-log ng-if="vm.target === 'logs'" section-height="vm.sectionHeight" target="vm.target"></list-log> </div> </div> diff --git a/src/ui/views/sections/script-include.htm b/src/ui/views/sections/script-include.htm index 74805fc4b..7404a9d82 100644 --- a/src/ui/views/sections/script-include.htm +++ b/src/ui/views/sections/script-include.htm @@ -130,6 +130,7 @@ <script src="/static/resources/js/components/validator/user-exist.validator.js"></script> <script src="/static/resources/js/components/validator/invalid-chars.validator.js"></script> <script src="/static/resources/js/components/validator/project-name.validator.js"></script> +<script src="/static/resources/js/components/validator/email.validator.js"></script> <script src="/static/resources/js/components/search/search.module.js"></script> <script src="/static/resources/js/components/search/search.directive.js"></script> diff --git a/src/ui/views/sign-up.htm b/src/ui/views/sign-up.htm index 4bae1a5d0..76bb5b7be 100644 --- a/src/ui/views/sign-up.htm +++ b/src/ui/views/sign-up.htm @@ -45,12 +45,11 @@ <div class="form-group"> <label for="email" class="col-sm-3 control-label">// 'email' | tr //:</label> <div class="col-sm-7"> - <input type="email" class="form-control" id="email" ng-model="user.email" name="uEmail" ng-model-options="{ updateOn: 'blur' }" required user-exists data-target="email" maxlength="50"> - <div class="error-message" ng-messages="(form.$submitted || form.uEmail.$touched) &&form.uEmail.$error"> + <input type="text" class="form-control" id="email" ng-model="user.email" name="uEmail" ng-model-options="{ updateOn: 'blur' }" required user-exists data-target="email" email> + <div class="error-message" ng-messages="(form.$submitted || form.uEmail.$touched) && form.uEmail.$error"> <span ng-message="required">// 'email_is_required' | tr //</span> <span ng-message="email">// 'email_content_illegal' | tr //</span> <span ng-message="userExists">// 'email_has_been_taken' | tr //</span> - <span ng-message="maxlength">// 'email_is_too_long' | tr //</span> </div> <p class="help-block small-size-fonts">// 'email_desc' | tr //</p> </div> diff --git a/tests/docker-compose.test.yml b/tests/docker-compose.test.yml index e37f16bc8..8f233bd9b 100644 --- a/tests/docker-compose.test.yml +++ b/tests/docker-compose.test.yml @@ -1,7 +1,7 @@ version: '2' services: registry: - image: library/registry:2.4.0 + image: library/registry:2.5.0 restart: always volumes: - /data/registry:/storage diff --git a/tests/testcases/Group1-user-management/1-01-DB-user-registration.md b/tests/testcases/Group1-user-management/1-01-DB-user-registration.md new file mode 100644 index 000000000..078849afd --- /dev/null +++ b/tests/testcases/Group1-user-management/1-01-DB-user-registration.md @@ -0,0 +1,41 @@ +Test 1-01 - User Registration (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can register an account (singup) when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +1. On the Harbor's home page, click "Sign Up" +2. Enter user information to register a user. +3. Use the username of the newly registered user to log in to the UI. +4. Log out from the UI. +5. Use the email of the newly registered user to log in to the UI. +6. On a Docker client host, use `docker login <harbor_host>` command to verify the user can log in by either the **username** or **email** . (verify both) +7. Log out from the UI and register another new user. Try to provide invalid values of input to see if validation works: + + +* username is the same as an existing user +* username is very long in length +* wrong email formatting +* email is very long in length +* password input does not compliant to password rule +* two passwords do not match + + +# Expected Outcome: +* A new user created in step 2. +* The new user can log in via UI in Step 3 and Step 5. +* The new user can log in via docker client in Step 6 by email and username. +* Invalid input during sign up can be rejected, proper error messages can be displayed in Step 7. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-02-DB-user-log-in-log-out.md b/tests/testcases/Group1-user-management/1-02-DB-user-log-in-log-out.md new file mode 100644 index 000000000..4b1965827 --- /dev/null +++ b/tests/testcases/Group1-user-management/1-02-DB-user-log-in-log-out.md @@ -0,0 +1,36 @@ +Test 1-02 - User Log In and Log Out (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can log in and log out when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +**NOTE:** Use a non-admin user for this test case. Admin user has other test cases. + +1. A non-admin user logs in to the UI by username. +2. The user logs out from the UI. +3. A non-admin user logs in to the UI by email. +4. The user logs out from the UI. +5. Use the incorrect password and username/email of the user to log in to the UI and check the error message. +6. On a Docker client host, use `docker login <harbor_host>` command to verify the user can log in by either the **username** or **email** . (check both) +7. Use `docker login <harbor_host>` command to log in with incorrect password by either the **username** or **email** . + + +# Expected Outcome: +* The user can log in via UI in Step 1 & 3, verify the dashboard and navigation bar are for a non-admin user. (should not see admin options) +* After the user logged out in Step 2 & 4, the login page will be displayed again. +* The error message in Step 5 should not show which input value is incorrect. It should only display the username(email) and password combination is incorrect. +* Docker client can log in in Step 6. +* Docker client fails to log in in Step 7. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-03-DB-user-update-password.md b/tests/testcases/Group1-user-management/1-03-DB-user-update-password.md new file mode 100644 index 000000000..d51c2208d --- /dev/null +++ b/tests/testcases/Group1-user-management/1-03-DB-user-update-password.md @@ -0,0 +1,36 @@ +Test 1-03 - User update password (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can update password when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +**NOTE:** Use a **non-admin** user for this test case. Admin user has other test cases. + +1. A non-admin user logs in to the UI by **username**. +2. The user changes his/her own password. +3. The user logs out. +4. The same user logs in to the UI by **email** using the new password. +5. The user can log in using `docker login` command using the new password. +6. The user goes to the page to change his/her own password. Provide invalid values of input to see if validation works: + +* old password is incorrect +* password input does not compliant to password rule +* two passwords do not match + +# Expected Outcome: +* Password can be changed in Step 2. +* User can log in using new password in Step 4. +* In Step 6, the user cannot change password due to various errors. Proper error message should be displayed. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-04-DB-user-update-account-settings.md b/tests/testcases/Group1-user-management/1-04-DB-user-update-account-settings.md new file mode 100644 index 000000000..a49656397 --- /dev/null +++ b/tests/testcases/Group1-user-management/1-04-DB-user-update-account-settings.md @@ -0,0 +1,35 @@ +Test 1-04 - User update account settings (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can update his/her account settings when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +**NOTE:** Use a **non-admin** user for this test case. Admin user has other test cases. + +1. A non-admin user logs in to UI. +2. The user changes his/her account settings, including email, full name and comments. +3. The user logs out. +4. The same user logs in again using **new email**, and verify the user's account settings had been changed. +5. The user goes to the page to change his/her settings again. Provide invalid values of input to see if validation works: + +* email formatting +* very long email address string +* required fields are empty + +# Expected Outcome: +* Account settings can be changed in Step 2. +* User can log in using new email in Step 4 and the settings are the same as input in Step 2. +* In Step 5, the user cannot change account settings due to various errors. Proper error message should be displayed. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-05-LDAP-user-log-in-log-out.md b/tests/testcases/Group1-user-management/1-05-LDAP-user-log-in-log-out.md new file mode 100644 index 000000000..0d16907fb --- /dev/null +++ b/tests/testcases/Group1-user-management/1-05-LDAP-user-log-in-log-out.md @@ -0,0 +1,41 @@ +Test 1-05 - LDAP User Log In and Log Out (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can log in and log out when users are managed externally by an LDAP server (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP server. (auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP server. +* A linux host with Docker CLI installed (Docker client). +* An LDAP server has been set up and it has a few users available for testing. + +# Test Steps: + +1. A user has **NEVER** logged in to Harbor. He/she logs in to the UI for the first time by his/her id in LDAP. The id could be the uid attribte (or what is configured in ldap_uid) of his/her LDAP user DN. +2. The user logs out from the UI. +3. The user logs in again to the UI, should not see his/her own account settings and cannot change password(need to go to LDAP/AD for this). +4. The user logs out from the UI. +5. Use the incorrect password and username of the user to log in to the UI and check the error message. +6. On a Docker client host, use `docker login <harbor_host>` command to verify the user can log in by username/password. +7. Run `docker login <harbor_host>` command to log in with incorrect password of the user. +8. Log in as a system admin to UI, go to "admin Options" and should see above LDAP user in the list. System admin can assign or remove system admin role to the above LDAP user. +9. Disable or remove the user in LDAP. +10. The user should no longer log in to UI or by Docker client. + +# Expected Outcome: +* The user can log in to UI by LDAP id in Step 1 & 3, verify the dashboard and navigation bar are for a non-admin user. (should not see admin options) +* In Step 3, also verify that the user cannot update his/her account settings and cannot change password. +* After the user logged out in Step 2 & 4, the login page will be displayed again. +* The error message in Step 5 should not show which input value is incorrect. It should only display the username(email) and password combination is incorrect. +* Docker client can log in in Step 6. +* Docker client fails to log in in Step 7. +* LDAP user should have no difference from a user in local database in Step 8. +* The user's login should fail in Step 10. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-06-AD-user-log-in-log-out.md b/tests/testcases/Group1-user-management/1-06-AD-user-log-in-log-out.md new file mode 100644 index 000000000..36072452d --- /dev/null +++ b/tests/testcases/Group1-user-management/1-06-AD-user-log-in-log-out.md @@ -0,0 +1,41 @@ +Test 1-06 - AD (Active Directory) User Log In and Log Out (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can log in and log out when users are managed externally by an AD server (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an AD server. (auth_mode is set to **ldap_auth** .) The user data is stored in an AD server. +* A linux host with Docker CLI installed (Docker client). +* An Active Directory (AD) server has been set up and it has a few users available for testing. + +# Test Steps: + +1. A user has **NEVER** logged in to Harbor. He/she logs in to the UI for the first time by his/her id in AD. The id could be the cn attribte (or what is configured in ldap_uid) of his/her AD user DN. +2. The user logs out from the UI. +3. The user logs in again to the UI, should not see his/her own account settings and cannot change password(need to go to LDAP/AD for this). +4. The user logs out from the UI. +5. Use the incorrect password and username of the user to log in to the UI and check the error message. +6. On a Docker client host, use `docker login <harbor_host>` command to verify the user can log in by username/password. +7. Run `docker login <harbor_host>` command to log in with incorrect password of the user. +8. Log in as a system admin to UI, go to "admin Options" and should see above AD user in the list. System admin can assign or remove system admin role of the above AD user. +9. Disable or remove the user in AD. +10. The user should no longer log in to UI or by Docker client. + +# Expected Outcome: +* The user can log in to UI by AD id in Step 1 & 3, verify the dashboard and navigation bar are for a non-admin user. (should not see admin options) +* In Step 3, also verify that the user cannot update his/her account settings and cannot change password. +* After the user logged out in Step 2 & 4, the login page will be displayed again. +* The error message in Step 5 should not show which input value is incorrect. It should only display the username(email) and password combination is incorrect. +* Docker client can log in in Step 6. +* Docker client fails to log in in Step 7. +* AD user should have no difference from a user in local database in Step 8. +* The user's login should fail in Step 10. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-07-LDAP-mode-general.md b/tests/testcases/Group1-user-management/1-07-LDAP-mode-general.md new file mode 100644 index 000000000..532d4241c --- /dev/null +++ b/tests/testcases/Group1-user-management/1-07-LDAP-mode-general.md @@ -0,0 +1,37 @@ +Test 1-07 - LDAP Mode general functions +======= + +# Purpose: + +To verify that Harbor's UI works properly in LDAP mode. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an AD or LDAP server. (auth_mode is set to **ldap_auth** .) +* An Active Directory (AD) or LDAP server has been set up and it has a few users available for testing. + +# Test Steps: + +1. The login page should not have "sign up" button. There is no need to allow self-registration. +2. Log in as a non system admin user(LDAP/AD) to the UI, he/she should NOT see the option of changing password or updating account settings. +3. Log out the user. +4. Log in as a system admin user to the UI, he/she should see the option of changing his/her own password or updating account settings. +5. The system admin user should NOT see the option of adding a new user. +6. The system admin user should see above LDAP/AD user in the list. +7. From the list, the system admin assigns system admin role to an AD/LDAP user A. +8. On a different browser(e.g. if the admin logs in using Chrome, then choose Safari or FireFox ), log in as the AD/LDAP user A to verify that user A has admin privilege. +9. From the list, the system admin removes system admin role from user A. +10. On a different browser, refresh the UI to verify user A has no admin privilege any more. +11. The system admin user deletes an AD/LDAP user in Harbor. **NOTE:** The user can log in again to regain access, however, all the previous projects he/she is a member of are lost. +To really disable a user's login, the user must be removed or disabled in AD or LDAP. + +# Expected Outcome: +* As described in steps 1-6. +* A LDAP/AD user can be assigned or removed admin role in Step 7-10. +* The user can be deleted successfully in Step 11. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-08-admin-log-in-log-out.md b/tests/testcases/Group1-user-management/1-08-admin-log-in-log-out.md new file mode 100644 index 000000000..1cd641019 --- /dev/null +++ b/tests/testcases/Group1-user-management/1-08-admin-log-in-log-out.md @@ -0,0 +1,32 @@ +Test 1-08 - Admin User Log In and Log Out (DB Mode or LDAP mode) +======= + +# Purpose: + +To verify that an admin user can log in and log out. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database or LDAP server. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +1. The admin user logs in to the UI by username. +2. The admin user logs out from the UI. +3. Use the incorrect password of the admin user to log in to the UI and check the error message. +4. On a Docker client host, use `docker login <harbor_host>` command to verify the admin user can log in. +5. Use `docker login <harbor_host>` command to log in with incorrect password. + + +# Expected Outcome: +* The admin user can log in/out successfully in Step 1 & 2. +* The error message in Step 3 should not show which input value is incorrect. It should only display the username and password combination is incorrect. +* Docker client can log in in Step 4. +* Docker client fails to log in in Step 5. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-09-admin-create-delete-user.md b/tests/testcases/Group1-user-management/1-09-admin-create-delete-user.md new file mode 100644 index 000000000..960246cb3 --- /dev/null +++ b/tests/testcases/Group1-user-management/1-09-admin-create-delete-user.md @@ -0,0 +1,44 @@ +Test 1-09 - Admin User Create, Delete and Recreate a User(DB Mode) +======= + +# Purpose: + +To verify that an admin user can create/delete/recreate a user when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +1. The admin user logs in to the UI. +2. The admin user creates a user from the UI. +3. On a different browser, log in as the newly created user. +4. The user views his/her own account settings. +5. The user create two projects in the UI. +6. On a Docker client host, use `docker login <harbor_host>` command to verify the user can log in. +7. The admin user deletes the user from the UI. +8. When clicking on any link on the page, the deleted user's session on the different browswer should be redirected to the login page and logged out. +9. On a Docker client host, use `docker login <harbor_host>` command to verify the user cannot log in. +10. The admin user re-creates a user with the same username of the deleted user. +11. On a different browser, log in as the re-created user. +12. The user views his/her own account settings. +13. The user views "My Projects" to see what projects he/she owns. +14. On a Docker client host, use `docker login <harbor_host>` command to verify the re-created user can log in. +15. The admin user view logs from the dashboard and should see two items of two project creation of the deleted user. The user has special number associated with him/her username. + +# Expected Outcome: +* The newly created user can log in successfully in Step 3. +* The newly created user can view his/her own settings as entered by the admin in Step 4. +* The newly created user can create project successfully in Step 5. +* The admin should be able to re-create the user in Step 10. +* The re-created user should be able to log in, however, the previous projects no longer belong to him/her (Step 11-13). +* Docker client logs in successfully in Step 14. +* Should see special user id in logs in Step 15. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-10-admin-update-account-settings.md b/tests/testcases/Group1-user-management/1-10-admin-update-account-settings.md new file mode 100644 index 000000000..9ffe0ba3e --- /dev/null +++ b/tests/testcases/Group1-user-management/1-10-admin-update-account-settings.md @@ -0,0 +1,28 @@ +Test 1-10 - Admin User update account settings (DB Mode) +======= + +# Purpose: + +To verify that the admin user can update his/her account settings. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +1. Thw admin user logs in to UI. +2. The user changes his/her account settings, including email, full name and comments. +3. The user logs out. +4. The admin user logs in again using **new email**, and verify the account settings had been changed. + +# Expected Outcome: +* Account settings can be changed in Step 2. +* User can log in using new email in Step 4 and the settings are the same as input in Step 2. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-11-admin-update-password.md b/tests/testcases/Group1-user-management/1-11-admin-update-password.md new file mode 100644 index 000000000..e4a51ba08 --- /dev/null +++ b/tests/testcases/Group1-user-management/1-11-admin-update-password.md @@ -0,0 +1,29 @@ +Test 1-11 - Admin User update password (DB Mode) +======= + +# Purpose: + +To verify that the admin user can update password. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +1. The admin user logs in to the UI by **username**. +2. The admin user changes his/her own password. +3. The admin user logs out. +4. The admin user logs in to the UI by **email** using the new password. +5. The admin user can log in using `docker login` command using the new password. + +# Expected Outcome: +* Password can be changed in Step 2. +* User can log in using new password in Step 4. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-12-admin-assign-sys-admin-role.md b/tests/testcases/Group1-user-management/1-12-admin-assign-sys-admin-role.md new file mode 100644 index 000000000..5db6436e0 --- /dev/null +++ b/tests/testcases/Group1-user-management/1-12-admin-assign-sys-admin-role.md @@ -0,0 +1,28 @@ +Test 1-12 - Admin User Assign and Remove Admin Role to a User. +======= + +# Purpose: + +To verify that Admin user can assign/remove admin role to/from a user. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. + +# Test Steps: + +1. Log in as the admin user to the UI. +2. The admin user should see a list of users in "Admin Options". +3. From the list, the admin user assigns system admin role to a user A. +4. On a different browser(e.g. if the admin logs in using Chrome, then choose Safari or FireFox ), log in as user A to verify that user A has admin privilege. +5. From the list, the admin user removes system admin role from user A. +6. On a different browser, refresh the UI to verify user A has no admin privilege any more. + +# Expected Outcome: +* As described in steps 1-6. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-13-admin-search-users.md b/tests/testcases/Group1-user-management/1-13-admin-search-users.md new file mode 100644 index 000000000..ede5d73fa --- /dev/null +++ b/tests/testcases/Group1-user-management/1-13-admin-search-users.md @@ -0,0 +1,25 @@ +Test 1-13 - Admin User Search Users. +======= + +# Purpose: + +To verify that Admin user can search users. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database, LDAP or AD. + +# Test Steps: + +1. Log in as the admin user to the UI. +2. The admin user should see a list of users in "Admin Options". +3. Enter different keywords or partial words to see if the user list can be filtered according to the criteria. + +# Expected Outcome: +* As described in steps 1-3. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-14-LDAP-admin-role-general.md b/tests/testcases/Group1-user-management/1-14-LDAP-admin-role-general.md new file mode 100644 index 000000000..8e3c0a4b9 --- /dev/null +++ b/tests/testcases/Group1-user-management/1-14-LDAP-admin-role-general.md @@ -0,0 +1,29 @@ +Test 1-14 - LDAP Mode Admin Role General Functions +======= + +# Purpose: + +To verify that Harbor's UI of a user with system admin role works properly in LDAP mode. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an AD or LDAP server. (auth_mode is set to **ldap_auth** .) +* An Active Directory (AD) or LDAP server has been set up and it has a few users available for testing. + +# Test Steps: + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 1-07. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 1-07. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 1-07. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group1-user-management/1-15-admin-role-create-delete-user.md b/tests/testcases/Group1-user-management/1-15-admin-role-create-delete-user.md new file mode 100644 index 000000000..779055ebb --- /dev/null +++ b/tests/testcases/Group1-user-management/1-15-admin-role-create-delete-user.md @@ -0,0 +1,29 @@ +Test 1-15 - Admin Role User Create, Delete and Recreate a User(DB Mode) +======= + +# Purpose: + +To verify that an admin user can create/delete/recreate a user when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 1-09. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 1-09. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 1-09. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-01-DB-user-create-project.md b/tests/testcases/Group2-image-management/2-01-DB-user-create-project.md new file mode 100644 index 000000000..4673abb90 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-01-DB-user-create-project.md @@ -0,0 +1,42 @@ +Test 2-01 - User Create Project (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can create projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least two non-admin users are in Harbor. + +# Test Steps: + +**NOTE:** +In below test, user A and B are non-admin users. User A, B and project X, Y should be replaced by longer and meaningful names. + +1. Log in to UI as user A (non-admin). +2. Create a new project X with publicity is off (default). +3. Create another new project Y with publicity is on . +4. While keeping user A logging on, in another browswer log in as user B (non-admin). +5. User B checks his/her public projects to see if project Y is listed and project X is not listed. +6. User A changes project X's publicity to on, project Y's publicity to off. +7. User B refreshes his/her public projects to see if project X is listed and project Y is not listed. +8. On a Docker client host, use `docker login <harbor_host>` to log in as user A. +9. User A runs `docker push` to push an image to project X, push an image to project Y. +10. User A checks in the browser that the images had been successfully pushed to project X and Y. +11. On a Docker client host, use `docker login <harbor_host>` to log in as user B. +12. User B runs `docker pull` to an image of project X, and an image of project Y. + +# Expected Outcome: +* Step 5, user B should see project Y is listed and project X is not listed. +* Step 7, user B should see project X is listed and project Y is not listed. +* Step 9,10, images should be pushed to project X and Y successfully and can be viewed from UI. +* Step 11,12, user B can pull the image of project X, cannot pull from project Y. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-02-DB-user-push-multiple-images.md b/tests/testcases/Group2-image-management/2-02-DB-user-push-multiple-images.md new file mode 100644 index 000000000..aeecce5d3 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-02-DB-user-push-multiple-images.md @@ -0,0 +1,38 @@ +Test 2-02 - User Push Multiple Images (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can push multiple images to a project when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user and the user has at least a project as project admin. + +# Test Steps: + +**NOTE:** +In below test, user A is non-admin user. User A and project X should be replaced by longer and meaningful names. + +1. Log in to UI as user A (non-admin). +2. Verify User A has at least a project X with the role of project admin. +3. On a Docker client, log in as User A and run `docker push` to push an image with tag (e.g. nginx:1.5) to project X. +4. Continue to push at least 5 images with different tags, for example, nginx:1.6, nginx:1.7, nginx:1.8, nginx:release . +5. In the UI, go to "My Projects" to see if all images/tags are properly displayed. +6. Checks the detail info of each tag. +7. Enter keyword to search(filter) images/tags of a project. +8. On a Docker client, log in as User A and run `docker pull` to pull images with different tags from project X. + +# Expected Outcome: +* Step 3-5, images can be pushed to project X and can be shown in UI. +* Step 6, image/tag info should be correct. +* Step 7, image/tag should be filtered and results should be matched the search criteria. +* Step 8, images can be pulled from project X. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-03-DB-user-push-multiple-projects.md b/tests/testcases/Group2-image-management/2-03-DB-user-push-multiple-projects.md new file mode 100644 index 000000000..bae5bd1f2 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-03-DB-user-push-multiple-projects.md @@ -0,0 +1,31 @@ +Test 2-03 - User Create Multiple Projects (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can create multiple projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +**NOTE:** +In below test, user A is non-admin user. User A should be replaced by a longer and meaningful name. + +1. Log in to UI as user A (non-admin). +2. Create 16 or more projects so that the pagination control has multiple pages. +3. Go through multiple pages of the list and click on a few projects to see if pagination work properly. +4. Search projects with keywords to see if the list and pagination update accordingly. + +# Expected Outcome: +* As descirbed in step 3-4. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-04-DB-user-view-projects.md b/tests/testcases/Group2-image-management/2-04-DB-user-view-projects.md new file mode 100644 index 000000000..e5d99a309 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-04-DB-user-view-projects.md @@ -0,0 +1,33 @@ +Test 2-04 - User View Projects (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can view projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* There is at least a non-admin user. +* The user has at least 3 private projects. +* The registry has at least 3 public repositories. + +# Test Steps: + +**NOTE:** +In below test, user A is non-admin user. User A should be replaced by a longer and meaningful name. + +1. Log in to UI as user A (non-admin). +2. Create at least 3 projects if he/she has less than 3 projects. +3. Switch a few times between "My Projects" and "Public projects" tab, view listed projects and click to check details of images. +4. Check logs of projects in "My Projects". + +# Expected Outcome: +* Step 3, verify the information listed of projects are correctly displayed, such as creation time and role. +* Step 4, should see logs of the project. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-05-DB-user-delete-image.md b/tests/testcases/Group2-image-management/2-05-DB-user-delete-image.md new file mode 100644 index 000000000..c6f7620e6 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-05-DB-user-delete-image.md @@ -0,0 +1,39 @@ +Test 2-05 - User Delete Images (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can delete images when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +**NOTE:** +In below test, user A is non-admin user. User A and project X, Y should be replaced by longer and meaningful names. + +1. Log in to UI as user A (non-admin). +2. Create a project X so that the user has the project admin role. +3. On a Docker client, log in as User A and run `docker push` to push an image to the project X, e.g. projectX/myimage:v1. +4. Push a second image with different tag to project X, e.g. projectX/myimage:v2 . +5. Push an image with different name to project X, e.g. projectX/newimage:v1 . +6. Run `docker pull` to verify images can be pushed successfully. +7. In UI, delete the three images one by one. +8. On a Docker client, log in as User A and run `docker pull` to pull the three deleted images of project X. +9. In UI, delete project X. +10. Run `docker pull` to pull the three deleted images of the project X. + +# Expected Outcome: +* Step 7, images should be deleted successfully. +* Step 9, project X should be deleted successfully. +* Step 8,10, docker client should report error message. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-06-DB-user-delete-projects.md b/tests/testcases/Group2-image-management/2-06-DB-user-delete-projects.md new file mode 100644 index 000000000..1f8b7c954 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-06-DB-user-delete-projects.md @@ -0,0 +1,51 @@ +Test 2-06 - User Delete Projects (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can delete projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that two(2) Harbor instances are running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +**NOTE:** +* In below test, user A is non-admin user. User A and project X should be replaced by longer and meaningful names. +* Must use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +1. Log in to UI as user A (non-admin). +2. Create a project X so that the user has the project admin role. +3. On a Docker client, log in as User A and run `docker push` to push an image to project X, e.g. projectX/myimage:v1. +4. Push an image with different name to project X, e.g. projectX/newimage:v1 . +5. Run `docker pull` to verify images can be pushed successfully. +6. In UI, delete project X directly. (should fail with errors) +7. While keeping the current user A logged on, in a different browser, log in as admin user. +8. Under "Admin Options", create a replication policy of project X to another Harbor instance. (Do not need to activate this policy.) +9. Switch to the UI of User A, delete all images under project X. +10. In user A's UI, delete project X directly. (should fail with errors) +11. Switch to the UI of admin user, delete the replication policy of project X. +12. In user A's UI, delete project X. +13. In user A's UI, recreate project X, +14. On a Docker client, log in as User A and run `docker push` to push an image to project X, e.g. projectX/anotherimage:v1. The image name should not be the same as those deleted in previous steps. +15. Switch to the UI of admin user, view images under the re-created project X. +16. As an admin user, view the log in dashboard and should see delete and create operaions of project X. + +# Expected Outcome: +* Step 6, deleting project X should fail because there are images under it. +* Step 10, deleting project X should fail because there is an image replication policy under it. +* Step 12, deleting project X should succeed. +* Step 13, re-creation of project X should succeed. +* Step 14, push should succeed. +* Step 15, project X should contain newly pushed image. The old images should not be displayed. +* Step 16, there should be logs for delete and create of project X, notice the project name of the deleted operation is displayed differently. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-11-LDAP-user-create-project .md b/tests/testcases/Group2-image-management/2-11-LDAP-user-create-project .md new file mode 100644 index 000000000..1734efd7a --- /dev/null +++ b/tests/testcases/Group2-image-management/2-11-LDAP-user-create-project .md @@ -0,0 +1,25 @@ +Test 2-11 - User Create Project (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can create projects in (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least two non-admin users are in Harbor. + +# Test Steps: + +Same as Test 2-01 except that users are from LDAP/AD. + +# Expected Outcome: +* Same as Test 2-01. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-12-LDAP-user-push-multiple-images.md b/tests/testcases/Group2-image-management/2-12-LDAP-user-push-multiple-images.md new file mode 100644 index 000000000..a7f845b65 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-12-LDAP-user-push-multiple-images.md @@ -0,0 +1,25 @@ +Test 2-12 - User Push Multiple Images (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can push multiple images to a project in LDAP mode. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user and the user has at least a project as project admin. + +# Test Steps: + +Same as Test 2-02 except that users are from LDAP/AD. + +# Expected Outcome: +* Same as Test 2-02. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-13-LDAP-user-push-multiple-projects.md b/tests/testcases/Group2-image-management/2-13-LDAP-user-push-multiple-projects.md new file mode 100644 index 000000000..268baa422 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-13-LDAP-user-push-multiple-projects.md @@ -0,0 +1,25 @@ +Test 2-13 - User Create Multiple Projects (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can create multiple projects in LDAP mode. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +Same as Test 2-03 except that users are from LDAP/AD. + +# Expected Outcome: +* Same as Test 2-03. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-14-LDAP-user-view-projects.md b/tests/testcases/Group2-image-management/2-14-LDAP-user-view-projects.md new file mode 100644 index 000000000..002c807f4 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-14-LDAP-user-view-projects.md @@ -0,0 +1,26 @@ +Test 2-14 - User View Projects (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can view projects in (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* There is at least a non-admin user. +* The user has at least 3 private projects. +* The registry has at least 3 public repositories. + +# Test Steps: + +Same as Test 2-04 except that users are from LDAP/AD. + +# Expected Outcome: +* Same as Test 2-04. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-15-LDAP-user-delete-image.md b/tests/testcases/Group2-image-management/2-15-LDAP-user-delete-image.md new file mode 100644 index 000000000..3bdd5e962 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-15-LDAP-user-delete-image.md @@ -0,0 +1,25 @@ +Test 2-15 - User Delete Images (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can delete images in (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +Same as Test 2-05 except that users are from LDAP/AD. + +# Expected Outcome: +* Same as Test 2-05. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-16-LDAP-user-delete-projects.md b/tests/testcases/Group2-image-management/2-16-LDAP-user-delete-projects.md new file mode 100644 index 000000000..429f7ca7c --- /dev/null +++ b/tests/testcases/Group2-image-management/2-16-LDAP-user-delete-projects.md @@ -0,0 +1,25 @@ +Test 2-16 - User Delete Projects (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can delete projects in (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that two(2) Harbor instances are running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +Same as Test 2-06 except that users are from LDAP/AD. + +# Expected Outcome: +* Same as Test 2-06. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-21-admin-view-project.md b/tests/testcases/Group2-image-management/2-21-admin-view-project.md new file mode 100644 index 000000000..08bfe4019 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-21-admin-view-project.md @@ -0,0 +1,48 @@ +Test 2-21 - Admin View Projects (DB Mode) +======= + +# Purpose: + +To verify that an admin user can view all projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user is in Harbor. + +# Test Steps: + +**NOTE:** +In below test, user A is non-admin user. User A and project X, Y should be replaced by longer and meaningful names. + +1. Log in to UI as user A (non-admin). +2. Create a new project X with publicity is off (default). +3. Create another new project Y with publicity is on. +4. On a Docker client host, use `docker login <harbor_host>` to log in as user A. +5. Push an image to project X, push another image to project Y. +6. In UI, logs out and log in as admin user. +7. Check "Public Projects" to verify that project Y is listed and project X is not listed. +8. Verify that project "library" is listed. +9. Click project Y and view the newly pushed image in project Y. +10. Click "Users" to view the members of project Y. +11. Check "My projects" to verify that both project X and project Y are listed. +12. Verify that project "library" is listed. +13. Click project X and view the newly pushed image in project X. +14. Click "Users" to view the members of project X. + +# Expected Outcome: +* Step 7, admin should see project Y is listed and project X is not listed. +* Step 8, project library should be listed. +* Step 9, the image pushed by user A should be listed under project Y. +* Step 10, user A should be project admin of project Y. +* Step 11, admin should see project X and Y are both listed. +* Step 12, project library should be listed. +* Step 13, the image pushed by user A should be listed under project X. +* Step 14, user A should be project admin of project X. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-22-admin-search-projects.md b/tests/testcases/Group2-image-management/2-22-admin-search-projects.md new file mode 100644 index 000000000..25be16922 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-22-admin-search-projects.md @@ -0,0 +1,33 @@ +Test 2-22 - Admin User Search Projects (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can search projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +**NOTE:** +In below test, user A is non-admin user. User A should be replaced by a longer and meaningful name. + +1. Log in to UI as user A (non-admin). +2. Create at least 5 projects with different names. +3. Log out and log in again as admin user. +4. Search projects with keywords to see if projects of user A can be matched by criteria. +5. Click on a few projects to toggle publicity on and off. +6. Check in "Public Projects" to verify the projects with publicity on can be listed. + +# Expected Outcome: +* As descirbed in step 4,6. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-23-admin-delete-images.md b/tests/testcases/Group2-image-management/2-23-admin-delete-images.md new file mode 100644 index 000000000..41775d36d --- /dev/null +++ b/tests/testcases/Group2-image-management/2-23-admin-delete-images.md @@ -0,0 +1,37 @@ +Test 2-23 - Admin User Delete Images (DB Mode) +======= + +# Purpose: + +To verify that an admin user can delete images owned by other users when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +**NOTE:** +In below test, user A is non-admin user. User A and project X should be replaced by longer and meaningful names. + +1. Log in to UI as user A (non-admin). +2. Create a project X so that the user has the project admin role. +3. On a Docker client, log in as User A and run `docker push` to push an image to the project X, e.g. projectX/myimage:v1. +4. Push an image with different name to project X, e.g. projectX/newimage:v1 . +5. Run `docker pull` to verify images can be pushed successfully. +6. In UI, log out user A's session. +7. Log in as admin user. +8. Under project X, delete the two images one by one. +9. On a Docker client, log in as User A and run `docker pull` to pull the two deleted images of project X. + +# Expected Outcome: +* Step 8, admin user can delete images of project X. +* Step 9, docker client should report errors. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-24-admin-delete-projects.md b/tests/testcases/Group2-image-management/2-24-admin-delete-projects.md new file mode 100644 index 000000000..5c558a429 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-24-admin-delete-projects.md @@ -0,0 +1,41 @@ +Test 2-24 - Admin User Delete Projects (DB Mode) +======= + +# Purpose: + +To verify that an admin user can delete other user's projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that two(2) Harbor instances are running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least a non-admin user. + +# Test Steps: + +**NOTE:** +* In below test, user A is non-admin user. User A and project X should be replaced by longer and meaningful names. +* Must use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users in different windows(tabs). + +1. Log in to UI as user A (non-admin). +2. Create a project X so that the user has the project admin role. +3. On a Docker client, log in as User A and run `docker push` to push an image to project X, e.g. projectX/myimage:v1. +4. Run `docker pull` to verify images can be pushed successfully. +5. In UI, log out user A. +6. Log in as admin user. +7. Delete project X. (should fail with errors) +8. Delete all images of project X. +9. Delete project X. +10. As an admin user, view the log in dashboard and should see delete operaion of project X and its images. + +# Expected Outcome: +* Step 7, deleting project X should fail because there are images under it. +* Step 8-9, deleting images of project X and then deleting project X should succeed. +* Step 10, there should be logs for delete and create of project X, notice the project name of the deleted operation is displayed differently. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-31-admin-role-view-project.md b/tests/testcases/Group2-image-management/2-31-admin-role-view-project.md new file mode 100644 index 000000000..7f36a5595 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-31-admin-role-view-project.md @@ -0,0 +1,30 @@ +Test 2-31 - Admin Role View Projects (DB Mode) +======= + +# Purpose: + +To verify that a user with system admin role can view all projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least two non-admin users are in Harbor. + +# Test Steps: + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 2-21. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 2-21. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 2-21. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-32-admin-role-search-projects.md b/tests/testcases/Group2-image-management/2-32-admin-role-search-projects.md new file mode 100644 index 000000000..19d75e3b4 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-32-admin-role-search-projects.md @@ -0,0 +1,31 @@ +Test 2-32 - Admin Role User Search Projects (DB Mode) +======= + +# Purpose: + +To verify that a user with system admin role can search projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least two non-admin users are in Harbor. + +# Test Steps: + + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 2-22. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 2-22. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 2-22. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-33-admin-role-delete-images.md b/tests/testcases/Group2-image-management/2-33-admin-role-delete-images.md new file mode 100644 index 000000000..4d26d5caa --- /dev/null +++ b/tests/testcases/Group2-image-management/2-33-admin-role-delete-images.md @@ -0,0 +1,30 @@ +Test 2-33 - Admin Role User Delete Images (DB Mode) +======= + +# Purpose: + +To verify that a user with system admin role can delete images owned by other users when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least tow non-admin users. + +# Test Steps: + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 2-23. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 2-23. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 2-23. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group2-image-management/2-34-admin-role-delete-projects.md b/tests/testcases/Group2-image-management/2-34-admin-role-delete-projects.md new file mode 100644 index 000000000..6ff3f6836 --- /dev/null +++ b/tests/testcases/Group2-image-management/2-34-admin-role-delete-projects.md @@ -0,0 +1,30 @@ +Test 2-34 - Admin User Delete Projects (DB Mode) +======= + +# Purpose: + +To verify that a user with system admin role can delete other user's projects when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that two(2) Harbor instances are running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least two non-admin users. + +# Test Steps: + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 2-24. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 2-24. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 2-24. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-01-DB-user-manage-project-members.md b/tests/testcases/Group3-RBAC/3-01-DB-user-manage-project-members.md new file mode 100644 index 000000000..fce0235cf --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-01-DB-user-manage-project-members.md @@ -0,0 +1,74 @@ +Test 3-01 - Manage Project Members (DB Mode) +======= + +# Purpose: + +To verify that a non system admin user can add members of various roles to a project when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least three non-admin users are in Harbor. + +# Test Steps: + +**NOTE:** +* In below test, user A, B and C are non system admin users. User A, B, C and project X should be replaced by longer and meaningful names. +* MUST use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +1. Log in to UI as user A (non-admin). +2. Create a new project X with publicity is off (default). +3. Verify that user A cannot change his/her own role of project X. +4. On a Docker client host, use `docker login <harbor_host>` to log in as user A. +5. Push an image to project X. +6. On the Docker client host, log out user A and log in as user B. +7. Use `docker pull` to pull the image from project X. (should fail) +8. Use `docker push` to push an image to project X. (should fail) +9. Keeps user A's UI session logging on, in a different browser log in as user B. +10. User B views "My Projects", should not see project X. + +11. In user A's UI, add user B to project X as a guest. +12. In user B's UI, check project X is in "My projects" and view members and images of project X. +13. Verify that user B cannot change role of other members in project X. +14. Verify that user B cannot add a new member to project X. +15. On a Docker client host, user B again pulls the image from project X. +16. user B pushes a new image to project X. (should fail) +17. In user A's UI, update user B to developer role of project X. +18. In user B's UI, check project X is in "My projects" and view members and images of project X. +19. Verify that user B cannot change role of other members in project X. +20. Verify that user B cannot add a new member to project X. + +21. On a Docker client host, user B pushes a new image to project X. +22. In user A's UI, update user B to project admin role of project X. +23. In user B's UI, check project X is in "My projects" and view members and images of project X. +24. Verify that user B can add a new member user C to project X. +25. Verify that user B can change role of user C in project X. +26. On a Docker client host, user B pushes a new image to project X. +27. In user A's UI, remove user B from project X. +28. On a Docker client host, user B pulls an image of project X. (should fail) +29. On a Docker client host, log out user B and log in as user C. +30. User C pulls an image of project X. + +# Expected Outcome: + +* Step 3, user B cannot change his/her own role, and cannot remove himself/herself from project X. +* Step 7, user B should fail to pull image from project X. +* Step 8, user B should fail to push image to project X. +* Step 9, make sure to keep A's session while using another browser to log in as user B. +* Step 10, user B should not see project X. +* Step 12-14, as described. +* Step 15, user B can pull the image of project X, +* Step 16, user B cannot push images to project X. +* Step 18-20, as described. +* Step 21, user B pushes an image successfully. +* Step 23-25, as described. +* Step 28, user B's pulling should fail +* Step 30, user C's pulling should succeed. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-02-DB-user-manage-project-publicity.md b/tests/testcases/Group3-RBAC/3-02-DB-user-manage-project-publicity.md new file mode 100644 index 000000000..40fd749ed --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-02-DB-user-manage-project-publicity.md @@ -0,0 +1,49 @@ +Test 3-02 - Manage Project Publicity (DB Mode) +======= + +# Purpose: + +To verify that a non system admin user can change project publicity of a project when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least three non-admin users are in Harbor. + +# Test Steps: + +**NOTE:** +* In below test, user A, B are non system admin users. User A, B and project X should be replaced by longer and meaningful names. +* MUST use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +1. Log in to UI as user A (non-admin). +2. Create a new project X with publicity is on. +3. On a Docker client host, use `docker login <harbor_host>` to log in as user A. +4. Push an image to project X. +5. On the Docker client host, log out user A and log in as user B. +6. User B pulls an image from project X. +7. Keeps user A's UI session logging on, in a different browser log in as user B. +8. User B views "My Projects", should not see project X. +9. User B views "Public Projects", should see project X. +10. In user A's UI, change publicity of project X to off. +11. In user B's UI, user B checks "My Projects" and "Public Projects", should not see project X in both places. +12. On the Docker client host, user B pulls an image from project X. (should fail) +13. In user A's UI, change publicity of project X to on again. +14. User B views "My Projects", should not see project X. +15. User B views "Public Projects", should see project X. + + +# Expected Outcome: +* Step 6, user B's pulling image from project X should succeed. +* Step 7, make sure to keep A's session while using another browser to log in user B. +* Step 8-11, as described. +* Step 12, user B's pulling should fail. +* Step 13-15, as described. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-03-DB-user-search-project-members.md b/tests/testcases/Group3-RBAC/3-03-DB-user-search-project-members.md new file mode 100644 index 000000000..8c8bce185 --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-03-DB-user-search-project-members.md @@ -0,0 +1,40 @@ +Test 3-03 - Search Project Members (DB Mode) +======= + +# Purpose: + +To verify that a non system admin user can search members of a project when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least five(5) non-admin users are in Harbor. + +# Test Steps: + +**NOTE:** +* In below test, user A, B are non system admin users. User A, B and project X should be replaced by longer and meaningful names. +* MUST use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +1. Log in to UI as user A (non-admin). +2. Create a new project X. +3. Add user B as a project admin role of project X. +4. Add 3 more members to project X with various roles, such as developer, guest. +5. Keeps user A's UI session logging on, in a different browser log in as user B. +6. In user B's UI, search members of project X using different criteria (keywords). +7. In user A's UI, change user B's role to developer of project X. +8. In user B's UI, search members of project X using different criteria. +9. In user A's UI, change user B's role to guest of project X. +10. In user B's UI, search members of project X using different criteria. + + +# Expected Outcome: +* Step 6,8,10, user B should see results based on search keywords. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-04-LDAP-user-manage-project-members.md b/tests/testcases/Group3-RBAC/3-04-LDAP-user-manage-project-members.md new file mode 100644 index 000000000..e20a507a0 --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-04-LDAP-user-manage-project-members.md @@ -0,0 +1,31 @@ +Test 3-04 - Manage Project Members (LDAP Mode) +======= + +# Purpose: + +To verify that a non system admin user can add members of various roles to a project in LDAP mode. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least three non-admin users are in Harbor. + +# Test Steps: + +**NOTE:** +* In below test, user A, B and C are non system admin users. User A, B, C and project X should be replaced by longer and meaningful names. +* MUST use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +Same as Test 3-01. + +# Expected Outcome: + +Same as Test 3-01. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-05-LDAP-user-manage-project-publicity.md b/tests/testcases/Group3-RBAC/3-05-LDAP-user-manage-project-publicity.md new file mode 100644 index 000000000..9be70ca80 --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-05-LDAP-user-manage-project-publicity.md @@ -0,0 +1,31 @@ +Test 3-05 - Manage Project Publicity (LDAP Mode) +======= + +# Purpose: + +To verify that a non system admin user can change project publicity of a project in LDAP mode. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least three non-admin users are in Harbor. + +# Test Steps: + +**NOTE:** +* In below test, user A, B are non system admin users. User A, B and project X should be replaced by longer and meaningful names. +* MUST use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +Same as Test 3-02. + + +# Expected Outcome: +Same as Test 3-02 + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-06-LDAP-user-search-project-members.md b/tests/testcases/Group3-RBAC/3-06-LDAP-user-search-project-members.md new file mode 100644 index 000000000..bed13514a --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-06-LDAP-user-search-project-members.md @@ -0,0 +1,26 @@ +Test 3-06 - Search Project Members (LDAP Mode) +======= + +# Purpose: + +To verify that a non system admin user can search members of a project in LDAP mode. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). +* At least five(5) non-admin users are in Harbor. + +# Test Steps: + +Same as Test 3-03. + + +# Expected Outcome: +* Same as Test 3-03. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-11-DB-admin-user-manage-project-members.md b/tests/testcases/Group3-RBAC/3-11-DB-admin-user-manage-project-members.md new file mode 100644 index 000000000..b1a86a9b6 --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-11-DB-admin-user-manage-project-members.md @@ -0,0 +1,78 @@ +Test 3-11 - Admin Manages Project Members (DB Mode) +======= + +# Purpose: + +To verify that an admin user can add members of various roles to a project. Users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least three non-admin users are in Harbor. +* At least one project that admin user is not a member of. + +# Test Steps: + +**NOTE:** +* In below test, user A, B and C are non system admin users. User A, B, C and project X should be replaced by longer and meaningful names. +* MUST use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +1. Log in to UI as admin user. +2. Look for an existing project X that admin is not a member of. +3. Add user A to project X as project admin. +4. Add user B to project X as developer. +5. Add user C to project X as guest. +6. On a Docker client host, use `docker login <harbor_host>` to log in as admin. +7. Use `docker push` to push an image to project X. +8. Use `docker pull` to pull the image from project X. +9. On a Docker client host, log out admin and log in as user A. +10. Use `docker push` to push an image to project X. +11. Use `docker pull` to pull the image from project X. + +12. On a Docker client host, log out user A and log in as user B. +13. Use `docker push` to push an image to project X. +14. Use `docker pull` to pull the image from project X. +15. On a Docker client host, log out user B and log in as user C. +16. Use `docker pull` to pull the image from project X. +17. Use `docker push` to push an image to project X. (should fail) + +18. Keeps admin's UI session logging on, in a different browser log in as user C. +19. In user C's UI, verify his/her role is guest of project X. +20. In admin's UI, change user C's role to developer of project X. +21. In user C's UI, verify his/her role is developer of project X. +22. On a Docker client host, log in as user C. +23. Use `docker pull` to pull the image from project X. +24. Use `docker push` to push an image to project X. +25. In admin's UI, change user C's role to project admin of project X. +26. In user C's UI, verify his/her role is project admin of project X. + +27. In admin's UI, remove user C's from project X. +28. Set project X's publicity to on. +29. On a Docker client host, log in as user C. +30. Use `docker pull` to pull the image from project X. +31. Use `docker push` to push an image to project X. (should fail) +32. In admin's UI, remove user C's from project X. +33. Set project X's publicity to off. +34. On a Docker client host, log in as user C. +35. Use `docker pull` to pull the image from project X. (should fail) +36. Use `docker push` to push an image to project X. (should fail) + + +# Expected Outcome: + +* Step 7,8,10,11,16 should succeed. +* Step 17 should report errors. +* Step 19,21, as described. +* Step 23,24 should succeed. +* Step 26 as described. +* Step 30 should succeed. +* Step 31 should fail. +* Step 35-36 should fail. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-12-DB-admin-user-search-project-members.md b/tests/testcases/Group3-RBAC/3-12-DB-admin-user-search-project-members.md new file mode 100644 index 000000000..df05ae4b4 --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-12-DB-admin-user-search-project-members.md @@ -0,0 +1,34 @@ +Test 3-12 - Admin Search Project Members (DB Mode) +======= + +# Purpose: + +To verify that an admin user can search members of a project when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least five(5) non-admin users are in Harbor. +* At least 5 members in a project that admin user is not a member of. + +# Test Steps: + +**NOTE:** +* In below test, user A, B are non system admin users. User A, B and project X should be replaced by longer and meaningful names. +* MUST use two kinds of browsers at the same time to ensure independent sessions. For example, use Chrome and Firefox, or Chrome and Safari. +* DO NOT use the same browser to log in two users at the same time in different windows(tabs). + +1. Log in to UI as admin. +2. Look for an existing project X that admin is not a member of, project X should have at least 5 members. +3. Search members of project X using different criteria (keywords). + + +# Expected Outcome: +* Step 3, admin should see results based on search keywords. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-21-DB-admin-role-user-manage-project-members.md b/tests/testcases/Group3-RBAC/3-21-DB-admin-role-user-manage-project-members.md new file mode 100644 index 000000000..eee91e11a --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-21-DB-admin-role-user-manage-project-members.md @@ -0,0 +1,31 @@ +Test 3-21 - Admin Role User Manages Project Members (DB Mode) +======= + +# Purpose: + +To verify that a user with system admin role can add members of various roles to a project. Users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least three non-admin users are in Harbor. +* At least one project that admin user is not a member of. + +# Test Steps: + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 3-11. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 3-11. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 3-11. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group3-RBAC/3-22-DB-admin-role-user-search-project-members.md b/tests/testcases/Group3-RBAC/3-22-DB-admin-role-user-search-project-members.md new file mode 100644 index 000000000..43e930518 --- /dev/null +++ b/tests/testcases/Group3-RBAC/3-22-DB-admin-role-user-search-project-members.md @@ -0,0 +1,32 @@ +Test 3-22 - Admin Role User Search Project Members (DB Mode) +======= + +# Purpose: + +To verify that a user with system admin role can search members of a project when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). +* At least five(5) non-admin users are in Harbor. +* At least 5 members in a project that admin user is not a member of. + + +# Test Steps: + +**NOTE:** The below non-admin user M should NOT be the same as the non-admin user in Test 3-12. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 3-12. + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Same as Test 3-12. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group4-logging/4-01-DB-user-view-logs.md b/tests/testcases/Group4-logging/4-01-DB-user-view-logs.md new file mode 100644 index 000000000..935a91081 --- /dev/null +++ b/tests/testcases/Group4-logging/4-01-DB-user-view-logs.md @@ -0,0 +1,39 @@ +Test 4-01 - User Views Logs (DB Mode) +======= + +# Purpose: + +To verify that a non-admin user can views logs when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +1. On a Docker client host, use `docker login <harbor_host>` command to log in as a non-admin user. +2. Run some `docker push` and `docker pull` commands to push images to the registry and pull from the registry. +3. Log in to the UI as the non-admin user. +4. Delete a few images from the project. +5. View the logs of the project. +6. Try below search criteria to see if the search result is correct: + +* push only +* pull only +* pull and push +* delete only +* all +* push and delete +* different date ranges +* date range and push + +# Expected Outcome: +* All operations in Step 2 & 4 should be logged. +* Logs can be viewed in Step 5, check if the time and operations are correct. +* Logs can be filtered in Step 6. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group4-logging/4-02-LDAP-user-view-logs.md b/tests/testcases/Group4-logging/4-02-LDAP-user-view-logs.md new file mode 100644 index 000000000..99d5471ae --- /dev/null +++ b/tests/testcases/Group4-logging/4-02-LDAP-user-view-logs.md @@ -0,0 +1,39 @@ +Test 4-02 - User Views Logs (LDAP Mode) +======= + +# Purpose: + +To verify that a non-admin user can views logs when users are managed externally by LDAP or AD (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +1. On a Docker client host, use `docker login <harbor_host>` command to log in as a non-admin user. +2. Run some `docker push` and `docker pull` commands to push images to the registry and pull from the registry. +3. Log in to the UI as the non-admin user. +4. Delete a few images from the project. +5. View the logs of the project. +6. Try below search criteria to see if the search result is correct: + +* push only +* pull only +* pull and push +* delete only +* all +* push and delete +* different date ranges +* date range and push + +# Expected Outcome: +* All operations in Step 2 & 4 should be logged. +* Logs can be viewed in Step 5, check if the time and operations are correct. +* Logs can be filtered in Step 6. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group4-logging/4-03-DB-admin-view-logs.md b/tests/testcases/Group4-logging/4-03-DB-admin-view-logs.md new file mode 100644 index 000000000..6aff89aa9 --- /dev/null +++ b/tests/testcases/Group4-logging/4-03-DB-admin-view-logs.md @@ -0,0 +1,40 @@ +Test 4-03 - Admin Views Logs (DB Mode) +======= + +# Purpose: + +To verify that an admin user can views logs when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +1. On a Docker client host, use `docker login <harbor_host>` command to log in as a non-admin user. +2. Run some `docker push` and `docker pull` commands to push images to the registry and pull from the registry. +3. Log in to the UI as the non-admin user. +4. Delete a few images from the project. +5. Log out non-admin user and log in to the UI as the admin user. +6. View the logs of the project of the non-admin user. +7. Try below search criteria to see if the search result is correct: + +* push only +* pull only +* pull and push +* delete only +* all +* push and delete +* different date ranges +* date range and push + +# Expected Outcome: +* All operations of non-admin users in Step 2 & 4 should be logged. +* Logs can be viewed in Step 6, check if the time and operations are correct. +* Logs can be filtered in Step 7. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group4-logging/4-04-DB-admin-role-view-logs.md b/tests/testcases/Group4-logging/4-04-DB-admin-role-view-logs.md new file mode 100644 index 000000000..e758c3a7d --- /dev/null +++ b/tests/testcases/Group4-logging/4-04-DB-admin-role-view-logs.md @@ -0,0 +1,30 @@ +Test 4-04 - User with Admin Role Views Logs (DB Mode) +======= + +# Purpose: + +To verify that a user with system admin role can views logs when users are managed locally by Harbor (DB mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against a local database. ( auth_mode is set to **db_auth** .) The user data is stored in a local database. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +**NOTE:** The below non-admin user A should NOT be the same as the non-admin user in Test 4-03. + +1. Assign an non-admin user A with system admin role. +2. Repeat all steps in Test 4-03. + + +# Expected Outcome: + +* A user with admin role can perform all operations the same as the admin user. +* Outcome should be the same as Test 4-03. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group4-logging/4-05-LDAP-admin-role-view-logs.md b/tests/testcases/Group4-logging/4-05-LDAP-admin-role-view-logs.md new file mode 100644 index 000000000..104559f00 --- /dev/null +++ b/tests/testcases/Group4-logging/4-05-LDAP-admin-role-view-logs.md @@ -0,0 +1,30 @@ +Test 4-05 - User with Admin Role Views Logs (LDAP Mode) +======= + +# Purpose: + +To verify that a user with system admin role can views logs when users are managed externally by LDAP or AD (LDAP mode). + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* Harbor is set to authenticate against an LDAP or AD server. ( auth_mode is set to **ldap_auth** .) The user data is stored in an LDAP or AD server. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +**NOTE:** The below non-admin user A should NOT be the same as the non-admin user in Test 4-03. + +1. Assign an non-admin user M with system admin role and act as an admin user. +2. Repeat all steps in Test 4-03. + + +# Expected Outcome: + +* A user with system admin role can perform all operations the same as the admin user. +* Outcome should be the same as Test 4-03. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group5-OVA-install-config/5-01-OVA-networking.md b/tests/testcases/Group5-OVA-install-config/5-01-OVA-networking.md new file mode 100644 index 000000000..8557e2db4 --- /dev/null +++ b/tests/testcases/Group5-OVA-install-config/5-01-OVA-networking.md @@ -0,0 +1,39 @@ +Test 5-01 - OVA Networking Settings During Deployment +======= + +# Purpose: + +To verify that an OVA version of Harbor can be configured to obtain IP address from DHCP or static IP. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an OVA binary of Harbor. +* A vCenter, at least an ESX host, and a network that supports DHCP. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +1. From vSphere Web Client, import Harbor's OVA onto an ESX host. +2. In the deployment wizard, enter different passwords of Linux root user, Harbor admin user and MySQL root user. +3. Leave the networking settings blank. +4. Power on the imported OVA. +5. Wait a few minutes for the VM's booting and its IP address comes up in vCenter. (may need to refresh in Web Client) +6. Open a browser and enter http://VM_IP_address. +7. Log in as admin user of Harbor. +8. Create a new project. +9. On a Docker client host, use `docker login <harbor_host>` command to log in as the admin user. +10. Run some `docker push` and `docker pull` commands to push images to the project and pull from the project. +11. On vSphere, open the console of Harbor's VM, log in as root user using the password entered during deployment. +12. Check the network IP address of the VM. +13. From vSphere Web Client, import a new Harbor's OVA onto an ESX host. +14. Repeat Step 2-12, except in Step 3, enter static network settings of the OVA, such as static IP, DNS, gateway, hostname. + + +# Expected Outcome: +* In Step 1-11, everything should work without errors. The passwords entered during deployment should work in Step 7, 9 and 11. +* In Step 14, basically is to test if static networking works for the OVA. The outcome should be the same as Step 1-12, except the networking settings are from the deployment wizard. +Verify the IP address, hostname, DNS are correctly set inside the VM. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group5-OVA-install-config/5-02-OVA-reboot.md b/tests/testcases/Group5-OVA-install-config/5-02-OVA-reboot.md new file mode 100644 index 000000000..747609a4a --- /dev/null +++ b/tests/testcases/Group5-OVA-install-config/5-02-OVA-reboot.md @@ -0,0 +1,43 @@ +Test 5-02 - OVA Reboot +======= + +# Purpose: + +To verify that an OVA version of Harbor can be rebooted. Its configuration remains unchanged and should work the same way as before a reboot. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an OVA binary of Harbor. +* A vCenter, at least an ESX host, and a network that supports DHCP. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +1. From vSphere Web Client, import Harbor's OVA onto an ESX host. +2. In the deployment wizard, enter different passwords of Linux root user, Harbor admin user and MySQL root user. +3. Leave the networking settings blank. +4. Configure email settings. (can be mail server that does not exist ) +5. Power on the imported OVA. +6. Wait a few minutes for the VM's booting and its IP address comes up in vCenter. (may need to refresh in Web Client) +7. Open a browser and enter http://VM_IP_address. +8. Log in as admin user of Harbor. +9. Create a new project. +10. On a Docker client host, use `docker login <harbor_host>` command to log in as the admin user. +11. Run some `docker push` and `docker pull` commands to push images to the project and pull from the project. +12. On vSphere, open the console of Harbor's VM, log in as root user using the password entered during deployment. +13. On vCenter Web Client, reboot the VM. (soft reboot) +14. After the VM starts up, repeat Step 7-12, should work the same. +15. Power off the VM, and the power it on. (hard reboot) +16. After the VM starts up, repeat Step 7-12, should work the same. +17. On vSphere, open the console of Harbor's VM, log in as root user, type `ovfenv` command to verify environment variables are the same as those entered during deployment. + + +# Expected Outcome: +* In Step 1-12, everything should work without errors. The passwords entered during deployment should work in Step 7, 9 and 11. +* In Step 14, 16, the VM should work the same before the reboot or power-off. +* Step 17, environment variables should remain unchanged as those entered during deployment. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group5-OVA-install-config/5-03-OVA-garbage-collection.md b/tests/testcases/Group5-OVA-install-config/5-03-OVA-garbage-collection.md new file mode 100644 index 000000000..94e0d27c6 --- /dev/null +++ b/tests/testcases/Group5-OVA-install-config/5-03-OVA-garbage-collection.md @@ -0,0 +1,40 @@ +Test 5-03 - OVA Garbage Collection +======= + +# Purpose: + +To verify that an OVA version of Harbor can perform garbage collection to release unused space of deleted images. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an OVA binary of Harbor. +* A vCenter, at least an ESX host, and a network that supports DHCP. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: +1. Deploy an OVA version of Harbor, with "Garbage Collection" set to false. +2. Create a project in Harbor. +3. On a Docker client host, use `docker login <harbor_host>` command to log in as the admin user. +4. Run some `docker push` to push some images to the project. The size of the images should be at least 500MB. +5. In Harbor's UI, delete the newly pushed images. +6. On vSphere, open the console of Harbor's VM, log in as root user, type `df -h /data` command to get the space usage of the /data volume. Take a note of the **Used** space. +7. Power off the VM. +8. Right click on the VM and select "Edit Settings". +9. Set "Garbage Collection" to true. +10. Power on the VM. +11. Wait for a while until Harbor service is available (check by a browser) +12. On vSphere, open the console of Harbor's VM, log in as root user, type `df -h /data` command to get the space usage of the /data volume and compare with previous number. +13. Check the log file of garbage collection under /data to see if there was any error. +14. Repeat Step 3-7, to create some deleted images. +15. Repeat Step 10-13, to see the garbage collection works on the second reboot. + + +# Expected Outcome: +* Step 12, the used space should be reduced. The space of deleted images should be recycled. +* Step 13, log file should contain no errors. +* Step 15, verify garbage collection works for the second time. + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group5-OVA-install-config/5-04-OVA-HTTPS.md b/tests/testcases/Group5-OVA-install-config/5-04-OVA-HTTPS.md new file mode 100644 index 000000000..831d40ff4 --- /dev/null +++ b/tests/testcases/Group5-OVA-install-config/5-04-OVA-HTTPS.md @@ -0,0 +1,25 @@ +Test 5-04 - OVA Uses HTTPS +======= + +# Purpose: + +To verify that an OVA version of Harbor can set up for HTTPS. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an OVA binary of Harbor. +* A vCenter, at least an ESX host, and a network that supports DHCP. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +TBD + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group5-OVA-install-config/5-05-OVA-LDAP-integration.md b/tests/testcases/Group5-OVA-install-config/5-05-OVA-LDAP-integration.md new file mode 100644 index 000000000..f2a1b91eb --- /dev/null +++ b/tests/testcases/Group5-OVA-install-config/5-05-OVA-LDAP-integration.md @@ -0,0 +1,25 @@ +Test 5-05 - OVA LDAP integration +======= + +# Purpose: + +To verify that an OVA version of Harbor can work with AD and LDAP. The LDAP/AD configuration can be updated after power-off/on. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an OVA binary of Harbor. +* A vCenter, at least an ESX host, and a network that supports DHCP. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +TBD + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group5-OVA-install-config/5-06-OVA-Configuration.md b/tests/testcases/Group5-OVA-install-config/5-06-OVA-Configuration.md new file mode 100644 index 000000000..4ab6a5d8c --- /dev/null +++ b/tests/testcases/Group5-OVA-install-config/5-06-OVA-Configuration.md @@ -0,0 +1,28 @@ +Test 5-06 - OVA Configuration +======= + +# Purpose: + +To verify that the settings of an OVA version of Harbor can be configured and re-configured after power-on/off. + +# References: +User guide, Installation guide of Harbor OVA version. + +# Environment: +* This test requires an OVA binary of Harbor. +* A vCenter, at least an ESX host, and a network that supports DHCP. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +To-Do: +1. verify all setttings work at initial import +2. verify all settings work after a hard power off followed by a power on +3. Verify all settings can be changed as needed. (except networks, root password, Harbor password , mysql password) + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group6-OVA-operation-upgrade-patching/6-01-OVA-upgrade.md b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-01-OVA-upgrade.md new file mode 100644 index 000000000..2242504bc --- /dev/null +++ b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-01-OVA-upgrade.md @@ -0,0 +1,29 @@ +Test 6-01 - OVA Upgrade +======= + +# Purpose: + +To verify that an OVA version of Harbor can be upgraded when no changes to the number of OVA params. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an upgrade binary of Harbor. +* A running version of older version of Harbor OVA version. +* A vCenter, at least an ESX host. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +TBD + +1. Normal flow should include rollback steps. + + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group6-OVA-operation-upgrade-patching/6-02-OVA-upgrade-advanced.md b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-02-OVA-upgrade-advanced.md new file mode 100644 index 000000000..8b21c9843 --- /dev/null +++ b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-02-OVA-upgrade-advanced.md @@ -0,0 +1,28 @@ +Test 6-02 - OVA Advanced Upgrade +======= + +# Purpose: + +To verify that an OVA version of Harbor can be upgraded when the number of OVA params increases. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an upgrade binary of Harbor. +* A running version of older version of Harbor OVA version. +* A vCenter, at least an ESX host. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +When the number of params of OVA changes, the old OVA needs to have special handling of upgrading. + +TBD + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group6-OVA-operation-upgrade-patching/6-03-OVA-patching.md b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-03-OVA-patching.md new file mode 100644 index 000000000..650b7799e --- /dev/null +++ b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-03-OVA-patching.md @@ -0,0 +1,26 @@ +Test 6-03 - OVA Patching +======= + +# Purpose: + +To verify that an OVA version of Harbor can be patching. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: +* This test requires an upgrade binary of Harbor. +* A running version of older version of Harbor OVA version. +* A vCenter, at least an ESX host. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +TBD + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group6-OVA-operation-upgrade-patching/6-04-OVA-increase-volume.md b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-04-OVA-increase-volume.md new file mode 100644 index 000000000..97d3df0bc --- /dev/null +++ b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-04-OVA-increase-volume.md @@ -0,0 +1,31 @@ +Test 6-04 - OVA Increase Volume Space +======= + +# Purpose: + +To verify that an OVA version of Harbor can be upgraded the volume to larger space. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: + +* A running version of Harbor OVA version. +* A vCenter, at least an ESX host. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +To-Do: + +1. Test attached 1 disk +2. Test attached 2nd disk +3. test attached 3rd disk +4. reboot or not reboot? + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group6-OVA-operation-upgrade-patching/6-05-OVA-collect-logs.md b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-05-OVA-collect-logs.md new file mode 100644 index 000000000..082817739 --- /dev/null +++ b/tests/testcases/Group6-OVA-operation-upgrade-patching/6-05-OVA-collect-logs.md @@ -0,0 +1,27 @@ +Test 6-05 - OVA Collect Logs +======= + +# Purpose: + +To verify that the logs of an OVA version of Harbor can be retrieved. + +# References: +User guide, installation guide of Harbor OVA version. + +# Environment: + +* A running version of Harbor OVA version. +* A vCenter, at least an ESX host. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +TBD + + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tests/testcases/Group7-Replication/7-01-DB-user-registration.md b/tests/testcases/Group7-Replication/7-01-DB-user-registration.md new file mode 100644 index 000000000..25d4dcc0d --- /dev/null +++ b/tests/testcases/Group7-Replication/7-01-DB-user-registration.md @@ -0,0 +1,24 @@ +Test 7-01 - Create Replication Policy +======= + +# Purpose: + +To verify that an admin user can create a replication policy. + +# References: +User guide + +# Environment: +* This test requires that a Harbor instance is running and available. +* A linux host with Docker CLI installed (Docker client). + +# Test Steps: + +TBD + +# Expected Outcome: + +TBD + +# Possible Problems: +None \ No newline at end of file diff --git a/tools/ova/deps/docker-compose-1.7.1/install.sh b/tools/ova/deps/docker-compose-1.7.1/install.sh new file mode 100755 index 000000000..810b24630 --- /dev/null +++ b/tools/ova/deps/docker-compose-1.7.1/install.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo "docker-compose version 1.7.1" +cd "$( dirname "${BASH_SOURCE[0]}" )" +cp ./docker-compose-Linux-x86_64 /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose + diff --git a/tools/ova/script/app_post_install.sh b/tools/ova/script/app_post_install.sh new file mode 100755 index 000000000..aeda8239a --- /dev/null +++ b/tools/ova/script/app_post_install.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +tdnf install -y docker +systemctl enable docker.service + +mkdir -p /var/log/harbor + +echo "Downloading harbor..." +wget -O /ova.tar.gz http://10.117.5.62/ISV/appliancePackages/ova.tar.gz + +echo "Downloading notice file..." +wget -O /NOTICE_Harbor_0.4.1_Beta.txt http://10.117.5.62/ISV/appliancePackages/NOTICE_Harbor_0.4.1_Beta.txt + +echo "Downloading license file..." +wget -O /LICENSE_Harbor_0.4.1_Beta_100216.txt http://10.117.5.62/ISV/appliancePackages/LICENSE_Harbor_0.4.1_Beta_100216.txt \ No newline at end of file diff --git a/tools/ova/script/common.sh b/tools/ova/script/common.sh new file mode 100755 index 000000000..88b268a78 --- /dev/null +++ b/tools/ova/script/common.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +#Shut down Harbor +function down { + base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + docker-compose -f $base_dir/../harbor/docker-compose*.yml down +} + +#Start Harbor +function up { + base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + $base_dir/start_harbor.sh +} + +#Configure Harbor +function configure { + base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + $base_dir/config.sh +} + +#Garbage collectoin +function gc { + echo "======================= $(date)=====================" + + #the registry image + image=$1 + + base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + + docker run --name gc --rm --volume /data/registry:/storage \ + --volume $base_dir/../harbor/common/config/registry/:/etc/registry/ \ + $image garbage-collect /etc/registry/config.yml + + echo "====================================================" +} + +#Add rules to iptables +function addIptableRules { + iptables -A INPUT -p tcp --dport 5480 -j ACCEPT + iptables -A INPUT -p tcp --dport 5488 -j ACCEPT + iptables -A INPUT -p tcp --dport 5489 -j ACCEPT +} + +#Install docker-compose +function installDockerCompose { + base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + $base_dir/../deps/docker-compose-1.7.1/install.sh +} + +#Load images +function load { + basedir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + docker load -i $basedir/../harbor/harbor*.tgz +} \ No newline at end of file diff --git a/tools/ova/script/config.sh b/tools/ova/script/config.sh new file mode 100755 index 000000000..20df3f278 --- /dev/null +++ b/tools/ova/script/config.sh @@ -0,0 +1,89 @@ +#!/bin/bash +set -e + +attrs=( + harbor_admin_password + auth_mode + ldap_url + ldap_searchdn + ldap_search_pwd + ldap_basedn + ldap_uid + email_server + email_server_port + email_username + email_password + email_from + email_ssl + db_password + verify_remote_cert + self_registration + ) + +base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )" + +#The location of harbor.cfg +cfg=$base_dir/harbor/harbor.cfg + +#Format cert and key files +function format { + file=$1 + head=$(sed -rn 's/(-+[A-Za-z ]*-+)([^-]*)(-+[A-Za-z ]*-+)/\1/p' $file) + body=$(sed -rn 's/(-+[A-Za-z ]*-+)([^-]*)(-+[A-Za-z ]*-+)/\2/p' $file) + tail=$(sed -rn 's/(-+[A-Za-z ]*-+)([^-]*)(-+[A-Za-z ]*-+)/\3/p' $file) + echo $head > $file + echo $body | sed 's/\s\+/\n/g' >> $file + echo $tail >> $file +} + +#Modify hostname +ip=$(ip addr show eth0|grep "inet "|tr -s ' '|cut -d ' ' -f 3|cut -d '/' -f 1) +if [ -n "$ip" ] +then + echo "Read IP address: [ IP - $ip ]" + sed -i -r s/"hostname = .*"/"hostname = $ip"/ $cfg +else + echo "Failed to get the IP address" + exit 1 +fi + +#Handle http/https +protocal=http +echo "Read attribute using ovfenv: [ ssl_cert ]" +ssl_cert=$(ovfenv -k ssl_cert) +echo "Read attribute using ovfenv: [ ssl_cert_key ]" +ssl_cert_key=$(ovfenv -k ssl_cert_key) +if [ -n "$ssl_cert" ] && [ -n "$ssl_cert_key" ] +then + echo "ssl_cert and ssl_cert_key are set, using HTTPS protocal" + protocal=https + sed -i -r s%"#?ui_url_protocol = .*"%"ui_url_protocol = $protocal"% $cfg + mkdir -p /path/to + echo $ssl_cert > /path/to/server.crt + format /path/to/server.crt + echo $ssl_cert_key > /path/to/server.key + format /path/to/server.key +else + echo "ssl_cert and ssl_cert_key are not set, using HTTP protocal" +fi + +for attr in "${attrs[@]}" +do + echo "Read attribute using ovfenv: [ $attr ]" + value=$(ovfenv -k $attr) + + #ldap search password and email password can be null + if [ -n "$value" ] || [ "$attr" = "ldap_search_pwd" ] \ + || [ "$attr" = "email_password" ] + then + if [ "$attr" = ldap_search_pwd ] \ + || [ "$attr" = email_password ] \ + || [ "$attr" = db_password ] \ + || [ "$attr" = harbor_admin_password ] + then + bs=$(echo $value | base64) + #value={base64}$bs + fi + sed -i -r s%"#?$attr = .*"%"$attr = $value"% $cfg + fi +done \ No newline at end of file diff --git a/tools/ova/script/firstboot.sh b/tools/ova/script/firstboot.sh new file mode 100755 index 000000000..dde6da80e --- /dev/null +++ b/tools/ova/script/firstboot.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e + +echo "======================= $(date)=====================" + +export PATH=$PATH:/usr/local/bin + +base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source $base_dir/common.sh + +#Reset root password +value=$(ovfenv -k root_pwd) +if [ -n "$value" ] +then + echo "Resetting root password..." + printf "$value\n$value\n" | passwd root +fi + +#echo "Adding rules to iptables..." +#addIptableRules + +echo "Installing docker compose..." +installDockerCompose + +echo "Starting docker service..." +systemctl start docker + +echo "Uncompress Harbor offline instaler tar..." +tar -zxvf $base_dir/../harbor-offline-installer*.tgz -C $base_dir/../ + +echo "Loading images..." +load + +#Configure Harbor +echo "Configuring Harbor..." +chmod 600 $base_dir/../harbor/harbor.cfg +configure + +#Start Harbor +echo "Starting Harbor..." +up + +echo "====================================================" \ No newline at end of file diff --git a/tools/ova/script/start_harbor.sh b/tools/ova/script/start_harbor.sh new file mode 100755 index 000000000..9dc967ede --- /dev/null +++ b/tools/ova/script/start_harbor.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +workdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $workdir/../harbor + +echo "[Step 1]: preparing environment ..." +./prepare + +echo "[Step 2]: starting Harbor ..." +docker-compose -f docker-compose*.yml up -d + +protocol=http +hostname=reg.mydomain.com + +if [[ $(cat ./harbor.cfg) =~ ui_url_protocol[[:blank:]]*=[[:blank:]]*(https?) ]] +then +protocol=${BASH_REMATCH[1]} +fi + +if [[ $(grep 'hostname[[:blank:]]*=' ./harbor.cfg) =~ hostname[[:blank:]]*=[[:blank:]]*(.*) ]] +then +hostname=${BASH_REMATCH[1]} +fi + +echo $" +----Harbor has been installed and started successfully.---- + +Now you should be able to visit the admin portal at ${protocol}://${hostname}. +For more details, please visit https://github.com/vmware/harbor . +" diff --git a/tools/ova/script/subsequentboot.sh b/tools/ova/script/subsequentboot.sh new file mode 100755 index 000000000..ad09d8743 --- /dev/null +++ b/tools/ova/script/subsequentboot.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -e +echo "======================= $(date)=====================" + +export PATH=$PATH:/usr/local/bin + +base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source $base_dir/common.sh + +#echo "Adding rules to iptables..." +#addIptableRules + +#Stop Harbor +echo "Shutting down Harbor..." +down + +#Garbage collection +value=$(ovfenv -k gc_enabled) +if [ "$value" = "true" ] +then + echo "GC enabled, starting garbage collection..." + #If the registry contains no images, the gc will fail. + #So append a true to avoid failure. + gc registry:2.5.0 2>&1 >> /var/log/harbor/gc.log || true +else + echo "GC disabled, skip garbage collection" +fi + +#Configure Harbor +echo "Configuring Harbor..." +configure + +#Start Harbor +echo "Starting Harbor..." +up + +echo "====================================================" \ No newline at end of file