#!/usr/bin/env bats   -*- bats -*-
#
# tests for podman login
#

load helpers

###############################################################################
# BEGIN one-time envariable setup

# Create a scratch directory; our podman registry will run from here. We
# also use it for other temporary files like authfiles.
if [ -z "${PODMAN_LOGIN_WORKDIR}" ]; then
    export PODMAN_LOGIN_WORKDIR=$(mktemp -d --tmpdir=${BATS_TMPDIR:-${TMPDIR:-/tmp}} podman_bats_login.XXXXXX)
fi

# Randomly-generated username and password
if [ -z "${PODMAN_LOGIN_USER}" ]; then
    export PODMAN_LOGIN_USER="user$(random_string 4)"
    export PODMAN_LOGIN_PASS=$(random_string 15)
fi

# Randomly-assigned port in the 5xxx range
if [ -z "${PODMAN_LOGIN_REGISTRY_PORT}" ]; then
    for port in $(shuf -i 5000-5999);do
        if ! { exec 3<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then
            export PODMAN_LOGIN_REGISTRY_PORT=$port
            break
        fi
    done
fi

# Override any user-set path to an auth file
unset REGISTRY_AUTH_FILE

# END   one-time envariable setup
###############################################################################
# BEGIN filtering - none of these tests will work with podman-remote

function setup() {
    skip_if_remote "none of these tests work with podman-remote"

    basic_setup
}

# END   filtering - none of these tests will work with podman-remote
###############################################################################
# BEGIN first "test" - start a registry for use by other tests
#
# This isn't really a test: it's a helper that starts a local registry.
# Note that we're careful to use a root/runroot separate from our tests,
# so setup/teardown don't clobber our registry image.
#

@test "podman login [start registry]" {
    AUTHDIR=${PODMAN_LOGIN_WORKDIR}/auth
    mkdir -p $AUTHDIR

    # Pull registry image, but into a separate container storage
    mkdir -p ${PODMAN_LOGIN_WORKDIR}/root
    mkdir -p ${PODMAN_LOGIN_WORKDIR}/runroot
    PODMAN_LOGIN_ARGS="--root ${PODMAN_LOGIN_WORKDIR}/root --runroot ${PODMAN_LOGIN_WORKDIR}/runroot"
    # Give it three tries, to compensate for flakes
    run_podman ${PODMAN_LOGIN_ARGS} pull registry:2 ||
        run_podman ${PODMAN_LOGIN_ARGS} pull registry:2 ||
        run_podman ${PODMAN_LOGIN_ARGS} pull registry:2

    # Registry image needs a cert. Self-signed is good enough.
    CERT=$AUTHDIR/domain.crt
    if [ ! -e $CERT ]; then
        openssl req -newkey rsa:4096 -nodes -sha256 \
                -keyout $AUTHDIR/domain.key -x509 -days 2 \
                -out $AUTHDIR/domain.crt \
                -subj "/C=US/ST=Foo/L=Bar/O=Red Hat, Inc./CN=localhost"
    fi

    # Store credentials where container will see them
    if [ ! -e $AUTHDIR/htpasswd ]; then
        run_podman ${PODMAN_LOGIN_ARGS} run --rm                  \
                   --entrypoint htpasswd registry:2               \
                   -Bbn ${PODMAN_LOGIN_USER} ${PODMAN_LOGIN_PASS} \
                   > $AUTHDIR/htpasswd

        # In case $PODMAN_TEST_KEEP_LOGIN_REGISTRY is set, for testing later
        echo "${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS}" \
             > $AUTHDIR/htpasswd-plaintext
    fi

    # Run the registry container.
    run_podman '?' ${PODMAN_LOGIN_ARGS} rm -f registry
    run_podman ${PODMAN_LOGIN_ARGS} run -d \
               -p ${PODMAN_LOGIN_REGISTRY_PORT}:5000 \
               --name registry \
               -v $AUTHDIR:/auth:Z \
               -e "REGISTRY_AUTH=htpasswd" \
               -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
               -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
               -e REGISTRY_HTTP_TLS_CERTIFICATE=/auth/domain.crt \
               -e REGISTRY_HTTP_TLS_KEY=/auth/domain.key \
               registry:2
}

# END   first "test" - start a registry for use by other tests
###############################################################################
# BEGIN actual tests
# BEGIN primary podman login/push/pull tests

@test "podman login - basic test" {
    run_podman login --tls-verify=false \
               --username ${PODMAN_LOGIN_USER} \
               --password-stdin \
               localhost:${PODMAN_LOGIN_REGISTRY_PORT} <<<"${PODMAN_LOGIN_PASS}"
    is "$output" "Login Succeeded!" "output from podman login"

    # Now log out
    run_podman logout localhost:${PODMAN_LOGIN_REGISTRY_PORT}
    is "$output" "Removed login credentials for localhost:${PODMAN_LOGIN_REGISTRY_PORT}" \
       "output from podman logout"
}

@test "podman login - with wrong credentials" {
    registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}

    run_podman 125 login --tls-verify=false \
               --username ${PODMAN_LOGIN_USER} \
               --password-stdin \
               $registry <<< "x${PODMAN_LOGIN_PASS}"
    is "$output" \
       "Error: error logging into \"$registry\": invalid username/password" \
       'output from podman login'
}

