diff --git a/.circleci/config.yml b/.circleci/config.yml index 4408c6e..ac91a2a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,18 +5,56 @@ jobs: - image: circleci/golang:1.12 steps: - checkout + - run: mkdir -p workspace/plugins workspace/coverage - run: name: Build command: | - go build -o bin/vault-plugin-secrets-artifactory . - - run: - name: Create directory for artifacts - command: | - mkdir -p /tmp/artifacts + mkdir -p workspace/plugins + go build -a -ldflags '-linkmode external -extldflags "-static"' -o workspace/plugins/artifactory ./cmd/vault-plugin-secrets-artifactory + - persist_to_workspace: + root: workspace + paths: + - plugins - run: name: Unit tests command: | go test -cover -coverprofile=coverage.out -v ./... - go tool cover -html=coverage.out -o /tmp/artifacts/coverage.html + go tool cover -html=coverage.out -o workspace/coverage/coverage.html + - store_artifacts: + path: workspace + + integration: + docker: + - image: vault:1.2.1 + environment: + ARTIFACTORY_URL: http://localhost:8081/artifactory + VAULT_ADDR: http://127.0.0.1:8200 + VAULT_TOKEN: root-token + VAULT_PLUGIN_DIR: workspace/plugins + VAULT_LOG_DIR: workspace/integration + - image: docker.bintray.io/jfrog/artifactory-oss:latest + environment: + ARTIFACTORY_MASTER_KEY: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + steps: + - checkout + - attach_workspace: + at: workspace + - run: + name: Install dependencies + command: | + apk add --update --no-cache bash curl + - run: + name: Integration tests + command: | + ./integration/test.sh - store_artifacts: - path: /tmp/artifacts + path: workspace + +workflows: + version: 2 + build-and-test: + jobs: + - build + - integration: + requires: + - build diff --git a/integration/artifactory.config.xml b/integration/artifactory.config.xml new file mode 100644 index 0000000..ba3f0cf --- /dev/null +++ b/integration/artifactory.config.xml @@ -0,0 +1,269 @@ + + + false + true + 100 + 11 + dd-MM-yy HH:mm:ss z + + true + 1565407695531 + + + true + false + + supported + + false + 60 + true + + + true + 3 + 60 + + + + + + false + 5 + + + false + false + + + + backup-daily + true + 0 0 2 ? * MON-FRI + 0 + false + + true + false + false + + + backup-weekly + false + 0 0 2 ? * SAT + 336 + false + + true + false + false + + + + false + 0 23 5 * * ? + + + + artifactory-build-info + buildinfo + Build Info repository + **/* + simple-default + V2 + false + false + true + true + 0 + 0 + true + + false + unique + client-checksums + false + 0 + false + false + + + local + generic + **/* + simple-default + V2 + false + false + true + true + 0 + 0 + true + + false + unique + client-checksums + false + 0 + false + false + + + + + + + + + + + + maven-2-default + [orgPath]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + true + [orgPath]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).pom + SNAPSHOT + SNAPSHOT|(?:(?:[0-9]{8}.[0-9]{6})-(?:[0-9]+)) + + + ivy-default + [org]/[module]/[baseRev](-[folderItegRev])/[type]s/[module](-[classifier])-[baseRev](-[fileItegRev]).[ext] + true + [org]/[module]/[baseRev](-[folderItegRev])/[type]s/ivy-[baseRev](-[fileItegRev]).xml + \d{14} + \d{14} + + + gradle-default + [org]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + true + [org]/[module]/ivy-[baseRev](-[fileItegRev]).xml + \d{14} + \d{14} + + + maven-1-default + [org]/[type]s/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + true + [org]/[type]s/[module]-[baseRev](-[fileItegRev]).pom + .+ + .+ + + + nuget-default + [orgPath]/[module]/[module].[baseRev](-[fileItegRev]).nupkg + false + .* + .* + + + npm-default + [orgPath]/[module]/[module]-[baseRev](-[fileItegRev]).tgz + false + .* + .* + + + bower-default + [orgPath]/[module]/[module]-[baseRev](-[fileItegRev]).[ext] + false + .* + .* + + + vcs-default + [orgPath]/[module]/[refs<tags|branches>]/[baseRev]/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext] + false + .* + [a-zA-Z0-9]{40} + + + sbt-default + [org]/[module]/(scala_[scalaVersion<.+>])/(sbt_[sbtVersion<.+>])/[baseRev]/[type]s/[module](-[classifier]).[ext] + true + [org]/[module]/(scala_[scalaVersion<.+>])/(sbt_[sbtVersion<.+>])/[baseRev]/[type]s/ivy.xml + \d{14} + \d{14} + + + simple-default + [orgPath]/[module]/[module]-[baseRev].[ext] + false + .* + .* + + + composer-default + [orgPath]/[module]/[module]-[baseRev](-[fileItegRev]).[ext] + false + .* + .* + + + conan-default + [org]/[module]/[baseRev]/[channel<[^/]+>]/[folderItegRev]/(package/[package_id<[^/]+>]/[fileItegRev]/)?[remainder<(?:.+)>] + false + [^/]+ + [^/]+ + + + puppet-default + [orgPath]/[module]/[orgPath]-[module]-[baseRev].tar.gz + false + .* + .* + + + go-default + [orgPath]/[module]/@v/v[refs].zip + false + .* + .* + + + build-default + [orgPath]/[module](-[fileItegRev]).[ext] + false + .* + .* + + + + + + 0 0 /4 * * ? + + + 0 12 5 * * ? + + + 0 12 0 * * ? + + + false + false + 1024 + 5000 + 10 + + + true + false + 14 + + + false + false + + + + false + + + 720 + + + 31536000 + + diff --git a/integration/artifactory.security.xml b/integration/artifactory.security.xml new file mode 100644 index 0000000..3977906 --- /dev/null +++ b/integration/artifactory.security.xml @@ -0,0 +1,122 @@ + + + + + anonymous + false + true + false + true + false + true + true + internal + false + + + 0 + 0 + false + + + admin + bcrypt$$2a$08$ww3xUFAUw4eOgASvYc3O6e9tNgtNa5G4qHpXvWMpwwcGxiEu2UM.. + true + true + true + true + false + true + true + internal + false + + + 1565410034560 + 172.17.0.1 + 0 + false + + + access-admin + bcrypt$$2a$08$MNZh9xfCw0FYF.hPNKtjAuAaXKOotl.kQcH6QbyW186Ovmzewqq.m + false + true + false + true + false + true + true + internal + false + + + 0 + 0 + false + + + + + readers + A group for read-only users + true + internal + false + + + writers + Able to write to any repository + false + internal + false + + + + + + + readers + true + 1 + + + admin + 1565411163416 + jfrt@01dhwtj1th5w3a1r4aja62177e:72d32801-6fd9-4f98-afa2-18d555f98d08 + + readers + + ** + + + + ANY LOCAL + + + + + + + writers + true + 14 + + + admin + 1565411138117 + jfrt@01dhwtj1th5w3a1r4aja62177e:236c3bd1-1a0a-4a4b-9b5b-d03e6b7f115d + + writers + + ** + + + + ANY LOCAL + + + + + + \ No newline at end of file diff --git a/integration/test.sh b/integration/test.sh new file mode 100755 index 0000000..3492c30 --- /dev/null +++ b/integration/test.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +function fail { + echo $1 >&2 + exit 1 +} + +function retry { + local n=1 + local max=15 + local delay=15 + echo -n "Waiting for $@ to succeed" + while true; do + "$@" >/dev/null 2>&1 && break || { + if [[ $n -lt $max ]]; then + ((n++)) + echo -n "." + sleep $delay; + else + fail " FAILED after $n attempts!" + fi + } + done + echo " OK!" +} + +function cleanup { + kill "${vault_pid}" +} +trap cleanup EXIT + +mkdir -p "${VAULT_LOG_DIR}" +vault server \ + -dev \ + -dev-plugin-dir="${VAULT_PLUGIN_DIR}" \ + -dev-plugin-init \ + -dev-root-token-id="${VAULT_TOKEN}" \ + -log-level=trace \ + > "${VAULT_LOG_DIR}/vault.log" 2>&1 & +vault_pid=$! + +retry vault status + +vault secrets enable \ + -path=artifactory \ + -plugin-name=artifactory \ + plugin + +retry curl -fsS "${ARTIFACTORY_URL}/api/system/ping" + +echo "Artifactory system/configuration:" +curl \ + -fsS \ + -u admin:password \ + -X POST \ + -H "Content-type: application/xml" \ + --data-binary @integration/artifactory.config.xml \ + "${ARTIFACTORY_URL}/api/system/configuration" +echo "" + +echo "Artifactory system/security:" +curl \ + -fsS \ + -u admin:password \ + -X POST \ + -H "Content-type: application/xml" \ + --data-binary @integration/artifactory.security.xml \ + "${ARTIFACTORY_URL}/api/system/security" +echo "" + +vault write artifactory/config \ + address="${ARTIFACTORY_URL}" \ + tls_verify=false \ + username=admin \ + password=password + +vault write artifactory/roles/writer \ + member_of_groups=writers \ + username=vault-user-writer \ + ttl=600 +WRITER_ACCESS_TOKEN="$(vault read -field=access_token artifactory/token/writer)" + +vault write artifactory/roles/reader \ + member_of_groups=readers \ + username=vault-user-reader \ + ttl=600 +READER_ACCESS_TOKEN="$(vault read -field=access_token artifactory/token/reader)" + +echo -n "Verify writer can deploy artifact to local repository: " +if ! curl -fs -o /dev/null -X PUT -T "$0" -H "Authorization: Bearer ${WRITER_ACCESS_TOKEN}" "${ARTIFACTORY_URL}/local/artifact" +then + echo "ERROR: writer role was unable to deploy an artifact" + exit 1 +fi +echo "SUCCESS!" + +echo -n "Verify reader cannot deploy artifact to local repository: " +if curl -fs -X PUT -T "$0" -H "Authorization: Bearer ${READER_ACCESS_TOKEN}" "${ARTIFACTORY_URL}/local/artifact" +then + echo "ERROR: reader should not be able to deploy artifact!" + exit 1 +fi +echo "SUCCESS!" + +echo -n "Verify reader can read artifact: " +if ! curl -fs -o /dev/null -H "Authorization: Bearer ${READER_ACCESS_TOKEN}" "${ARTIFACTORY_URL}/local/artifact" +then + echo "ERROR: reader role was unable to read an artifact" + exit 1 +fi +echo "SUCCESS!" + +echo -n "Verify writer cannot read artifact: " +if curl -fs -o /dev/null -H "Authorization: Bearer ${WRITER_ACCESS_TOKEN}" "${ARTIFACTORY_URL}/local/artifact" +then + echo "ERROR: writer should not be able to read artifact!" + exit 1 +fi +echo "SUCCESS!"