|
2 | 2 |
|
3 | 3 | #@testset "libgit2" begin
|
4 | 4 |
|
| 5 | +isdefined(:TestHelpers) || include(joinpath(dirname(@__FILE__), "TestHelpers.jl")) |
| 6 | +using TestHelpers |
| 7 | + |
5 | 8 | const LIBGIT2_MIN_VER = v"0.23.0"
|
6 | 9 |
|
7 | 10 | #########
|
@@ -567,6 +570,180 @@ mktempdir() do dir
|
567 | 570 | @test creds.user == creds_user
|
568 | 571 | @test creds.pass == creds_pass
|
569 | 572 | #end
|
| 573 | + |
| 574 | + #@testset "SSH" begin |
| 575 | + sshd_command = "" |
| 576 | + ssh_repo = joinpath(dir, "Example.SSH") |
| 577 | + if !is_windows() |
| 578 | + try |
| 579 | + # SSHD needs to be executed by its full absolute path |
| 580 | + sshd_command = strip(readstring(`which sshd`)) |
| 581 | + catch |
| 582 | + warn("Skipping SSH tests (Are `which` and `sshd` installed?)") |
| 583 | + end |
| 584 | + end |
| 585 | + if !isempty(sshd_command) |
| 586 | + mktempdir() do fakehomedir |
| 587 | + mkdir(joinpath(fakehomedir,".ssh")) |
| 588 | + # Unsetting the SSH agent serves two purposes. First, we make |
| 589 | + # sure that we don't accidentally pick up an existing agent, |
| 590 | + # and second we test that we fall back to using a key file |
| 591 | + # if the agent isn't present. |
| 592 | + withenv("HOME"=>fakehomedir,"SSH_AUTH_SOCK"=>nothing) do |
| 593 | + # Generate user file, first an unencrypted one |
| 594 | + wait(spawn(`ssh-keygen -N "" -C juliatest@localhost -f $fakehomedir/.ssh/id_rsa`)) |
| 595 | + |
| 596 | + # Generate host keys |
| 597 | + wait(spawn(`ssh-keygen -f $fakehomedir/ssh_host_rsa_key -N '' -t rsa`)) |
| 598 | + wait(spawn(`ssh-keygen -f $fakehomedir/ssh_host_dsa_key -N '' -t dsa`)) |
| 599 | + |
| 600 | + our_ssh_port = rand(13000:14000) # Chosen arbitrarily |
| 601 | + |
| 602 | + key_option = "AuthorizedKeysFile $fakehomedir/.ssh/id_rsa.pub" |
| 603 | + pidfile_option = "PidFile $fakehomedir/sshd.pid" |
| 604 | + sshp = agentp = nothing |
| 605 | + logfile = tempname() |
| 606 | + ssh_debug = false |
| 607 | + function spawn_sshd() |
| 608 | + debug_flags = ssh_debug ? `-d -d` : `` |
| 609 | + _p = open(logfile, "a") do logfilestream |
| 610 | + spawn(pipeline(pipeline(`$sshd_command |
| 611 | + -e -f /dev/null $debug_flags |
| 612 | + -h $fakehomedir/ssh_host_rsa_key |
| 613 | + -h $fakehomedir/ssh_host_dsa_key -p $our_ssh_port |
| 614 | + -o $pidfile_option |
| 615 | + -o 'Protocol 2' |
| 616 | + -o $key_option |
| 617 | + -o 'UsePrivilegeSeparation no' |
| 618 | + -o 'StrictModes no'`,STDOUT),stderr=logfilestream)) |
| 619 | + end |
| 620 | + # Give the SSH server 5 seconds to start up |
| 621 | + yield(); sleep(5) |
| 622 | + _p |
| 623 | + end |
| 624 | + sshp = spawn_sshd() |
| 625 | + |
| 626 | + TIOCSCTTY_str = "ccall(:ioctl, Void, (Cint, Cint, Int64), 0, |
| 627 | + (is_bsd() || is_apple()) ? 0x20007461 : is_linux() ? 0x540E : |
| 628 | + error(\"Fill in TIOCSCTTY for this OS here\"), 0)" |
| 629 | + |
| 630 | + # To fail rather than hang |
| 631 | + function killer_task(p, master) |
| 632 | + @async begin |
| 633 | + sleep(10) |
| 634 | + kill(p) |
| 635 | + if isopen(master) |
| 636 | + nb_available(master) > 0 && |
| 637 | + write(logfile, |
| 638 | + readavailable(master)) |
| 639 | + close(master) |
| 640 | + end |
| 641 | + end |
| 642 | + end |
| 643 | + |
| 644 | + try |
| 645 | + function try_clone(challenges = []) |
| 646 | + cmd = """ |
| 647 | + repo = nothing |
| 648 | + try |
| 649 | + $TIOCSCTTY_str |
| 650 | + reponame = "ssh://$(ENV["USER"])@localhost:$our_ssh_port$cache_repo" |
| 651 | + repo = LibGit2.clone(reponame, "$ssh_repo") |
| 652 | + catch err |
| 653 | + open("$logfile","a") do f |
| 654 | + println(f,"HOME: ",ENV["HOME"]) |
| 655 | + println(f, err) |
| 656 | + end |
| 657 | + finally |
| 658 | + finalize(repo) |
| 659 | + end |
| 660 | + """ |
| 661 | + # We try to be helpful by desparately looking for |
| 662 | + # a way to prompt the password interactively. Pretend |
| 663 | + # to be a TTY to suppress those shenanigans. Further, we |
| 664 | + # need to detach and change the controlling terminal with |
| 665 | + # TIOCSCTTY, since getpass opens the controlling terminal |
| 666 | + TestHelpers.with_fake_pty() do slave, master |
| 667 | + err = Base.Pipe() |
| 668 | + let p = spawn(detach( |
| 669 | + `$(Base.julia_cmd()) --startup-file=no -e $cmd`),slave,slave,STDERR) |
| 670 | + killer_task(p, master) |
| 671 | + for (challenge, response) in challenges |
| 672 | + readuntil(master, challenge) |
| 673 | + sleep(1) |
| 674 | + print(master, response) |
| 675 | + end |
| 676 | + sleep(2) |
| 677 | + wait(p) |
| 678 | + close(master) |
| 679 | + end |
| 680 | + end |
| 681 | + @test isfile(joinpath(ssh_repo,"testfile")) |
| 682 | + rm(ssh_repo, recursive = true) |
| 683 | + end |
| 684 | + |
| 685 | + # Should use the default files, no interaction required. |
| 686 | + try_clone() |
| 687 | + ssh_debug && (kill(sshp); sshp = spawn_sshd()) |
| 688 | + |
| 689 | + # Ok, now encrypt the file and test with that (this also |
| 690 | + # makes sure that we don't accidentally fall back to the |
| 691 | + # unencrypted version) |
| 692 | + wait(spawn(`ssh-keygen -p -N "xxxxx" -f $fakehomedir/.ssh/id_rsa`)) |
| 693 | + |
| 694 | + # Try with the encrypted file. Needs a password. |
| 695 | + try_clone(["Passphrase"=>"xxxxx\r\n"]) |
| 696 | + ssh_debug && (kill(sshp); sshp = spawn_sshd()) |
| 697 | + |
| 698 | + # Move the file. It should now ask for the location and |
| 699 | + # then the passphrase |
| 700 | + mv("$fakehomedir/.ssh/id_rsa","$fakehomedir/.ssh/id_rsa2") |
| 701 | + cp("$fakehomedir/.ssh/id_rsa.pub","$fakehomedir/.ssh/id_rsa2.pub") |
| 702 | + try_clone(["location"=>"$fakehomedir/.ssh/id_rsa2\n", |
| 703 | + "Passphrase"=>"xxxxx\n"]) |
| 704 | + mv("$fakehomedir/.ssh/id_rsa2","$fakehomedir/.ssh/id_rsa") |
| 705 | + rm("$fakehomedir/.ssh/id_rsa2.pub") |
| 706 | + |
| 707 | + # Ok, now start an agent |
| 708 | + agent_sock = tempname() |
| 709 | + agentp = spawn(`ssh-agent -a $agent_sock -d`) |
| 710 | + while stat(agent_sock).mode == 0 # Wait until the agent is started |
| 711 | + sleep(1) |
| 712 | + end |
| 713 | + |
| 714 | + # fake pty is required for the same reason as in try_clone |
| 715 | + # above |
| 716 | + withenv("SSH_AUTH_SOCK" => agent_sock) do |
| 717 | + TestHelpers.with_fake_pty() do slave, master |
| 718 | + cmd = """ |
| 719 | + $TIOCSCTTY_str |
| 720 | + run(pipeline(`ssh-add $fakehomedir/.ssh/id_rsa`, |
| 721 | + stderr = DevNull)) |
| 722 | + """ |
| 723 | + addp = spawn(detach(`$(Base.julia_cmd()) --startup-file=no -e $cmd`), |
| 724 | + slave, slave, STDERR) |
| 725 | + killer_task(addp, master) |
| 726 | + sleep(2) |
| 727 | + write(master, "xxxxx\n") |
| 728 | + wait(addp) |
| 729 | + end |
| 730 | + |
| 731 | + # Should now use the agent |
| 732 | + try_clone() |
| 733 | + end |
| 734 | + catch err |
| 735 | + println("SSHD logfile contents follows:") |
| 736 | + println(readstring(logfile)) |
| 737 | + rethrow(err) |
| 738 | + finally |
| 739 | + rm(logfile) |
| 740 | + sshp !== nothing && kill(sshp) |
| 741 | + agentp !== nothing && kill(agentp) |
| 742 | + end |
| 743 | + end |
| 744 | + end |
| 745 | + end |
| 746 | + #end |
570 | 747 | end
|
571 | 748 |
|
572 | 749 | #end
|
0 commit comments