Merge branch '11908-properties-column-json'
authorTom Clegg <tclegg@veritasgenetics.com>
Fri, 8 Dec 2017 18:49:34 +0000 (13:49 -0500)
committerTom Clegg <tclegg@veritasgenetics.com>
Fri, 8 Dec 2017 18:49:34 +0000 (13:49 -0500)
refs #11908

Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg@veritasgenetics.com>

32 files changed:
build/build.list
build/package-build-dockerfiles/centos7/Dockerfile
build/package-build-dockerfiles/debian8/Dockerfile
build/package-build-dockerfiles/debian9/Dockerfile
build/package-build-dockerfiles/ubuntu1404/Dockerfile
build/package-build-dockerfiles/ubuntu1604/Dockerfile
build/run-build-packages-all-targets.sh
build/run-build-packages-one-target.sh
build/run-build-packages.sh
build/run-library.sh
build/run-tests.sh
sdk/cwl/setup.py
sdk/pam/setup.py
sdk/python/setup.py
sdk/python/tests/run_test_server.py
services/api/Gemfile
services/api/Gemfile.lock
services/api/app/controllers/application_controller.rb
services/api/app/controllers/arvados/v1/schema_controller.rb
services/api/app/controllers/user_sessions_controller.rb
services/api/app/middlewares/arvados_api_token.rb
services/api/app/models/api_client_authorization.rb
services/api/config/application.default.yml
services/api/test/functional/arvados/v1/groups_controller_test.rb
services/api/test/integration/remote_user_test.rb [new file with mode: 0644]
services/api/test/integration/users_test.rb
services/api/test/test_helper.rb
services/dockercleaner/setup.py
services/fuse/setup.py
services/nodemanager/setup.py
tools/arvbox/lib/arvbox/docker/service/composer/run-service
tools/crunchstat-summary/setup.py

index 841638048205d20de4a8d05f638e74a0dc8dce8c..84ef784d44c77fde399cf8e2fb53dcd03b2e1ded 100644 (file)
@@ -35,7 +35,7 @@ debian8,debian9,ubuntu1204,ubuntu1404,ubuntu1604,centos7|websocket-client|0.37.0
 ubuntu1204,ubuntu1404|requests|2.4.3|2|python|all
 ubuntu1204,centos7|contextlib2|0.5.4|2|python|all
 ubuntu1204,centos7|isodate|0.5.4|2|python|all
-centos7|daemon|2.1.1|2|python|all
+centos7|python-daemon|2.1.2|1|python|all
 centos7|pbr|0.11.1|2|python|all
 centos7|pyparsing|2.1.10|2|python|all
 centos7|keepalive|0.5|2|python|all
index cf120c911c563b71fada0a1d788f1f6a8b1035a8..c2fdfeee559a66fdd82ac5595c2281da31089c53 100644 (file)
@@ -32,5 +32,7 @@ RUN scl enable python33 "easy_install-3.3 pip" && easy_install-2.7 pip
 # Old versions of setuptools cannot build a schema-salad package.
 RUN pip install --upgrade setuptools
 
+RUN git clone --depth 1 git://git.curoverse.com/arvados.git /tmp/arvados && cd /tmp/arvados/services/api && /usr/local/rvm/bin/rvm-exec default bundle && cd /tmp/arvados/apps/workbench && /usr/local/rvm/bin/rvm-exec default bundle && rm -rf /tmp/arvados
+
 ENV WORKSPACE /arvados
 CMD ["scl", "enable", "python33", "/usr/local/rvm/bin/rvm-exec default bash /jenkins/run-build-packages.sh --target centos7"]
index b9998c6e7bc22d1a9edaec3c0098a30129d2d67f..739244d467e9b420296401888d4d1ba05ac9c9fb 100644 (file)
@@ -29,5 +29,7 @@ RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
 # Old versions of setuptools cannot build a schema-salad package.
 RUN pip install --upgrade setuptools
 
+RUN git clone --depth 1 git://git.curoverse.com/arvados.git /tmp/arvados && cd /tmp/arvados/services/api && /usr/local/rvm/bin/rvm-exec default bundle && cd /tmp/arvados/apps/workbench && /usr/local/rvm/bin/rvm-exec default bundle && rm -rf /tmp/arvados
+
 ENV WORKSPACE /arvados
 CMD ["/usr/local/rvm/bin/rvm-exec", "default", "bash", "/jenkins/run-build-packages.sh", "--target", "debian8"]
index 28ba9a352d7a333d56a3f5a466fe6284c79d64f0..a6e5e88d14514aae04870e0927e62dbc6427b817 100644 (file)
@@ -31,5 +31,7 @@ RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
 # Old versions of setuptools cannot build a schema-salad package.
 RUN pip install --upgrade setuptools
 
+RUN git clone --depth 1 git://git.curoverse.com/arvados.git /tmp/arvados && cd /tmp/arvados/services/api && /usr/local/rvm/bin/rvm-exec default bundle && cd /tmp/arvados/apps/workbench && /usr/local/rvm/bin/rvm-exec default bundle && rm -rf /tmp/arvados
+
 ENV WORKSPACE /arvados
 CMD ["/usr/local/rvm/bin/rvm-exec", "default", "bash", "/jenkins/run-build-packages.sh", "--target", "debian9"]
index 9e77ad3121756ce5be29a5edec07af63b56fb81a..55b9899e839210a92c1fa43ed7d1954ed8f0e94b 100644 (file)
@@ -29,5 +29,7 @@ RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
 # Old versions of setuptools cannot build a schema-salad package.
 RUN pip install --upgrade setuptools
 
+RUN git clone --depth 1 git://git.curoverse.com/arvados.git /tmp/arvados && cd /tmp/arvados/services/api && /usr/local/rvm/bin/rvm-exec default bundle && cd /tmp/arvados/apps/workbench && /usr/local/rvm/bin/rvm-exec default bundle && rm -rf /tmp/arvados
+
 ENV WORKSPACE /arvados
 CMD ["/usr/local/rvm/bin/rvm-exec", "default", "bash", "/jenkins/run-build-packages.sh", "--target", "ubuntu1404"]
index e4673c8ae1f21bb187bd80632cce26efde4c15d1..92aee31b3604cbb235ccdce7c46156da3c1928d1 100644 (file)
@@ -29,5 +29,7 @@ RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
 # Old versions of setuptools cannot build a schema-salad package.
 RUN pip install --upgrade setuptools
 
+RUN git clone --depth 1 git://git.curoverse.com/arvados.git /tmp/arvados && cd /tmp/arvados/services/api && /usr/local/rvm/bin/rvm-exec default bundle && cd /tmp/arvados/apps/workbench && /usr/local/rvm/bin/rvm-exec default bundle && rm -rf /tmp/arvados
+
 ENV WORKSPACE /arvados
 CMD ["/usr/local/rvm/bin/rvm-exec", "default", "bash", "/jenkins/run-build-packages.sh", "--target", "ubuntu1604"]
index 4cba3e9a62a513c8cb18d816dab98ced7f5b5363..bb2b2af7b2acf1df55f3f5f60f3c7f6649ad23ef 100755 (executable)
@@ -17,6 +17,8 @@ Options:
     Run package install tests
 --debug
     Output debug information (default: false)
+--build-version <string>
+    Version to build (default: \$ARVADOS_BUILDING_VERSION or 0.1.timestamp.commithash)
 
 WORKSPACE=path         Path to the Arvados source tree to build packages from
 
@@ -41,7 +43,7 @@ fi
 set -e
 
 PARSEDOPTS=$(getopt --name "$0" --longoptions \
-    help,test-packages,debug,command:,only-test: \
+    help,test-packages,debug,command:,only-test:,build-version: \
     -- "" "$@")
 if [ $? -ne 0 ]; then
     exit 1
