#!/usr/bin/env bash

. "$(dirname "$0")/testlib.sh"

ensure_git_version_isnt $VERSION_LOWER "2.3.0"

begin_test "credentials with url-specific helper skips askpass"
(
  set -e

  reponame="url-specific-helper"
  setup_remote_repo "$reponame"

  clone_repo "$reponame" "$reponame"
  git config credential.useHttpPath false
  git config credential.helper ""
  git config credential.$GITSERVER.helper "lfstest"

  git lfs track "*.dat"
  echo "hello" > a.dat

  git add .gitattributes a.dat
  git commit -m "initial commit"

  # askpass is skipped
  GIT_ASKPASS="lfs-bad-cmd" GIT_TRACE=1 git push origin main 2>&1 | tee push.log

  [ "0" -eq "$(grep "filling with GIT_ASKPASS" push.log | wc -l)" ]
)
end_test

begin_test "credentials without useHttpPath, with bad path password"
(
  set -e

  reponame="no-httppath-bad-password"
  setup_remote_repo "$reponame"

  printf "path:wrong" > "$CREDSDIR/127.0.0.1--$reponame"

  clone_repo "$reponame" without-path
  git config credential.useHttpPath false
  git checkout -b without-path

  git lfs track "*.dat" 2>&1 | tee track.log
  grep "Tracking \"\*.dat\"" track.log

  printf "a" > a.dat
  git add a.dat
  git add .gitattributes
  git commit -m "add a.dat"

  GIT_TRACE=1 git push origin without-path 2>&1 | tee push.log
  grep "Uploading LFS objects: 100% (1/1), 1 B" push.log

  echo "approvals:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential approve" | wc -l)" ]
  echo "fills:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential fill" | wc -l)" ]

  echo "credential calls have no path:"
  credcalls="$(grep "creds: git credential" push.log)"
  [ "0" -eq "$(echo "$credcalls" | grep "no-httppath-bad-password" | wc -l)" ]
  expected="$(echo "$credcalls" | wc -l)"
  [ "$expected" -eq "$(printf "%s" "$credcalls" | grep '", "")' | wc -l)" ]
)
end_test

begin_test "credentials with url-specific useHttpPath, with bad path password"
(
  set -e

  reponame="url-specific-httppath-bad-password"
  setup_remote_repo "$reponame"

  printf "path:wrong" > "$CREDSDIR/127.0.0.1--$reponame"

  clone_repo "$reponame" with-url-specific-path
  git config credential.$GITSERVER.useHttpPath false
  git config lfs.locksverify false
  git checkout -b without-path

  git lfs track "*.dat" 2>&1 | tee track.log
  grep "Tracking \"\*.dat\"" track.log

  printf "a" > a.dat
  git add a.dat
  git add .gitattributes
  git commit -m "add a.dat"

  GIT_TRACE=1 git push origin without-path 2>&1 | tee push.log
  grep "Uploading LFS objects: 100% (1/1), 1 B" push.log

  echo "approvals:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential approve" | wc -l)" ]
  echo "fills:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential fill" | wc -l)" ]
)
end_test

begin_test "credentials with useHttpPath, with wrong password"
(
  set -e

  reponame="httppath-bad-password"
  setup_remote_repo "$reponame"

  printf "path:wrong" > "$CREDSDIR/127.0.0.1--$reponame"

  clone_repo "$reponame" with-path-wrong-pass
  git checkout -b with-path-wrong-pass

  git lfs track "*.dat" 2>&1 | tee track.log
  grep "Tracking \"\*.dat\"" track.log

  contents="a"
  contents_oid=$(calc_oid "$contents")

  printf "%s" "$contents" > a.dat
  git add a.dat
  git add .gitattributes
  git commit -m "add a.dat"

  GIT_TRACE=1 git push origin with-path-wrong-pass 2>&1 | tee push.log
  [ "0" = "$(grep -c "Uploading LFS objects: 100% (1/1), 0 B" push.log)" ]
  echo "approvals:"
  [ "0" -eq "$(cat push.log | grep "creds: git credential approve" | wc -l)" ]
  echo "fills:"
  [ "2" -eq "$(cat push.log | grep "creds: git credential fill" | wc -l)" ]
)
end_test

