{"thread":{"id":"43703","subject":"[PATCH] git-svn: handle authentication without relying on cached tokens on disk","startedAt":"2006-11-24T09:38:04Z","lastAt":"2006-11-24T09:38:04Z","messageCount":1,"participants":["Eric Wong"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"296986","messageId":"11643610841150-git-send-email-normalperson@yhbt.net","threadId":"43703","inReplyTo":null,"subject":"[PATCH] git-svn: handle authentication without relying on cached tokens on disk","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-11-24T09:38:04Z","receivedAt":"2006-11-24T09:38:04Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This is mostly gleaned off SVN::Mirror, with added support for\n--no-auth-cache and --config-dir.\n\nEven with this patch, git-svn does not yet support repositories\nwhere the user only has partial read permissions.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n---\n git-svn.perl |  156 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 files changed, 148 insertions(+), 8 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex bb8935a..47cd3e2 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -39,7 +39,7 @@ memoize('revisions_eq');\n memoize('cmt_metadata');\n memoize('get_commit_time');\n \n-my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);\n+my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib, $AUTH_BATON, $AUTH_CALLBACKS);\n \n sub nag_lib {\n \tprint STDERR <<EOF;\n@@ -66,7 +66,8 @@ my ($_revision,$_stdin,$_no_ignore_ext,$\n \t$_template, $_shared, $_no_default_regex, $_no_graft_copy,\n \t$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,\n \t$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,\n-\t$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive);\n+\t$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,\n+\t$_username, $_config_dir, $_no_auth_cache);\n my (@_branch_from, %tree_map, %users, %rusers, %equiv);\n my ($_svn_co_url_revs, $_svn_pg_peg_revs);\n my @repo_path_split_cache;\n@@ -79,6 +80,9 @@ my %fc_opts = ( 'no-ignore-externals' =>\n \t\t'repack:i' => \\$_repack,\n \t\t'no-metadata' => \\$_no_metadata,\n \t\t'quiet|q' => \\$_q,\n+\t\t'username=s' => \\$_username,\n+\t\t'config-dir=s' => \\$_config_dir,\n+\t\t'no-auth-cache' => \\$_no_auth_cache,\n \t\t'ignore-nodate' => \\$_ignore_nodate,\n \t\t'repack-flags|repack-args|repack-opts=s' => \\$_repack_flags);\n \n@@ -2683,18 +2687,154 @@ sub libsvn_load {\n \t\tmy $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.\n \t\t\t\t\t$SVN::Node::dir.$SVN::Node::unknown.\n \t\t\t\t\t$SVN::Node::none.$SVN::Node::file.\n-\t\t\t\t\t$SVN::Node::dir.$SVN::Node::unknown;\n+\t\t\t\t\t$SVN::Node::dir.$SVN::Node::unknown.\n+\t\t\t\t\t$SVN::Auth::SSL::CNMISMATCH.\n+\t\t\t\t\t$SVN::Auth::SSL::NOTYETVALID.\n+\t\t\t\t\t$SVN::Auth::SSL::EXPIRED.\n+\t\t\t\t\t$SVN::Auth::SSL::UNKNOWNCA.\n+\t\t\t\t\t$SVN::Auth::SSL::OTHER;\n \t\t1;\n \t};\n }\n \n+sub _simple_prompt {\n+\tmy ($cred, $realm, $default_username, $may_save, $pool) = @_;\n+\t$may_save = undef if $_no_auth_cache;\n+\t$default_username = $_username if defined $_username;\n+\tif (defined $default_username && length $default_username) {\n+\t\tif (defined $realm && length $realm) {\n+\t\t\tprint \"Authentication realm: $realm\\n\";\n+\t\t}\n+\t\t$cred->username($default_username);\n+\t} else {\n+\t\t_username_prompt($cred, $realm, $may_save, $pool);\n+\t}\n+\t$cred->password(_read_password(\"Password for '\" .\n+\t                               $cred->username . \"': \", $realm));\n+\t$cred->may_save($may_save);\n+\t$SVN::_Core::SVN_NO_ERROR;\n+}\n+\n+sub _ssl_server_trust_prompt {\n+\tmy ($cred, $realm, $failures, $cert_info, $may_save, $pool) = @_;\n+\t$may_save = undef if $_no_auth_cache;\n+\tprint \"Error validating server certificate for '$realm':\\n\";\n+\tif ($failures & $SVN::Auth::SSL::UNKNOWNCA) {\n+\t\tprint \" - The certificate is not issued by a trusted \",\n+\t\t      \"authority. Use the\\n\",\n+\t              \"   fingerprint to validate the certificate manually!\\n\";\n+\t}\n+\tif ($failures & $SVN::Auth::SSL::CNMISMATCH) {\n+\t\tprint \" - The certificate hostname does not match.\\n\";\n+\t}\n+\tif ($failures & $SVN::Auth::SSL::NOTYETVALID) {\n+\t\tprint \" - The certificate is not yet valid.\\n\";\n+\t}\n+\tif ($failures & $SVN::Auth::SSL::EXPIRED) {\n+\t\tprint \" - The certificate has expired.\\n\";\n+\t}\n+\tif ($failures & $SVN::Auth::SSL::OTHER) {\n+\t\tprint \" - The certificate has an unknown error.\\n\";\n+\t}\n+\tprintf( \"Certificate information:\\n\".\n+\t        \" - Hostname: %s\\n\".\n+\t        \" - Valid: from %s until %s\\n\".\n+\t        \" - Issuer: %s\\n\".\n+\t        \" - Fingerprint: %s\\n\",\n+\t        map $cert_info->$_, qw(hostname valid_from valid_until\n+\t                               issuer_dname fingerprint) );\n+\tmy $choice;\n+prompt:\n+\tprint $may_save ?\n+\t      \"(R)eject, accept (t)emporarily or accept (p)ermanently? \" :\n+\t      \"(R)eject or accept (t)emporarily? \";\n+\t$choice = lc(substr(<STDIN> || 'R', 0, 1));\n+\tif ($choice =~ /^t$/i) {\n+\t\t$cred->may_save(undef);\n+\t} elsif ($choice =~ /^r$/i) {\n+\t\treturn -1;\n+\t} elsif ($may_save && $choice =~ /^p$/i) {\n+\t\t$cred->may_save($may_save);\n+\t} else {\n+\t\tgoto prompt;\n+\t}\n+\t$cred->accepted_failures($failures);\n+\t$SVN::_Core::SVN_NO_ERROR;\n+}\n+\n+sub _ssl_client_cert_prompt {\n+\tmy ($cred, $realm, $may_save, $pool) = @_;\n+\t$may_save = undef if $_no_auth_cache;\n+\tprint \"Client certificate filename: \";\n+\tchomp(my $filename = <STDIN>);\n+\t$cred->cert_file($filename);\n+\t$cred->may_save($may_save);\n+\t$SVN::_Core::SVN_NO_ERROR;\n+}\n+\n+sub _ssl_client_cert_pw_prompt {\n+\tmy ($cred, $realm, $may_save, $pool) = @_;\n+\t$may_save = undef if $_no_auth_cache;\n+\t$cred->password(_read_password(\"Password: \", $realm));\n+\t$cred->may_save($may_save);\n+\t$SVN::_Core::SVN_NO_ERROR;\n+}\n+\n+sub _username_prompt {\n+\tmy ($cred, $realm, $may_save, $pool) = @_;\n+\t$may_save = undef if $_no_auth_cache;\n+\tif (defined $realm && length $realm) {\n+\t\tprint \"Authentication realm: $realm\\n\";\n+\t}\n+\tmy $username;\n+\tif (defined $_username) {\n+\t\t$username = $_username;\n+\t} else {\n+\t\tprint \"Username: \";\n+\t\tchomp($username = <STDIN>);\n+\t}\n+\t$cred->username($username);\n+\t$cred->may_save($may_save);\n+\t$SVN::_Core::SVN_NO_ERROR;\n+}\n+\n+sub _read_password {\n+\tmy ($prompt, $realm) = @_;\n+\tprint $prompt;\n+\trequire Term::ReadKey;\n+\tTerm::ReadKey::ReadMode('noecho');\n+\tmy $password = '';\n+\twhile (defined(my $key = Term::ReadKey::ReadKey(0))) {\n+\t\tlast if $key =~ /[\\012\\015]/; # \\n\\r\n+\t\t$password .= $key;\n+\t}\n+\tTerm::ReadKey::ReadMode('restore');\n+\tprint \"\\n\";\n+\t$password;\n+}\n+\n sub libsvn_connect {\n \tmy ($url) = @_;\n-\tmy $auth = SVN::Core::auth_open([SVN::Client::get_simple_provider(),\n-\t\t\t  SVN::Client::get_ssl_server_trust_file_provider(),\n-\t\t\t  SVN::Client::get_username_provider()]);\n-\tmy $s = eval { SVN::Ra->new(url => $url, auth => $auth) };\n-\treturn $s;\n+\tif (!$AUTH_BATON || !$AUTH_CALLBACKS) {\n+\t\tSVN::_Core::svn_config_ensure($_config_dir, undef);\n+\t\t($AUTH_BATON, $AUTH_CALLBACKS) = SVN::Core::auth_open_helper([\n+\t\t    SVN::Client::get_simple_provider(),\n+\t\t    SVN::Client::get_ssl_server_trust_file_provider(),\n+\t\t    SVN::Client::get_simple_prompt_provider(\n+\t\t      \\&_simple_prompt, 2),\n+\t\t    SVN::Client::get_ssl_client_cert_prompt_provider(\n+\t\t      \\&_ssl_client_cert_prompt, 2),\n+\t\t    SVN::Client::get_ssl_client_cert_pw_prompt_provider(\n+\t\t      \\&_ssl_client_cert_pw_prompt, 2),\n+\t\t    SVN::Client::get_username_provider(),\n+\t\t    SVN::Client::get_ssl_server_trust_prompt_provider(\n+\t\t      \\&_ssl_server_trust_prompt),\n+\t\t    SVN::Client::get_username_prompt_provider(\n+\t\t      \\&_username_prompt, 2),\n+\t\t  ]);\n+\t}\n+\tSVN::Ra->new(url => $url, auth => $AUTH_BATON,\n+\t             auth_provider_callbacks => $AUTH_CALLBACKS);\n }\n \n sub libsvn_get_file {\n-- \n1.4.4.1.g22a08\n"}]}