@test "podman login - check generated authfile" {
    authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
    rm -f $authfile

    registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}

    run_podman login --authfile=$authfile \
        --tls-verify=false \
        --username ${PODMAN_LOGIN_USER} \
        --password ${PODMAN_LOGIN_PASS} \
        $registry

    # Confirm that authfile now exists
    test -e $authfile || \
        die "podman login did not create authfile $authfile"

    # Special bracket form needed because of colon in host:port
    run jq -r ".[\"auths\"][\"$registry\"][\"auth\"]" <$authfile
    is "$status" "0" "jq from $authfile"

    expect_userpass="${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS}"
    actual_userpass=$(base64 -d <<<"$output")
    is "$actual_userpass" "$expect_userpass" "credentials stored in $authfile"


    # Now log out and make sure credentials are removed
    run_podman logout --authfile=$authfile $registry

    run jq -r '.auths' <$authfile
    is "$status" "0" "jq from $authfile"
    is "$output" "{}" "credentials removed from $authfile"
}

# Some push tests
@test "podman push fail" {

    # Create an invalid authfile
    authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
    rm -f $authfile

    wrong_auth=$(base64 <<<"baduser:wrongpassword")
    cat >$authfile <<EOF
{
    "auths": {
            "localhost:${PODMAN_LOGIN_REGISTRY_PORT}": {
                    "auth": "$wrong_auth"
            }
    }
}
EOF

    run_podman 125 push --authfile=$authfile \
        --tls-verify=false $IMAGE \
        localhost:${PODMAN_LOGIN_REGISTRY_PORT}/badpush:1
    is "$output" ".*: unauthorized: authentication required" \
       "auth error on push"
}

@test "podman push ok" {
    # ARGH! We can't push $IMAGE (alpine_labels) to this registry; error is:
    #
    #    Writing manifest to image destination
    #    Error: Error copying image to the remote destination: Error writing manifest: Error uploading manifest latest to localhost:${PODMAN_LOGIN_REGISTRY_PORT}/okpush: received unexpected HTTP status: 500 Internal Server Error
    #
    # Root cause: something to do with v1/v2 s1/s2:
    #
    #   https://github.com/containers/skopeo/issues/651
    #

    run_podman pull busybox

    # Preserve its ID for later comparison against push/pulled image
    run_podman inspect --format '{{.Id}}' busybox
    id_busybox=$output

    destname=ok-$(random_string 10 | tr A-Z a-z)-ok
    # Use command-line credentials
    run_podman push --tls-verify=false \
               --creds ${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS} \
               busybox localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname

    # Yay! Pull it back
    run_podman pull --tls-verify=false \
               --creds ${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS} \
               localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname

    # Compare to original busybox
    run_podman inspect --format '{{.Id}}' $destname
    is "$output" "$id_busybox" "Image ID of pulled image == busybox"

    run_podman rmi busybox $destname
}