begin_test "credentials with useHttpPath, with correct password"
(
  set -e

  reponame="$(basename "$0" ".sh")"
  setup_remote_repo "$reponame"

  printf "path:$reponame" > "$CREDSDIR/127.0.0.1--$reponame"

  clone_repo "$reponame" with-path-correct-pass
  git checkout -b with-path-correct-pass

  git lfs track "*.dat" 2>&1 | tee track.log
  grep "Tracking \"\*.dat\"" track.log

  # creating new branch does not re-send any objects existing on other
  # remote branches anymore, generate new object, different from prev tests
  contents="b"
  contents_oid=$(calc_oid "$contents")

  printf "%s" "$contents" > b.dat
  git add b.dat
  git add .gitattributes
  git commit -m "add b.dat"

  GIT_TRACE=1 git push origin with-path-correct-pass 2>&1 | tee push.log
  grep "Uploading LFS objects: 100% (1/1), 1 B" push.log
  echo "approvals:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential approve" | wc -l)" ]
  echo "fills:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential fill" | wc -l)" ]
  echo "credential calls have path:"
  credcalls="$(grep "creds: git credential" push.log)"
  [ "0" -eq "$(echo "$credcalls" | grep '", "")' | wc -l)" ]
  expected="$(echo "$credcalls" | wc -l)"
  [ "$expected" -eq "$(printf "%s" "$credcalls" | grep "t-credentials" | wc -l)" ]
)
end_test

begin_test "git credential"
(
  set -e

  printf "git:server" > "$CREDSDIR/credential-test.com"
  printf "git:path" > "$CREDSDIR/credential-test.com--some-path"

  mkdir empty
  cd empty
  git init

  echo "protocol=http
host=credential-test.com
path=some/path" | GIT_TERMINAL_PROMPT=0 git credential fill > cred.log
  cat cred.log

  expected="protocol=http
host=credential-test.com
path=some/path
username=git
password=path"

  [ "$expected" = "$(cat cred.log)" ]

  git config credential.useHttpPath false

  echo "protocol=http
host=credential-test.com" | GIT_TERMINAL_PROMPT=0 git credential fill > cred.log
  cat cred.log

  expected="protocol=http
host=credential-test.com
username=git
password=server"
  [ "$expected" = "$(cat cred.log)" ]

  echo "protocol=http
host=credential-test.com
path=some/path" | GIT_TERMINAL_PROMPT=0 git credential fill > cred.log
  cat cred.log

  expected="protocol=http
host=credential-test.com
username=git
password=server"

  [ "$expected" = "$(cat cred.log)" ]
)
end_test


if [[ $(uname) == *"MINGW"* ]]; then
  NETRCFILE="$HOME/_netrc"
else
  NETRCFILE="$HOME/.netrc"
fi


begin_test "credentials from netrc"
(
  set -e

  printf "machine localhost\nlogin netrcuser\npassword netrcpass\n" >> "$NETRCFILE"
  echo $HOME
  echo "GITSERVER $GITSERVER"
  cat $NETRCFILE

  # prevent prompts on Windows particularly
  export SSH_ASKPASS=

  reponame="netrctest"
  setup_remote_repo "$reponame"

  clone_repo "$reponame" repo

  # Need a remote named "localhost" or 127.0.0.1 in netrc will interfere with the other auth
  git remote add "netrc" "$(echo $GITSERVER | sed s/127.0.0.1/localhost/)/netrctest"
  git lfs env

  git lfs track "*.dat"
  echo "push a" > a.dat
  git add .gitattributes a.dat
  git commit -m "add a.dat"

  GIT_TRACE=1 git lfs push netrc main 2>&1 | tee push.log
  grep "Uploading LFS objects: 100% (1/1), 7 B" push.log
  echo "any netrc credential calls:"
  [ "4" -eq "$(cat push.log | grep "netrc: git credential" | wc -l)" ]

  echo "any netrc credential fills:"
  [ "2" -eq "$(cat push.log | grep "netrc: git credential fill" | wc -l)" ]

  echo "any netrc credential approvals:"
  [ "2" -eq "$(cat push.log | grep "netrc: git credential approve" | wc -l)" ]
)
end_test