@@ -72,6 +74,9 @@ while [ $# -gt 0 ]; do
         --only-test)
             ONLY_TEST="$1 $2"; shift
             ;;
+        --build-version)
+            ARVADOS_BUILDING_VERSION="$2"; shift
+            ;;
         --)
             if [ $# -gt 1 ]; then
                 echo >&2 "$0: unrecognized argument '$2'. Try: $0 --help"
@@ -87,7 +92,7 @@ cd $(dirname $0)
 FINAL_EXITCODE=0
 
 for dockerfile_path in $(find -name Dockerfile | grep package-build-dockerfiles); do
-    if ./run-build-packages-one-target.sh --target "$(basename $(dirname "$dockerfile_path"))" --command "$COMMAND" $DEBUG $TEST_PACKAGES $ONLY_TEST ; then
+    if ./run-build-packages-one-target.sh --target "$(basename $(dirname "$dockerfile_path"))" --command "$COMMAND" --build-version "$ARVADOS_BUILDING_VERSION" $DEBUG $TEST_PACKAGES $ONLY_TEST ; then
         true
     else
         FINAL_EXITCODE=$?
index 0db305114e39cf3e77852457bcfc364ddb3fca7a..ef7862c8d686c004651a5b22044898802710e327 100755 (executable)
@@ -21,6 +21,8 @@ Syntax:
     Build only a specific package
 --only-test <package>
     Test only a specific package
+--build-version <string>
+    Version to build (default: \$ARVADOS_BUILDING_VERSION or 0.1.timestamp.commithash)
 
 WORKSPACE=path         Path to the Arvados source tree to build packages from
 
@@ -45,7 +47,7 @@ if ! [[ -d "$WORKSPACE" ]]; then
 fi
 
 PARSEDOPTS=$(getopt --name "$0" --longoptions \
-    help,debug,test-packages,target:,command:,only-test:,only-build: \
+    help,debug,test-packages,target:,command:,only-test:,only-build:,build-version: \
     -- "" "$@")
 if [ $? -ne 0 ]; then
     exit 1
@@ -83,6 +85,18 @@ while [ $# -gt 0 ]; do
         --test-packages)
             test_packages=1
             ;;
+        --build-version)
+            if [[ -z "$2" ]]; then
+                :
+            elif ! [[ "$2" =~ (.*)-(.*) ]]; then
+                echo >&2 "FATAL: --build-version '$2' does not include an iteration. Try '${2}-1'?"
+                exit 1
+            else
+                ARVADOS_BUILDING_VERSION="${BASH_REMATCH[1]}"
+                ARVADOS_BUILDING_ITERATION="${BASH_REMATCH[2]}"
+            fi
+            shift
+            ;;
         --)
             if [ $# -gt 1 ]; then
                 echo >&2 "$0: unrecognized argument '$2'. Try: $0 --help"
@@ -95,6 +109,10 @@ done
 
 set -e
 
+if [[ -n "$ARVADOS_BUILDING_VERSION" ]]; then
+    echo "build version='$ARVADOS_BUILDING_VERSION', package iteration='$ARVADOS_BUILDING_ITERATION'"
+fi
+
 if [[ -n "$test_packages" ]]; then
     if [[ -n "$(find $WORKSPACE/packages/$TARGET -name '*.rpm')" ]] ; then
        set +e
@@ -216,6 +234,8 @@ else
     # Build packages
     if docker run --rm \
         "${docker_volume_args[@]}" \
+        --env ARVADOS_BUILDING_VERSION="$ARVADOS_BUILDING_VERSION" \
+        --env ARVADOS_BUILDING_ITERATION="$ARVADOS_BUILDING_ITERATION" \
         --env ARVADOS_DEBUG=$ARVADOS_DEBUG \
         --env "ONLY_BUILD=$ONLY_BUILD" \
         "$IMAGE" $COMMAND
index 57e99e809c04905c37fd0602ae2652ccd5372083..915a3319350a8abeb0658d2e53bcac57a440a7fe 100755 (executable)
@@ -393,10 +393,16 @@ fi
 cd $WORKSPACE/packages/$TARGET
 rm -rf "$WORKSPACE/sdk/cwl/build"
 arvados_cwl_runner_version=$(awk '($1 == "Version:"){print $2}' $WORKSPACE/sdk/cwl/arvados_cwl_runner.egg-info/PKG-INFO)
-arvados_cwl_runner_iteration=3
+declare -a iterargs=()
+if [[ -z "$ARVADOS_BUILDING_VERSION" ]]; then
+    arvados_cwl_runner_iteration=3
+    iterargs+=(--iteration $arvados_cwl_runner_iteration)
+else
+    arvados_cwl_runner_iteration=
+fi
 test_package_presence ${PYTHON2_PKG_PREFIX}-arvados-cwl-runner "$arvados_cwl_runner_version" python "$arvados_cwl_runner_iteration"
 if [[ "$?" == "0" ]]; then
-  fpm_build $WORKSPACE/sdk/cwl "${PYTHON2_PKG_PREFIX}-arvados-cwl-runner" 'Curoverse, Inc.' 'python' "$arvados_cwl_runner_version" "--url=https://arvados.org" "--description=The Arvados CWL runner" --depends "${PYTHON2_PKG_PREFIX}-setuptools" --iteration $arvados_cwl_runner_iteration
+  fpm_build $WORKSPACE/sdk/cwl "${PYTHON2_PKG_PREFIX}-arvados-cwl-runner" 'Curoverse, Inc.' 'python' "$arvados_cwl_runner_version" "--url=https://arvados.org" "--description=The Arvados CWL runner" --depends "${PYTHON2_PKG_PREFIX}-setuptools" "${iterargs[@]}"
 fi
 
 # schema_salad. This is a python dependency of arvados-cwl-runner,
@@ -415,16 +421,16 @@ fi
 #
 # Ward, 2016-03-17
 saladversion=$(cat "$WORKSPACE/sdk/cwl/setup.py" | grep schema-salad== | sed "s/.*==\(.*\)'.*/\1/")
-test_package_presence python-schema-salad "$saladversion" python
+test_package_presence python-schema-salad "$saladversion" python 2
 if [[ "$?" == "0" ]]; then
-  fpm_build schema_salad "" "" python $saladversion --depends "${PYTHON2_PKG_PREFIX}-lockfile >= 1:0.12.2-2" --depends "${PYTHON2_PKG_PREFIX}-avro = 1.8.1-2"
+  fpm_build schema_salad "" "" python $saladversion --depends "${PYTHON2_PKG_PREFIX}-lockfile >= 1:0.12.2-2" --depends "${PYTHON2_PKG_PREFIX}-avro = 1.8.1-2" --iteration 2
 fi
 
 # And for cwltool we have the same problem as for schema_salad. Ward, 2016-03-17
 cwltoolversion=$(cat "$WORKSPACE/sdk/cwl/setup.py" | grep cwltool== | sed "s/.*==\(.*\)'.*/\1/")
-test_package_presence python-cwltool "$cwltoolversion" python
+test_package_presence python-cwltool "$cwltoolversion" python 2
 if [[ "$?" == "0" ]]; then
-  fpm_build cwltool "" "" python $cwltoolversion
+  fpm_build cwltool "" "" python $cwltoolversion --iteration 2
 fi
 
 # The PAM module
@@ -462,23 +468,25 @@ fi
 cd $WORKSPACE/packages/$TARGET
 rm -rf "$WORKSPACE/services/dockercleaner/build"
 dockercleaner_version=$(awk '($1 == "Version:"){print $2}' $WORKSPACE/services/dockercleaner/arvados_docker_cleaner.egg-info/PKG-INFO)
-dockercleaner_iteration=3
-test_package_presence arvados-docker-cleaner "$dockercleaner_version" python "$dockercleaner_iteration"
+iteration="${ARVADOS_BUILDING_ITERATION:-3}"
+test_package_presence arvados-docker-cleaner "$dockercleaner_version" python "$iteration"
 if [[ "$?" == "0" ]]; then
-  fpm_build $WORKSPACE/services/dockercleaner arvados-docker-cleaner 'Curoverse, Inc.' 'python3' "$dockercleaner_version" "--url=https://arvados.org" "--description=The Arvados Docker image cleaner" --depends "${PYTHON3_PKG_PREFIX}-websocket-client = 0.37.0" --iteration "$dockercleaner_iteration"
+  fpm_build $WORKSPACE/services/dockercleaner arvados-docker-cleaner 'Curoverse, Inc.' 'python3' "$dockercleaner_version" "--url=https://arvados.org" "--description=The Arvados Docker image cleaner" --depends "${PYTHON3_PKG_PREFIX}-websocket-client = 0.37.0" --iteration "$iteration"
 fi
 
 # The Arvados crunchstat-summary tool
 cd $WORKSPACE/packages/$TARGET
 crunchstat_summary_version=$(awk '($1 == "Version:"){print $2}' $WORKSPACE/tools/crunchstat-summary/crunchstat_summary.egg-info/PKG-INFO)
-test_package_presence "$PYTHON2_PKG_PREFIX"-crunchstat-summary "$crunchstat_summary_version" python
+iteration="${ARVADOS_BUILDING_ITERATION:-2}"
+test_package_presence "$PYTHON2_PKG_PREFIX"-crunchstat-summary "$crunchstat_summary_version" python "$iteration"
 if [[ "$?" == "0" ]]; then
   rm -rf "$WORKSPACE/tools/crunchstat-summary/build"
-  fpm_build $WORKSPACE/tools/crunchstat-summary ${PYTHON2_PKG_PREFIX}-crunchstat-summary 'Curoverse, Inc.' 'python' "$crunchstat_summary_version" "--url=https://arvados.org" "--description=Crunchstat-summary reads Arvados Crunch log files and summarize resource usage"
+  fpm_build $WORKSPACE/tools/crunchstat-summary ${PYTHON2_PKG_PREFIX}-crunchstat-summary 'Curoverse, Inc.' 'python' "$crunchstat_summary_version" "--url=https://arvados.org" "--description=Crunchstat-summary reads Arvados Crunch log files and summarize resource usage" --iteration "$iteration"
 fi
 
-if [[ -z "$ONLY_BUILD" ]] || [[ "${PYTHON2_PKG_PREFIX}-apache-libcloud" == "$ONLY_BUILD" ]] ; then
-  # Forked libcloud
+# Forked libcloud
+if test_package_presence "$PYTHON2_PKG_PREFIX"-apache-libcloud "$LIBCLOUD_PIN" python 2
+then
   LIBCLOUD_DIR=$(mktemp -d)
   (
       cd $LIBCLOUD_DIR
@@ -490,7 +498,7 @@ if [[ -z "$ONLY_BUILD" ]] || [[ "${PYTHON2_PKG_PREFIX}-apache-libcloud" == "$ONL
       handle_python_package
       DASHQ_UNLESS_DEBUG=$OLD_DASHQ_UNLESS_DEBUG
   )
-  fpm_build $LIBCLOUD_DIR "$PYTHON2_PKG_PREFIX"-apache-libcloud
+  fpm_build $LIBCLOUD_DIR "$PYTHON2_PKG_PREFIX"-apache-libcloud "" python "" --iteration 2
   rm -rf $LIBCLOUD_DIR
 fi
 
@@ -612,7 +620,7 @@ if [[ "$?" == "0" ]] ; then
 
       # We need to bundle to be ready even when we build a package without vendor directory
       # because asset compilation requires it.
-      bundle install --path vendor/bundle >"$STDOUT_IF_DEBUG"
+      bundle install --system >"$STDOUT_IF_DEBUG"
 
       # clear the tmp directory; the asset generation step will recreate tmp/cache/assets,
       # and we want that in the package, so it's easier to not exclude the tmp directory
index 029fefc9bb3880fb9643e15ee108e6692c0211f0..6d46eb1108f29a70c527a9049918ae1643a281a9 100755 (executable)
 # older packages.
 LICENSE_PACKAGE_TS=20151208015500
 
-RAILS_PACKAGE_ITERATION=8
+if [[ -z "$ARVADOS_BUILDING_VERSION" ]]; then
+    RAILS_PACKAGE_ITERATION=8
+else
+    RAILS_PACKAGE_ITERATION="$ARVADOS_BUILDING_ITERATION"
+fi
 
 debug_echo () {
     echo "$@" >"$STDOUT_IF_DEBUG"
@@ -38,20 +42,30 @@ format_last_commit_here() {
 }
 
 version_from_git() {
-  # Generates a version number from the git log for the current working
-  # directory, and writes it to stdout.
-  local git_ts git_hash prefix
-  if [[ -n "$1" ]] ; then
-      prefix="$1"
-  else
-      prefix="0.1"
-  fi
+    # Output the version being built, or if we're building a
+    # dev/prerelease, output a version number based on the git log for
+    # the current working directory.
+    if [[ -n "$ARVADOS_BUILDING_VERSION" ]]; then
+        echo "$ARVADOS_BUILDING_VERSION"
+        return
+    fi
+
+    local git_ts git_hash prefix
+    if [[ -n "$1" ]] ; then
+        prefix="$1"
+    else
+        prefix="0.1"
+    fi
 
-  declare $(format_last_commit_here "git_ts=%ct git_hash=%h")
-  echo "${prefix}.$(date -ud "@$git_ts" +%Y%m%d%H%M%S).$git_hash"
+    declare $(format_last_commit_here "git_ts=%ct git_hash=%h")
+    echo "${prefix}.$(date -ud "@$git_ts" +%Y%m%d%H%M%S).$git_hash"
 }
 
 nohash_version_from_git() {
+    if [[ -n "$ARVADOS_BUILDING_VERSION" ]]; then
+        echo "$ARVADOS_BUILDING_VERSION"
+        return
+    fi
     version_from_git $1 | cut -d. -f1-3
 }
 
@@ -134,7 +148,7 @@ package_go_binary() {
 
     go get -ldflags "-X main.version=${version}" "git.curoverse.com/arvados.git/$src_path"
 
-    declare -a switches=()
+    local -a switches=()
     systemd_unit="$WORKSPACE/${src_path}/${prog}.service"
     if [[ -e "${systemd_unit}" ]]; then
         switches+=(
@@ -148,6 +162,10 @@ package_go_binary() {
 }
 
 default_iteration() {
+    if [[ -n "$ARVADOS_BUILDING_VERSION" ]]; then
+        echo "$ARVADOS_BUILDING_ITERATION"
+        return
+    fi
     local package_name="$1"; shift
     local package_version="$1"; shift
     local package_type="$1"; shift
@@ -189,7 +207,7 @@ test_rails_package_presence() {
 
   cd $tmppwd
 
-  test_package_presence $pkgname $version rails $RAILS_PACKAGE_ITERATION
+  test_package_presence $pkgname $version rails "$RAILS_PACKAGE_ITERATION"
 }
 
 test_package_presence() {
@@ -204,7 +222,7 @@ test_package_presence() {
     fi
 
     if [[ "$iteration" == "" ]]; then
-      iteration="$(default_iteration "$pkgname" "$version" "$pkgtype")"
+        iteration="$(default_iteration "$pkgname" "$version" "$pkgtype")"
     fi
 
     if [[ "$arch" == "" ]]; then
@@ -232,9 +250,11 @@ test_package_presence() {
     fi
 
     if [[ "$FORMAT" == "deb" ]]; then
-      local complete_pkgname=$pkgname"_"$version"-"$iteration"_"$deb_architecture".deb"
+        local complete_pkgname="${pkgname}_$version${iteration:+-$iteration}_$deb_architecture.deb"
     else
-      local complete_pkgname="$pkgname-$version-$iteration.$rpm_architecture.rpm"
+        # rpm packages get iteration 1 if we don't supply one
+        iteration=${iteration:-1}
+        local complete_pkgname="$pkgname-$version-${iteration}.$rpm_architecture.rpm"
     fi
 
     # See if we can skip building the package, only if it already exists in the
@@ -257,31 +277,33 @@ handle_rails_package() {
         return 0
     fi
     local srcdir="$1"; shift
+    cd "$srcdir"
     local license_path="$1"; shift
+    local version="$(version_from_git)"
     local scripts_dir="$(mktemp --tmpdir -d "$pkgname-XXXXXXXX.scripts")" && \
-    local version_file="$(mktemp --tmpdir "$pkgname-XXXXXXXX.version")" && (
+    (
         set -e
         _build_rails_package_scripts "$pkgname" "$scripts_dir"
         cd "$srcdir"
         mkdir -p tmp
-        version_from_git >"$version_file"
         git rev-parse HEAD >git-commit.version
         bundle package --all
     )
     if [[ 0 != "$?" ]] || ! cd "$WORKSPACE/packages/$TARGET"; then
         echo "ERROR: $pkgname package prep failed" >&2
-        rm -rf "$scripts_dir" "$version_file"
+        rm -rf "$scripts_dir"
         EXITCODE=1
         return 1
     fi
     local railsdir="/var/www/${pkgname%-server}/current"
-    local -a pos_args=("$srcdir/=$railsdir" "$pkgname" "Curoverse, Inc." dir
-                       "$(cat "$version_file")")
+    local -a pos_args=("$srcdir/=$railsdir" "$pkgname" "Curoverse, Inc." dir "$version")
     local license_arg="$license_path=$railsdir/$(basename "$license_path")"
-    local -a switches=(--iteration=$RAILS_PACKAGE_ITERATION
-                       --after-install "$scripts_dir/postinst"
+    local -a switches=(--after-install "$scripts_dir/postinst"
                        --before-remove "$scripts_dir/prerm"
                        --after-remove "$scripts_dir/postrm")
+    if [[ -z "$ARVADOS_BUILDING_VERSION" ]]; then
+        switches+=(--iteration $RAILS_PACKAGE_ITERATION)
+    fi
     # For some reason fpm excludes need to not start with /.
     local exclude_root="${railsdir#/}"
     # .git and packages are for the SSO server, which is built from its
@@ -296,8 +318,9 @@ handle_rails_package() {
         switches+=(-x "$exclude_root/$exclude")
     done
     fpm_build "${pos_args[@]}" "${switches[@]}" \
+              -x "$exclude_root/vendor/cache-*" \
               -x "$exclude_root/vendor/bundle" "$@" "$license_arg"
-    rm -rf "$scripts_dir" "$version_file"
+    rm -rf "$scripts_dir"
 }
 
 # Build packages for everything
@@ -389,9 +412,11 @@ fpm_build () {
   if [[ "$VERSION" != "" ]]; then
     COMMAND_ARR+=('-v' "$VERSION")
   fi
-  # We can always add an --iteration here.  If another one is specified in $@,
-  # that will take precedence, as desired.
-  COMMAND_ARR+=(--iteration "$default_iteration_value")
+  if [[ -n "$default_iteration_value" ]]; then
+      # We can always add an --iteration here.  If another one is specified in $@,
+      # that will take precedence, as desired.
+      COMMAND_ARR+=(--iteration "$default_iteration_value")
+  fi
 
   if [[ python = "$PACKAGE_TYPE" ]] && [[ -e "${PACKAGE}/${PACKAGE_NAME}.service" ]]
   then
index ebdf4c413d896a0a85a09315ad86f31498b1a062..7d1d4c9e6b29cc7783a40b992fed3773457b1341 100755 (executable)
@@ -811,6 +811,16 @@ install_apiserver() {
 
     mkdir -p "$WORKSPACE/services/api/tmp/pids"
 
+    cert="$WORKSPACE/services/api/tmp/self-signed"
+    if [[ ! -e "$cert.pem" || "$(date -r "$cert.pem" +%s)" -lt 1512659226 ]]; then
+        (
+            dir="$WORKSPACE/services/api/tmp"
+            set -ex
+            openssl req -newkey rsa:2048 -nodes -subj '/C=US/ST=State/L=City/CN=localhost' -out "$cert.csr" -keyout "$cert.key" </dev/null
+            openssl x509 -req -in "$cert.csr" -signkey "$cert.key" -out "$cert.pem" -days 3650 -extfile <(printf 'subjectAltName=DNS:localhost,DNS:::1,DNS:0.0.0.0,DNS:127.0.0.1,IP:::1,IP:0.0.0.0,IP:127.0.0.1')
+        ) || return 1
+    fi
+
     cd "$WORKSPACE/services/api" \
         && RAILS_ENV=test bundle exec rake db:drop \
         && RAILS_ENV=test bundle exec rake db:setup \
index 577295c4fc645cc4fdb2531a1b49c7beaf4b5436..ae487355c33700e889c2cb8d06184d9818f2b3dc 100644 (file)
@@ -13,28 +13,18 @@ from setuptools import setup, find_packages
 SETUP_DIR = os.path.dirname(__file__) or '.'
 README = os.path.join(SETUP_DIR, 'README.rst')
 
-try:
-    import gittaggers
-    tagger = gittaggers.EggInfoFromGit
-except ImportError:
-    tagger = egg_info_cmd.egg_info
-
-versionfile = os.path.join(SETUP_DIR, "arvados_cwl/_version.py")
-try:
-    gitinfo = subprocess.check_output(
-        ['git', 'log', '--first-parent', '--max-count=1',
-         '--format=format:%H', gittaggers.choose_version_from()]).strip()
-    with open(versionfile, "w") as f:
-        f.write("__version__ = '%s'\n" % gitinfo)
-except Exception as e:
-    # When installing from package, it won't be part of a git repository, and
-    # check_output() will raise an exception.  But the package should include the
-    # version file, so we can proceed.
-    if not os.path.exists(versionfile):
-        raise
+tagger = egg_info_cmd.egg_info
+version = os.environ.get("ARVADOS_BUILDING_VERSION")
+if not version:
+    version = "1.0"
+    try:
+        import gittaggers
+        tagger = gittaggers.EggInfoFromGit
+    except ImportError:
+        pass
 
 setup(name='arvados-cwl-runner',
-      version='1.0',
+      version=version,
       description='Arvados Common Workflow Language runner',
       long_description=open(README).read(),
       author='Arvados',
index dc81c3b8fa58f565c9079e7a6d2cc653c64bfb09..35ed08238d0663fde630036a888dbc0599cf0da8 100755 (executable)
@@ -15,14 +15,17 @@ SETUP_DIR = os.path.dirname(__file__) or '.'
 README = os.path.join(SETUP_DIR, 'README.rst')
 
 tagger = egg_info_cmd.egg_info
-try:
-    import gittaggers
-    tagger = gittaggers.EggInfoFromGit
-except (ImportError, OSError):
-    pass
+version = os.environ.get("ARVADOS_BUILDING_VERSION")
+if not version:
+    version = "0.1"
+    try:
+        import gittaggers
+        tagger = gittaggers.EggInfoFromGit
+    except ImportError:
+        pass
 
 setup(name='arvados-pam',
-      version='0.1',
+      version=version,
       description='Arvados PAM module',
       long_description=open(README).read(),
       author='Arvados',
index fdc15022f164318707e8a8df6296eb99831efb99..88bf51e8a8971181ea0f45a50e2b44ec27a9f275 100644 (file)
@@ -12,11 +12,15 @@ from setuptools import setup, find_packages
 SETUP_DIR = os.path.dirname(__file__) or '.'
 README = os.path.join(SETUP_DIR, 'README.rst')
 
-try:
-    import gittaggers
-    tagger = gittaggers.EggInfoFromGit
-except ImportError:
-    tagger = egg_info_cmd.egg_info
+tagger = egg_info_cmd.egg_info
+version = os.environ.get("ARVADOS_BUILDING_VERSION")
+if not version:
+    version = "0.1"
+    try:
+        import gittaggers
+        tagger = gittaggers.EggInfoFromGit
+    except ImportError:
+        pass
 
 short_tests_only = False
 if '--short-tests-only' in sys.argv:
@@ -24,7 +28,7 @@ if '--short-tests-only' in sys.argv:
     sys.argv.remove('--short-tests-only')
 
 setup(name='arvados-python-client',
-      version='0.1',
+      version=version,
       description='Arvados client library',
       long_description=open(README).read(),
       author='Arvados',
index 57efb97c4851ef3b5b678b906bddefb2b05abc83..567b3b3bfaacf693e7147159bff4d3aa9ad71025 100644 (file)
@@ -288,21 +288,6 @@ def run(leave_running_atexit=False):
     if not os.path.exists('tmp/logs'):
         os.makedirs('tmp/logs')
 
-    if not os.path.exists('tmp/self-signed.pem'):
-        # We assume here that either passenger reports its listening
-        # address as https:/0.0.0.0:port/. If it reports "127.0.0.1"
-        # then the certificate won't match the host and reset() will
-        # fail certificate verification. If it reports "localhost",
-        # clients (notably Python SDK's websocket client) might
-        # resolve localhost as ::1 and then fail to connect.
-        subprocess.check_call([
-            'openssl', 'req', '-new', '-x509', '-nodes',
-            '-out', 'tmp/self-signed.pem',
-            '-keyout', 'tmp/self-signed.key',
-            '-days', '3650',
-            '-subj', '/CN=0.0.0.0'],
-        stdout=sys.stderr)
-
     # Install the git repository fixtures.
     gitdir = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git')
     gittarball = os.path.join(SERVICES_SRC_DIR, 'api', 'test', 'test.git.tar')
index 25e13a51dbbf70444dbd0eaa6c4417fad93b1d15..4cb5671e1801fc75107057da949653482cbf8430 100644 (file)
@@ -57,6 +57,7 @@ gem 'themes_for_rails', git: 'https://github.com/curoverse/themes_for_rails'
 
 gem 'arvados', '>= 0.1.20150615153458'
 gem 'arvados-cli', '>= 0.1.20161017193526'
+gem 'httpclient'
 
 gem 'sshkey'
 gem 'safe_yaml'
index 91a9d04e4ebe26b48b4a85c88a8c40fa1d9ad826..b2de3f51f2851efe374613292ef99f827032a5b3 100644 (file)
@@ -127,6 +127,7 @@ GEM
     hashie (3.5.5)
     highline (1.7.8)
     hike (1.2.3)
+    httpclient (2.8.3)
     i18n (0.9.0)
       concurrent-ruby (~> 1.0)
     jquery-rails (4.2.2)
@@ -295,6 +296,7 @@ DEPENDENCIES
   database_cleaner
   factory_girl_rails
   faye-websocket
+  httpclient
   jquery-rails
   lograge
   logstash-event
index 6bdba7af89d803975faa40f50d6508ab0b25d953..649aa2b0df2a8a72941d399dbd3b5728c3f349db 100644 (file)
@@ -345,7 +345,7 @@ class ApplicationController < ActionController::Base
         .all
     end
     @read_auths.select! { |auth| auth.scopes_allow_request? request }
-    @read_users = @read_auths.map { |auth| auth.user }.uniq
+    @read_users = @read_auths.map(&:user).uniq
   end
 
   def require_login
index 3adbe9e387ff5caa446af117939aa38c6b0d9fa2..a237829ec7b4f06f6d0aae693a7853e318777b7f 100644 (file)
@@ -17,7 +17,13 @@ class Arvados::V1::SchemaController < ApplicationController
 
   def index
     expires_in 24.hours, public: true
-    discovery = Rails.cache.fetch 'arvados_v1_rest_discovery' do
+    send_json discovery_doc
+  end
+
+  protected
+
+  def discovery_doc
+    Rails.cache.fetch 'arvados_v1_rest_discovery' do
       Rails.application.eager_load!
       discovery = {
         kind: "discovery#restDescription",
@@ -49,6 +55,8 @@ class Arvados::V1::SchemaController < ApplicationController
         crunchLogThrottleLines: Rails.application.config.crunch_log_throttle_lines,
         crunchLimitLogBytesPerJob: Rails.application.config.crunch_limit_log_bytes_per_job,
         crunchLogPartialLineThrottlePeriod: Rails.application.config.crunch_log_partial_line_throttle_period,
+        remoteHosts: Rails.configuration.remote_hosts,
+        remoteHostsViaDNS: Rails.configuration.remote_hosts_via_dns,
         websocketUrl: Rails.application.config.websocket_address,
         workbenchUrl: Rails.application.config.workbench_address,
         keepWebServiceUrl: Rails.application.config.keep_web_service_url,
@@ -381,6 +389,5 @@ class Arvados::V1::SchemaController < ApplicationController
       end
       discovery
     end
-    send_json discovery
   end
 end
index 5a90f4f8ead61df1bfa5ae791dd0c4354bcebb51..5de85bc98bcbcb1a0051c3ecee355e82292b5a27 100644 (file)
@@ -24,7 +24,11 @@ class UserSessionsController < ApplicationController
       return redirect_to login_failure_url
     end
 
-    user = User.find_by_identity_url(omniauth['info']['identity_url'])
+    # Only local users can create sessions, hence uuid_like_pattern
+    # here.
+    user = User.where('identity_url = ? and uuid like ?',
+                      omniauth['info']['identity_url'],
+                      User.uuid_like_pattern).first
     if not user
       # Check for permission to log in to an existing User record with
       # a different identity_url
index 6a376318271472db857db6b926ba90d4d8262244..4098fd72ca436bdf3ee806b5a0dfb938fe7a5a9b 100644 (file)
@@ -15,57 +15,50 @@ class ArvadosApiToken
   end
 
   def call env
-    # First, clean up just in case we have a multithreaded server and thread
-    # local variables are still set from a prior request.  Also useful for
-    # tests that call this code to set up the environment.
-    Thread.current[:api_client_ip_address] = nil
-    Thread.current[:api_client_authorization] = nil
-    Thread.current[:api_client_uuid] = nil
-    Thread.current[:api_client] = nil
-    Thread.current[:user] = nil
-
     request = Rack::Request.new(env)
     params = request.params
     remote_ip = env["action_dispatch.remote_ip"]
 
     Thread.current[:request_starttime] = Time.now
-    user = nil
-    api_client = nil
-    api_client_auth = nil
-    if request.get? || params["_method"] == 'GET'
+
+    remote = false
+    reader_tokens = nil
+    if params["remote"] && request.get? && (
+         request.path.start_with?('/arvados/v1/groups') ||
+         request.path.start_with?('/arvados/v1/users/current'))
+      # Request from a remote API server, asking to validate a salted
+      # token.
+      remote = params["remote"]
+    elsif request.get? || params["_method"] == 'GET'
       reader_tokens = params["reader_tokens"]
       if reader_tokens.is_a? String
         reader_tokens = SafeJSON.load(reader_tokens)
       end
-    else
-      reader_tokens = nil
     end
 
     # Set current_user etc. based on the primary session token if a
     # valid one is present. Otherwise, use the first valid token in
     # reader_tokens.
+    auth = nil
     [params["api_token"],
      params["oauth_token"],
-     env["HTTP_AUTHORIZATION"].andand.match(/OAuth2 ([a-zA-Z0-9]+)/).andand[1],
+     env["HTTP_AUTHORIZATION"].andand.match(/(OAuth2|Bearer) ([-\/a-zA-Z0-9]+)/).andand[2],
      *reader_tokens,
     ].each do |supplied|
       next if !supplied
       try_auth = ApiClientAuthorization.
-        includes(:api_client, :user).
-        where('api_token=? and (expires_at is null or expires_at > CURRENT_TIMESTAMP)', supplied).
-        first
+                 validate(token: supplied, remote: remote)
       if try_auth.andand.user
-        api_client_auth = try_auth
-        user = api_client_auth.user
-        api_client = api_client_auth.api_client
+        auth = try_auth
         break
       end
     end
+
     Thread.current[:api_client_ip_address] = remote_ip
-    Thread.current[:api_client_authorization] = api_client_auth
-    Thread.current[:api_client_uuid] = api_client.andand.uuid
-    Thread.current[:api_client] = api_client
-    Thread.current[:user] = user
+    Thread.current[:api_client_authorization] = auth
+    Thread.current[:api_client_uuid] = auth.andand.api_client.andand.uuid
+    Thread.current[:api_client] = auth.andand.api_client
+    Thread.current[:user] = auth.andand.user
 
     @app.call env if @app
   end
index 10c02cca25a576a113801b07865a75dfa8affa82..3af206c450290cce28a914caa51b5ee385847269 100644 (file)
@@ -6,6 +6,7 @@ class ApiClientAuthorization < ArvadosModel
   include HasUuid
   include KindAndEtag
   include CommonApiTemplate
+  extend CurrentApiClient
 
   belongs_to :api_client
   belongs_to :user
@@ -16,6 +17,9 @@ class ApiClientAuthorization < ArvadosModel
     t.add :owner_uuid
     t.add :user_id
     t.add :api_client_id
+    # NB the "api_token" db column is a misnomer in that it's only the
+    # "secret" part of a token: a v1 token is just the secret, but a
+    # v2 token is "v2/uuid/secret".
     t.add :api_token
     t.add :created_by_ip_address
     t.add :default_owner_uuid
@@ -82,6 +86,120 @@ class ApiClientAuthorization < ArvadosModel
     ["#{table_name}.id desc"]
   end
 
+  def self.remote_host(uuid_prefix:)
+    Rails.configuration.remote_hosts[uuid_prefix] ||
+      (Rails.configuration.remote_hosts_via_dns &&
+       uuid_prefix+".arvadosapi.com")
+  end
+
+  def self.validate(token:, remote:)
+    return nil if !token
+    remote ||= Rails.configuration.uuid_prefix
+
+    case token[0..2]
+    when 'v2/'
+      _, uuid, secret = token.split('/')
+      unless uuid.andand.length == 27 && secret.andand.length.andand > 0
+        return nil
+      end
+
+      auth = ApiClientAuthorization.
+             includes(:user, :api_client).
+             where('uuid=? and (expires_at is null or expires_at > CURRENT_TIMESTAMP)', uuid).
+             first
+      if auth && auth.user &&
+         (secret == auth.api_token ||
+          secret == OpenSSL::HMAC.hexdigest('sha1', auth.api_token, remote))
+        return auth
+      end
+
+      uuid_prefix = uuid[0..4]
+      if uuid_prefix == Rails.configuration.uuid_prefix
+        # If the token were valid, we would have validated it above
+        return nil
+      elsif uuid_prefix.length != 5
+        # malformed
+        return nil
+      end
+
+      host = remote_host(uuid_prefix: uuid_prefix)
+      if !host
+        Rails.logger.warn "remote authentication rejected: no host for #{uuid_prefix.inspect}"
+        return nil
+      end
+
+      # Token was issued by a different cluster. If it's expired or
+      # missing in our database, ask the originating cluster to
+      # [re]validate it.
+      begin
+        clnt = HTTPClient.new
+        if Rails.configuration.sso_insecure
+          clnt.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
+        end
+        remote_user = SafeJSON.load(
+          clnt.get_content('https://' + host + '/arvados/v1/users/current',
+                           {'remote' => Rails.configuration.uuid_prefix},
+                           {'Authorization' => 'Bearer ' + token}))
+      rescue => e
+        Rails.logger.warn "remote authentication with token #{token.inspect} failed: #{e}"
+        return nil
+      end
+      if !remote_user.is_a?(Hash) || !remote_user['uuid'].is_a?(String) || remote_user['uuid'][0..4] != uuid[0..4]
+        Rails.logger.warn "remote authentication rejected: remote_user=#{remote_user.inspect}"
+        return nil
+      end
+      act_as_system_user do
+        # Add/update user and token in our database so we can
+        # validate subsequent requests faster.
+
+        user = User.find_or_create_by(uuid: remote_user['uuid']) do |user|
+          # (this block runs for the "create" case, not for "find")
+          user.is_admin = false
+          user.email = remote_user['email']
+          if remote_user['username'].andand.length.andand > 0
+            user.set_initial_username(requested: remote_user['username'])
+          end
+        end
+
+        if Rails.configuration.new_users_are_active
+          # Update is_active to whatever it is at the remote end
+          user.is_active = remote_user['is_active']
+        elsif !remote_user['is_active']
+          # Remote user is inactive; our mirror should be, too.
+          user.is_active = false
+        end
+
+        %w[first_name last_name email prefs].each do |attr|
+          user.send(attr+'=', remote_user[attr])
+        end
+
+        user.save!
+
+        auth = ApiClientAuthorization.find_or_create_by(uuid: uuid) do |auth|
+          auth.user = user
+          auth.api_token = secret
+          auth.api_client_id = 0
+        end
+
+        # Accept this token (and don't reload the user record) for
+        # 5 minutes. TODO: Request the actual api_client_auth
+        # record from the remote server in case it wants the token
+        # to expire sooner.
+        auth.update_attributes!(expires_at: Time.now + 5.minutes)
+      end
+      return auth
+    else
+      auth = ApiClientAuthorization.
+             includes(:user, :api_client).
+             where('api_token=? and (expires_at is null or expires_at > CURRENT_TIMESTAMP)', token).
+             first
+      if auth && auth.user
+        return auth
+      end
+    end
+    return nil
+  end
+
   protected
 
   def permission_to_create
index 778c3d4d95b324277cf70a61991d1a9b2d40bcee..a1c35f10fcf1f9e1aae9ead9bf1cda00b5f2535a 100644 (file)
@@ -382,15 +382,38 @@ common:
   # original job reuse behavior, and is still the default).
   reuse_job_if_outputs_differ: false
 
+  ###
+  ### Federation support.
+  ###
+
+  # You can enable use of this cluster by users who are authenticated
+  # by a remote Arvados site. Control which remote hosts are trusted
+  # to authenticate which user IDs by configuring remote_hosts,
+  # remote_hosts_via_dns, or both. The default configuration disables
+  # remote authentication.
+
+  # Map known prefixes to hosts. For example, if user IDs beginning
+  # with "zzzzz-" should be authenticated by the Arvados server at
+  # "zzzzz.example.com", use:
+  #
+  # remote_hosts:
+  #   zzzzz: zzzzz.example.com
+  remote_hosts: {}
+
+  # Use {prefix}.arvadosapi.com for any prefix not given in
+  # remote_hosts above.
+  remote_hosts_via_dns: false
+
   ###
   ### Remaining assorted configuration options.
   ###
 
   arvados_theme: default
 
-  # Permit insecure (OpenSSL::SSL::VERIFY_NONE) connections to the Single Sign
-  # On (sso) server.  Should only be enabled during development when the SSO
-  # server is using a self-signed cert.
+  # Permit insecure (OpenSSL::SSL::VERIFY_NONE) connections to the
+  # Single Sign On (sso) server and remote Arvados sites.  Should only
+  # be enabled during development when the SSO server is using a
+  # self-signed cert.
   sso_insecure: false
 
   ## Set Time.zone default to the specified zone and make Active
index ddc40a48448f9ab135d54f0f936e133751d4e391..3442eda2447aa1e75ecc254b3ffcfb2392853a8f 100644 (file)
@@ -704,6 +704,5 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
       assert_response :success
       assert_not_nil Group.readable_by(users(auth)).where(uuid: groups(:trashed_subproject).uuid).first
     end
-
   end
 end
diff --git a/services/api/test/integration/remote_user_test.rb b/services/api/test/integration/remote_user_test.rb
new file mode 100644 (file)
index 0000000..591bbaf
--- /dev/null
@@ -0,0 +1,214 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+require 'webrick'
+require 'webrick/https'
+require 'test_helper'
+require 'helpers/users_test_helper'
+
+class RemoteUsersTest < ActionDispatch::IntegrationTest
+  include DbCurrentTime
+
+  def salted_active_token(remote:)
+    salt_token(fixture: :active, remote: remote).sub('/zzzzz-', '/'+remote+'-')
+  end
+
+  def auth(remote:)
+    token = salted_active_token(remote: remote)
+    {"HTTP_AUTHORIZATION" => "Bearer #{token}"}
+  end
+
+  # For remote authentication tests, we bring up a simple stub server
+  # (on a port chosen by webrick) and configure the SUT so the stub is
+  # responsible for clusters "zbbbb" (a well-behaved cluster) and
+  # "zbork" (a misbehaving cluster).
+  #
+  # Test cases can override the stub's default response to
+  # .../users/current by changing @stub_status and @stub_content.
+  setup do
+    clnt = HTTPClient.new
+    clnt.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
+    HTTPClient.stubs(:new).returns clnt
+
+    @controller = Arvados::V1::UsersController.new
+    ready = Thread::Queue.new
+    srv = WEBrick::HTTPServer.new(
+      Port: 0,
+      Logger: WEBrick::Log.new(
+        Rails.root.join("log", "webrick.log").to_s,
+        WEBrick::Log::INFO),
+      AccessLog: [[File.open(Rails.root.join(
+                              "log", "webrick_access.log").to_s, 'a+'),
+                   WEBrick::AccessLog::COMBINED_LOG_FORMAT]],
+      SSLEnable: true,
+      SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
+      SSLPrivateKey: OpenSSL::PKey::RSA.new(
+        File.open(Rails.root.join("tmp", "self-signed.key")).read),
+      SSLCertificate: OpenSSL::X509::Certificate.new(
+        File.open(Rails.root.join("tmp", "self-signed.pem")).read),
+      SSLCertName: [["CN", WEBrick::Utils::getservername]],
+      StartCallback: lambda { ready.push(true) })
+    srv.mount_proc '/discovery/v1/apis/arvados/v1/rest' do |req, res|
+      Rails.cache.delete 'arvados_v1_rest_discovery'
+      res.body = Arvados::V1::SchemaController.new.send(:discovery_doc).to_json
+    end
+    srv.mount_proc '/arvados/v1/users/current' do |req, res|
+      res.status = @stub_status
+      res.body = @stub_content.is_a?(String) ? @stub_content : @stub_content.to_json
+    end
+    Thread.new do
+      srv.start
+    end
+    ready.pop
+    @remote_server = srv
+    @remote_host = "127.0.0.1:#{srv.config[:Port]}"
+    Rails.configuration.remote_hosts['zbbbb'] = @remote_host
+    Rails.configuration.remote_hosts['zbork'] = @remote_host
+    Arvados::V1::SchemaController.any_instance.stubs(:root_url).returns "https://#{@remote_host}"
+    @stub_status = 200
+    @stub_content = {
+      uuid: 'zbbbb-tpzed-000000000000000',
+      email: 'foo@example.com',
+      username: 'barney',
+      is_admin: true,
+      is_active: true,
+    }
+  end
+
+  teardown do
+    @remote_server.andand.stop
+  end
+
+  test 'authenticate with remote token' do
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response :success
+    assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
+    assert_equal false, json_response['is_admin']
+    assert_equal 'foo@example.com', json_response['email']
+    assert_equal 'barney', json_response['username']
+
+    # revoke original token
+    @stub_status = 401
+
+    # re-authorize before cache expires
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response :success
+
+    # simulate cache expiry
+    ApiClientAuthorization.where(
+      uuid: salted_active_token(remote: 'zbbbb').split('/')[1]).
+      update_all(expires_at: db_current_time - 1.minute)
+
+    # re-authorize after cache expires
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response 401
+
+    # revive original token and re-authorize
+    @stub_status = 200
+    @stub_content[:username] = 'blarney'
+    @stub_content[:email] = 'blarney@example.com'
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response :success
+    assert_equal 'barney', json_response['username'], 'local username should not change once assigned'
+    assert_equal 'blarney@example.com', json_response['email']
+  end
+
+  test 'authenticate with remote token, remote username conflicts with local' do
+    @stub_content[:username] = 'active'
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response :success
+    assert_equal 'active2', json_response['username']
+  end
+
+  test 'authenticate with remote token, remote username is nil' do
+    @stub_content.delete :username
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response :success
+    assert_equal 'foo', json_response['username']
+  end
+
+  test 'authenticate with remote token from misbhehaving remote cluster' do
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbork')
+    assert_response 401
+  end
+
+  test 'authenticate with remote token that fails validate' do
+    @stub_status = 401
+    @stub_content = {
+      error: 'not authorized',
+    }
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response 401
+  end
+
+  ['v2',
+   'v2/',
+   'v2//',
+   'v2///',
+   "v2/'; delete from users where 1=1; commit; select '/lol",
+   'v2/foo/bar',
+   'v2/zzzzz-gj3su-077z32aux8dg2s1',
+   'v2/zzzzz-gj3su-077z32aux8dg2s1/',
+   'v2/3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi',
+   'v2/3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi/zzzzz-gj3su-077z32aux8dg2s1',
+   'v2//3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi',
+   'v8/zzzzz-gj3su-077z32aux8dg2s1/3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi',
+   '/zzzzz-gj3su-077z32aux8dg2s1/3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi',
+   '"v2/zzzzz-gj3su-077z32aux8dg2s1/3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi"',
+   '/',
+   '//',
+   '///',
+  ].each do |token|
+    test "authenticate with malformed remote token #{token}" do
+      get '/arvados/v1/users/current', {format: 'json'}, {"HTTP_AUTHORIZATION" => "Bearer #{token}"}
+      assert_response 401
+    end
+  end
+
+  test "ignore extra fields in remote token" do
+    token = salted_active_token(remote: 'zbbbb') + '/foo/bar/baz/*'
+    get '/arvados/v1/users/current', {format: 'json'}, {"HTTP_AUTHORIZATION" => "Bearer #{token}"}
+    assert_response :success
+  end
+
+  test 'remote api server is not an api server' do
+    @stub_status = 200
+    @stub_content = '<html>bad</html>'
+    get '/arvados/v1/users/current', {format: 'json'}, auth(remote: 'zbbbb')
+    assert_response 401
+  end
+
+  ['zbbbb', 'z0000'].each do |token_valid_for|
+    test "validate #{token_valid_for}-salted token for remote cluster zbbbb" do
+      salted_token = salt_token(fixture: :active, remote: token_valid_for)
+      get '/arvados/v1/users/current', {format: 'json', remote: 'zbbbb'}, {
+            "HTTP_AUTHORIZATION" => "Bearer #{salted_token}"
+          }
+      if token_valid_for == 'zbbbb'
+        assert_response 200
+        assert_equal(users(:active).uuid, json_response['uuid'])
+      else
+        assert_response 401
+      end
+    end
+  end
+
+  test "list readable groups with salted token" do
+    salted_token = salt_token(fixture: :active, remote: 'zbbbb')
+    get '/arvados/v1/groups', {
+          format: 'json',
+          remote: 'zbbbb',
+          limit: 10000,
+        }, {
+          "HTTP_AUTHORIZATION" => "Bearer #{salted_token}"
+        }
+    assert_response 200
+    group_uuids = json_response['items'].collect { |i| i['uuid'] }
+    assert_includes(group_uuids, 'zzzzz-j7d0g-fffffffffffffff')
+    refute_includes(group_uuids, 'zzzzz-j7d0g-000000000000000')
+    assert_includes(group_uuids, groups(:aproject).uuid)
+    refute_includes(group_uuids, groups(:trashed_project).uuid)
+    refute_includes(group_uuids, groups(:testusergroup_admins).uuid)
+  end
+end
index 0288e887f94e83361e59f60a7beba6a7cf62a493..8ddab3fee1eb6963dff5c34b3f2788fa09bcef1e 100644 (file)
@@ -216,5 +216,4 @@ class UsersTest < ActionDispatch::IntegrationTest
     end
     nil
   end
-
 end
index d874bc4f1ffebd2777cdc9e7ec392e907eceb7d7..c834250cb6caa89c28ff25ad942978dd14399949 100644 (file)
@@ -127,6 +127,14 @@ class ActiveSupport::TestCase
                              "HTTP_AUTHORIZATION" => "OAuth2 #{t}")
   end
 
+  def salt_token(fixture:, remote:)
+    auth = api_client_authorizations(fixture)
+    uuid = auth.uuid
+    token = auth.api_token
+    hmac = OpenSSL::HMAC.hexdigest('sha1', token, remote)
+    return "v2/#{uuid}/#{hmac}"
+  end
+
   def self.skip_slow_tests?
     !(ENV['RAILS_TEST_SHORT'] || '').empty?
   end
index b904b0fa82aaeff118c7d5b94378883efe7a6e58..6a6a96a0455eee17d5f5bb1b725b69f767805cae 100644 (file)
@@ -9,14 +9,18 @@ import setuptools.command.egg_info as egg_info_cmd
 
 from setuptools import setup, find_packages
 
-try:
-    import gittaggers
-    tagger = gittaggers.EggInfoFromGit
-except ImportError:
-    tagger = egg_info_cmd.egg_info
+tagger = egg_info_cmd.egg_info
+version = os.environ.get("ARVADOS_BUILDING_VERSION")
+if not version:
+    version = "0.1"
+    try:
+        import gittaggers
+        tagger = gittaggers.EggInfoFromGit
+    except ImportError:
+        pass
 
 setup(name="arvados-docker-cleaner",
-      version="0.1",
+      version=version,
       description="Arvados Docker cleaner",
       author="Arvados",
       author_email="info@arvados.org",
index d46a3128305581837060727056c27a7eeadd7456..2358eb928fd6b2cccda04c7f7e08bf2657b42d00 100644 (file)
@@ -12,11 +12,15 @@ from setuptools import setup, find_packages
 SETUP_DIR = os.path.dirname(__file__) or '.'
 README = os.path.join(SETUP_DIR, 'README.rst')
 
-try:
-    import gittaggers
-    tagger = gittaggers.EggInfoFromGit
-except ImportError:
-    tagger = egg_info_cmd.egg_info
+tagger = egg_info_cmd.egg_info
+version = os.environ.get("ARVADOS_BUILDING_VERSION")
+if not version:
+    version = "0.1"
+    try:
+        import gittaggers
+        tagger = gittaggers.EggInfoFromGit
+    except ImportError:
+        pass
 
 short_tests_only = False
 if '--short-tests-only' in sys.argv:
@@ -24,7 +28,7 @@ if '--short-tests-only' in sys.argv:
     sys.argv.remove('--short-tests-only')
 
 setup(name='arvados_fuse',
-      version='0.1',
+      version=version,
       description='Arvados FUSE driver',
       long_description=open(README).read(),
       author='Arvados',
index 6382dcb7277c19c20bb7612c7b9aecbf65137b99..64545eb0f977a39338373291d2e271762cd61b7a 100644 (file)
@@ -12,14 +12,18 @@ from setuptools import setup, find_packages
 SETUP_DIR = os.path.dirname(__file__) or "."
 README = os.path.join(SETUP_DIR, 'README.rst')
 
-try:
-    import gittaggers
-    tagger = gittaggers.EggInfoFromGit
-except ImportError:
-    tagger = egg_info_cmd.egg_info
+tagger = egg_info_cmd.egg_info
+version = os.environ.get("ARVADOS_BUILDING_VERSION")
+if not version:
+    version = "0.1"
+    try:
+        import gittaggers
+        tagger = gittaggers.EggInfoFromGit
+    except ImportError:
+        pass
 
 setup(name='arvados-node-manager',
-      version='0.1',
+      version=version,
       description='Arvados compute node manager',
       long_description=open(README).read(),
       author='Arvados',
index 6578ea5820592e04cdf703cf75b91ec17895a40b..71b9b1c4ba1d3033c3c232bb1b8ff6e7b42d5d71 100755 (executable)
@@ -17,6 +17,6 @@ PATH=$PATH:/usr/src/composer/node_modules/.bin
 yarn install
 
 if test "$1" != "--only-deps" ; then
-    echo "apiEndPoint: https://${localip}:${services[api]}" > /usr/src/composer/src/arvados-configuration.yml
+    echo "apiEndPoint: https://${localip}:${services[api]}" > /usr/src/composer/src/composer.yml
     exec ng serve --host 0.0.0.0 --port 4200 --env=webdev
 fi
index e54d82f151b6731ac3bffde38ab1964f50563676..ce1467b058259ce7bb19e9e46cdae847e53bdc97 100755 (executable)
@@ -11,14 +11,18 @@ from setuptools import setup, find_packages
 
 SETUP_DIR = os.path.dirname(__file__) or '.'
 
-try:
-    import gittaggers
-    tagger = gittaggers.EggInfoFromGit
-except ImportError:
-    tagger = egg_info_cmd.egg_info
+tagger = egg_info_cmd.egg_info
+version = os.environ.get("ARVADOS_BUILDING_VERSION")
+if not version:
+    version = "0.1"
+    try:
+        import gittaggers
+        tagger = gittaggers.EggInfoFromGit
+    except ImportError:
+        pass
 
 setup(name='crunchstat_summary',
-      version='0.1',
+      version=version,
       description='read crunch log files and summarize resource usage',
       author='Arvados',
       author_email='info@arvados.org',