# END   primary podman login/push/pull tests
###############################################################################
# BEGIN cooperation with skopeo

# Skopeo helper - keep this separate, so we can test with different
# envariable settings
function _test_skopeo_credential_sharing() {
    if ! type -p skopeo; then
        skip "skopeo not available"
    fi

    registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}

    run_podman login "$@" --tls-verify=false \
               --username ${PODMAN_LOGIN_USER} \
               --password ${PODMAN_LOGIN_PASS} \
               $registry

    destname=skopeo-ok-$(random_string 10 | tr A-Z a-z)-ok
    echo "# skopeo copy ..."
    run skopeo copy "$@" \
        --format=v2s2 \
        --dest-tls-verify=false \
        containers-storage:$IMAGE \
        docker://$registry/$destname
    echo "$output"
    is "$status" "0" "skopeo copy - exit status"
    is "$output" ".*Copying blob .*"     "output of skopeo copy"
    is "$output" ".*Copying config .*"   "output of skopeo copy"
    is "$output" ".*Writing manifest .*" "output of skopeo copy"

    echo "# skopeo inspect ..."
    run skopeo inspect "$@" --tls-verify=false docker://$registry/$destname
    echo "$output"
    is "$status" "0" "skopeo inspect - exit status"

    got_name=$(jq -r .Name <<<"$output")
    is "$got_name" "$registry/$dest_name" "skopeo inspect -> Name"

    # Now try without a valid login; it should fail
    run_podman logout "$@" $registry
    echo "# skopeo inspect [with no credentials] ..."
    run skopeo inspect "$@" --tls-verify=false docker://$registry/$destname
    echo "$output"
    is "$status" "1" "skopeo inspect - exit status"
    is "$output" ".*: unauthorized: authentication required" \
       "auth error on skopeo inspect"
}

@test "podman login - shares credentials with skopeo - default auth file" {
    if is_rootless; then
        if [ -z "${XDG_RUNTIME_DIR}" ]; then
            skip "skopeo does not match podman when XDG_RUNTIME_DIR unset; #823"
        fi
    fi
    _test_skopeo_credential_sharing
}

@test "podman login - shares credentials with skopeo - via envariable" {
    skip "skopeo does not yet support REGISTRY_AUTH_FILE; #822"
    authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
    rm -f $authfile

    REGISTRY_AUTH_FILE=$authfile _test_skopeo_credential_sharing
    rm -f $authfile
}

@test "podman login - shares credentials with skopeo - via --authfile" {
    # Also test that command-line --authfile overrides envariable
    authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
    rm -f $authfile

    fake_authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
    rm -f $fake_authfile

    REGISTRY_AUTH_FILE=$authfile _test_skopeo_credential_sharing --authfile=$authfile

    if [ -e $fake_authfile ]; then
        die "REGISTRY_AUTH_FILE overrode command-line --authfile!"
    fi
    rm -f $authfile
}

# END   cooperation with skopeo
# END   actual tests
###############################################################################
# BEGIN teardown (remove the registry container)

@test "podman login [stop registry, clean up]" {
    # For manual debugging; user may request keeping the registry running
    if [ -n "${PODMAN_TEST_KEEP_LOGIN_REGISTRY}" ]; then
        skip "[leaving registry running by request]"
    fi

    run_podman --root    ${PODMAN_LOGIN_WORKDIR}/root   \
               --runroot ${PODMAN_LOGIN_WORKDIR}/runroot \
               rm -f registry
    run_podman --root    ${PODMAN_LOGIN_WORKDIR}/root   \
               --runroot ${PODMAN_LOGIN_WORKDIR}/runroot \
               rmi -a

    # By default, clean up
    if [ -z "${PODMAN_TEST_KEEP_LOGIN_WORKDIR}" ]; then
        rm -rf ${PODMAN_LOGIN_WORKDIR}
    fi

    # Make sure socket is closed
    if { exec 3<> /dev/tcp/127.0.0.1/${PODMAN_LOGIN_REGISTRY_PORT}; } &>/dev/null; then
        die "Socket still seems open"
    fi
}

# END   teardown (remove the registry container)
###############################################################################

# vim: filetype=sh