begin_test "credentials from netrc with unknown keyword"
(
  set -e

  printf "machine localhost\nlogin netrcuser\nnot-a-key something\npassword netrcpass\n" >> "$NETRCFILE"
  echo $HOME
  echo "GITSERVER $GITSERVER"
  cat $NETRCFILE

  # prevent prompts on Windows particularly
  export SSH_ASKPASS=

  reponame="netrctest"
  setup_remote_repo "$reponame"

  clone_repo "$reponame" repo2

  # Need a remote named "localhost" or 127.0.0.1 in netrc will interfere with the other auth
  git remote add "netrc" "$(echo $GITSERVER | sed s/127.0.0.1/localhost/)/netrctest"
  git lfs env

  git lfs track "*.dat"
  echo "push a" > a.dat
  git add .gitattributes a.dat
  git commit -m "add a.dat"

  GIT_TRACE=1 git lfs push netrc main 2>&1 | tee push.log
  grep "Uploading LFS objects: 100% (1/1), 7 B" push.log
  echo "any netrc credential calls:"
  [ "4" -eq "$(cat push.log | grep "netrc: git credential" | wc -l)" ]

  echo "any netrc credential fills:"
  [ "2" -eq "$(cat push.log | grep "netrc: git credential fill" | wc -l)" ]

  echo "any netrc credential approvals:"
  [ "2" -eq "$(cat push.log | grep "netrc: git credential approve" | wc -l)" ]
)
end_test

begin_test "credentials from netrc with bad password"
(
  set -e

  printf "machine localhost\nlogin netrcuser\npassword badpass\n" >> "$NETRCFILE"
  echo $HOME
  echo "GITSERVER $GITSERVER"
  cat $NETRCFILE

  # prevent prompts on Windows particularly
  export SSH_ASKPASS=

  reponame="netrctest"
  setup_remote_repo "$reponame"

  clone_repo "$reponame" repo3

  # Need a remote named "localhost" or 127.0.0.1 in netrc will interfere with the other auth
  git remote add "netrc" "$(echo $GITSERVER | sed s/127.0.0.1/localhost/)/netrctest"
  git lfs env

  git lfs track "*.dat"
  echo "push a" > a.dat
  git add .gitattributes a.dat
  git commit -m "add a.dat"

  git push netrc main 2>&1 | tee push.log
  [ "0" = "$(grep -c "Uploading LFS objects: 100% (1/1), 7 B" push.log)" ]
)
end_test

begin_test "credentials with bad netrc creds will retry"
(
  set -e

  printf "machine localhost\nlogin netrcuser\npassword badpassretry\n" >> "$NETRCFILE"
  echo $HOME
  echo "GITSERVER $GITSERVER"
  cat $NETRCFILE

  # prevent prompts on Windows particularly
  export SSH_ASKPASS=

  # ensure we provide the correct creds through ASKPASS so we can fall back
  # when .netrc fails
  export LFS_ASKPASS_USERNAME="netrcuser"
  export LFS_ASKPASS_PASSWORD="netrcpass"

  reponame="netrctest"
  setup_remote_repo "$reponame"

  clone_repo "$reponame" repo4

  # Need a remote named "localhost" or 127.0.0.1 in netrc will interfere with the other auth
  git remote add "netrc" "$(echo $GITSERVER | sed s/127.0.0.1/localhost/)/netrctest"
  git lfs env

  git lfs track "*.dat"
  echo "push a" > a.dat
  git add .gitattributes a.dat
  git commit -m "add a.dat"

  GIT_TRACE=1 GIT_ASKPASS="lfs-askpass" git push netrc main 2>&1 | tee push.log
  grep -c "Uploading LFS objects: 100% (1/1), 7 B" push.log

  # netrc credentials should be attempted then rejected for the lock request
  echo "netrc credentials attempted:"
  [ "1" -eq "$(cat push.log | grep "netrc: git credential fill" | wc -l)" ]

  echo "netrc credentials rejected:"
  [ "1" -eq "$(cat push.log | grep "netrc: git credential reject" | wc -l)" ]

  # credhelper should then use askpass to find the proper credentials, which
  # should be successful
  echo "askpass credentials attempted:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential fill" | wc -l)" ]

  echo "askpass credentials approved:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential approve" | wc -l)" ]

  # askpass creds should be cached and used for the batch request
  echo "cached credentials used:"
  [ "1" -eq "$(cat push.log | grep "creds: git credential cache" | wc -l)" ]
)
end_test

begin_test "credentials from lfs.url"
(
  set -e

  reponame="requirecreds-lfsurl"
  setup_remote_repo "$reponame"
  clone_repo "$reponame" "$reponame"

  git lfs track "*.dat"
  echo "push a" > a.dat
  git add .gitattributes a.dat
  git commit -m "add a.dat"

  echo "bad push"
  git lfs env
  git lfs push origin main 2>&1 | tee push.log
  grep "Uploading LFS objects:   0% (0/1), 0 B" push.log

  echo "good push"
  gitserverhost=$(echo "$GITSERVER" | cut -d'/' -f3)
  git config lfs.url http://requirecreds:pass@$gitserverhost/$reponame.git/info/lfs
  git lfs env
  GIT_TRACE=1 git lfs push origin main 2>&1 | tee push.log
  # A 401 indicates URL access mode for the /storage endpoint
  # was used instead of for the lfsapi endpoint
  grep "HTTP: 401" push.log
  # Ensure we didn't make a second batch request, which means the request
  # was successfully retried internally
  [ ! "$(grep "tq: retrying object" push.log)" ]
  grep "Uploading LFS objects:   0% (0/1), 0 B" push.log

  echo "bad fetch"
  rm -rf .git/lfs/objects
  git config lfs.url http://$gitserverhost/$reponame.git/info/lfs
  git lfs env
  git lfs fetch --all 2>&1 | tee fetch.log
  grep "Downloading LFS objects:   0% (0/1), 0 B" fetch.log

  echo "good fetch"
  rm -rf .git/lfs/objects
  git config lfs.url http://requirecreds:pass@$gitserverhost/$reponame.git/info/lfs
  git lfs env
  GIT_TRACE=1 git lfs fetch --all 2>&1 | tee fetch.log
  # No 401 should occur as we've already set an access mode for the
  # storage endpoint during the push
  [ ! "$(grep "HTTP: 401" fetch.log)" ]
  git lfs fsck

  echo "good fetch, setting access mode"
  rm -rf .git/lfs/objects
  git config lfs.url http://requirecreds:pass@$gitserverhost/$reponame.git/info/lfs
  git config lfs.http://$gitserverhost/storage/.access "None"
  git lfs env
  GIT_TRACE=1 git lfs fetch --all 2>&1 | tee fetch.log
  # A 401 indicates URL access mode for the /storage endpoint
  # was used instead of for the lfsapi endpoint
  grep "HTTP: 401" fetch.log
  # Ensure we didn't make a second batch request, which means the request
  # was successfully retried internally
  [ ! "$(grep "tq: retrying object" fetch.log)" ]

  git lfs fsck
)
end_test

begin_test "credentials from remote.origin.url"
(
  set -e

  reponame="requirecreds-remoteurl"
  setup_remote_repo "$reponame"
  clone_repo "$reponame" "$reponame"

  git lfs track "*.dat"
  echo "push b" > b.dat
  git add .gitattributes b.dat
  git commit -m "add b.dat"

  echo "bad push"
  git lfs env
  git lfs push origin main 2>&1 | tee push.log
  grep "Uploading LFS objects:   0% (0/1), 0 B" push.log

  echo "good push"
  gitserverhost=$(echo "$GITSERVER" | cut -d'/' -f3)
  git config remote.origin.url http://requirecreds:pass@$gitserverhost/$reponame.git
  git lfs env
  GIT_TRACE=1 git lfs push origin main 2>&1 | tee push.log
  # A 401 indicates URL access mode for the /storage endpoint
  # was used instead of for the lfsapi endpoint
  grep "HTTP: 401" push.log
  # Ensure we didn't make a second batch request, which means the request
  # was successfully retried internally
  [ ! "$(grep "tq: retrying object" push.log)" ]
  grep "Uploading LFS objects: 100% (1/1), 7 B" push.log

  echo "bad fetch"
  rm -rf .git/lfs/objects
  git config remote.origin.url http://$gitserverhost/$reponame.git
  git lfs env
  git lfs fetch --all 2>&1 | tee fetch.log
  # Missing authentication causes `git lfs fetch` to fail before the progress
  # meter is printed to the TTY.

  echo "good fetch"
  rm -rf .git/lfs/objects
  git config remote.origin.url http://requirecreds:pass@$gitserverhost/$reponame.git
  git lfs env
  GIT_TRACE=1 git lfs fetch --all 2>&1 | tee fetch.log
  # No 401 should occur as we've already set an access mode for the
  # storage endpoint during the push
  [ ! "$(grep "HTTP: 401" fetch.log)" ]
  git lfs fsck

  echo "good fetch, setting access mode"
  rm -rf .git/lfs/objects
  git config remote.origin.url http://requirecreds:pass@$gitserverhost/$reponame.git
  git config lfs.http://$gitserverhost/storage/.access "None"
  git lfs env
  GIT_TRACE=1 git lfs fetch --all 2>&1 | tee fetch.log
  # A 401 indicates URL access mode for the /storage endpoint
  # was used instead of for the lfsapi endpoint
  grep "HTTP: 401" fetch.log
  # Ensure we didn't make a second batch request, which means the request
  # was successfully retried internally
  [ ! "$(grep "tq: retrying object" fetch.log)" ]

  git lfs fsck
)
end_test
