- CredentialManager added
authorJan Kupec <jkupec@suse.cz>
Mon, 8 Sep 2008 15:11:27 +0000 (15:11 +0000)
committerJan Kupec <jkupec@suse.cz>
Mon, 8 Sep 2008 15:11:27 +0000 (15:11 +0000)
- MediaCurl adapted to use CredentialManager
- AuthData: url added (the credentials are tied to URL anyway)

14 files changed:
tests/CMakeLists.txt
tests/media/CMakeLists.txt
tests/media/CredentialFileReader_test.cc [new file with mode: 0644]
tests/media/CredentialManager_test.cc [new file with mode: 0644]
tests/media/data/credentials [new file with mode: 0644]
zypp/CMakeLists.txt
zypp/media/CredentialFileReader.cc [new file with mode: 0644]
zypp/media/CredentialFileReader.h [new file with mode: 0644]
zypp/media/CredentialManager.cc [new file with mode: 0644]
zypp/media/CredentialManager.h [new file with mode: 0644]
zypp/media/MediaCurl.cc
zypp/media/MediaCurl.h
zypp/media/MediaUserAuth.cc
zypp/media/MediaUserAuth.h

index 62356e7..4912671 100644 (file)
@@ -2,7 +2,7 @@ ENABLE_TESTING()
 
 ADD_DEFINITIONS( -DTESTS_SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}" -DTESTS_BUILD_DIR="${CMAKE_CURRENT_BINARY_DIR}" )
 
-#ADD_SUBDIRECTORY( media EXCLUDE_FROM_ALL )
+ADD_SUBDIRECTORY( media )
 ADD_SUBDIRECTORY( zypp )
 ADD_SUBDIRECTORY( parser )
 ADD_SUBDIRECTORY( repo )
index ac4196c..185787e 100644 (file)
@@ -1,2 +1,3 @@
+ADD_TESTS(CredentialManager CredentialFileReader)
 
-ADD_TESTS(media1 media2 media3 media4 file_exists throw_if_not_exists)
\ No newline at end of file
+#ADD_TESTS(media1 media2 media3 media4 file_exists throw_if_not_exists)
diff --git a/tests/media/CredentialFileReader_test.cc b/tests/media/CredentialFileReader_test.cc
new file mode 100644 (file)
index 0000000..013c5c8
--- /dev/null
@@ -0,0 +1,36 @@
+#include <iostream>
+#include <boost/test/auto_unit_test.hpp>
+#include <set>
+
+#include "zypp/Url.h"
+#include "zypp/media/MediaUserAuth.h"
+
+#include "zypp/media/CredentialFileReader.h"
+
+using std::cout;
+using std::endl;
+using namespace zypp;
+using namespace zypp::media;
+
+typedef std::set<AuthData_Ptr> CredentialSet;
+
+struct CredCollector
+{
+  bool collect(AuthData_Ptr & cred)
+  {
+    cout << "got: " << endl << *cred << endl;
+    creds.insert(cred);
+    return true;
+  }
+
+  CredentialSet creds;
+};
+
+BOOST_AUTO_TEST_CASE(read_cred)
+{
+  CredCollector collector;
+  CredentialFileReader reader(TESTS_SRC_DIR "/media/data/credentials",
+    bind( &CredCollector::collect, &collector, _1 ));
+
+  BOOST_CHECK(collector.creds.size() == 2);
+}
diff --git a/tests/media/CredentialManager_test.cc b/tests/media/CredentialManager_test.cc
new file mode 100644 (file)
index 0000000..59e452b
--- /dev/null
@@ -0,0 +1,42 @@
+#include <iostream>
+#include <boost/test/auto_unit_test.hpp>
+
+#include "zypp/Url.h"
+
+#include "zypp/media/CredentialManager.h"
+
+using std::cout;
+using std::endl;
+using namespace zypp;
+using namespace zypp::media;
+
+
+BOOST_AUTO_TEST_CASE(read_cred_for_url)
+{
+  CredManagerOptions opts;
+  opts.globalCredFilePath = TESTS_SRC_DIR "/media/data/credentials";
+  opts.userCredFilePath = Pathname();
+
+  CredentialManager cm(opts);
+  Url url("https://drink.it/repo/roots");
+
+  AuthData_Ptr credentials = cm.getCred(url);
+
+/*
+  cout << "credentials:";
+  if (credentials)
+    cout << *credentials;
+  else
+    cout << "(null)";
+  cout << endl;
+*/
+
+  BOOST_CHECK(credentials->username() == "ginger");
+  BOOST_CHECK(credentials->password() == "ale");
+  
+  Url url2("ftp://maria@weprovidesoft.fr/download/opensuse/110");
+  credentials = cm.getCred(url2);
+
+  BOOST_CHECK(credentials->username() == "maria");
+  BOOST_CHECK(credentials->password() == "antoin");
+}
diff --git a/tests/media/data/credentials b/tests/media/data/credentials
new file mode 100644 (file)
index 0000000..bf638c9
--- /dev/null
@@ -0,0 +1,4 @@
+https://ginger:ale@drink.it/repo/roots
+
+weirdo@invalid.record
+ftp://maria:antoin@weprovidesoft.fr/download/opensuse/110
\ No newline at end of file
index f1b317f..c5dd5b1 100644 (file)
@@ -258,6 +258,8 @@ SET( zypp_media_SRCS
   media/MediaISO.cc
   media/MediaManager.cc
   media/MediaUserAuth.cc
+  media/CredentialFileReader.cc
+  media/CredentialManager.cc
   media/CurlConfig.cc
 )
 
@@ -278,6 +280,8 @@ SET( zypp_media_HEADERS
   media/MediaUserAuth.h
   media/Mount.h
   media/ProxyInfo.h
+  media/CredentialFileReader.h
+  media/CredentialManager.h
   media/CurlConfig.h
 )
 
diff --git a/zypp/media/CredentialFileReader.cc b/zypp/media/CredentialFileReader.cc
new file mode 100644 (file)
index 0000000..7f87bb4
--- /dev/null
@@ -0,0 +1,74 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialFileReader.cc
+ *
+ */
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/IOStream.h"
+
+#include "zypp/media/CredentialFileReader.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialFileReader 
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredentialFileReader::CredentialFileReader(
+      const Pathname & crfile,
+      const ProcessCredentials & callback)
+  {
+    InputStream is(crfile);
+
+    for(iostr::EachLine in(is); in; in.next())
+    {
+      try
+      {
+        Url storedUrl(*in);
+
+        AuthData_Ptr credentials;
+        credentials.reset(
+          new AuthData(storedUrl));
+
+        if (credentials->valid())
+          callback(credentials);
+        else
+          // report invalid record
+          DBG << "invalid record: " << *in << endl;
+      }
+      catch (const url::UrlException &)
+      {} // not a URL
+      //! \todo this will need to be a bit more sophisticated to be able to pinpoint bad records
+    }
+  }
+
+  CredentialFileReader::~CredentialFileReader()
+  {}
+
+
+    /////////////////////////////////////////////////////////////////
+  } // media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // zypp
+///////////////////////////////////////////////////////////////////
+
diff --git a/zypp/media/CredentialFileReader.h b/zypp/media/CredentialFileReader.h
new file mode 100644 (file)
index 0000000..fba8e60
--- /dev/null
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialFileReader.h
+ *
+ */
+#ifndef ZYPP_MEDIA_CREDENTIALFILEREADER_H
+#define ZYPP_MEDIA_CREDENTIALFILEREADER_H
+
+#include "zypp/base/Function.h"
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+
+#include "zypp/media/MediaUserAuth.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+  namespace media
+  { /////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialFileReader 
+  //
+  class CredentialFileReader
+  {
+  public:
+    /**
+      * Callback definition.
+      * First parameter is the \ref Url with which the credentials are
+      * associated, the second are the credentials.
+      *
+      * Return false from the callback to get a \ref AbortRequestException
+      * to be thrown and the processing to be cancelled.
+      */
+    typedef function<bool(AuthData_Ptr &)> ProcessCredentials;
+
+    CredentialFileReader(const Pathname & crfile,
+                         const ProcessCredentials & callback);
+    ~CredentialFileReader();
+  private:
+    ProcessCredentials _callback;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+
+    /////////////////////////////////////////////////////////////////
+  } // media
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // zypp
+///////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_MEDIA_CREDENTIALFILEREADER_H */
diff --git a/zypp/media/CredentialManager.cc b/zypp/media/CredentialManager.cc
new file mode 100644 (file)
index 0000000..747b01c
--- /dev/null
@@ -0,0 +1,210 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialManager.h
+ *
+ */
+#include <iostream>
+
+#include "zypp/base/Function.h"
+#include "zypp/base/Logger.h"
+
+#include "zypp/media/CredentialFileReader.h"
+
+#include "zypp/media/CredentialManager.h"
+
+using namespace std;
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp 
+{ ////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////
+  namespace media
+  { ////////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredManagerOptions 
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredManagerOptions::CredManagerOptions(const Pathname & rootdir)
+    : globalCredFilePath(rootdir / "/etc/zypp/credentials")
+  {
+    char * homedir = getenv("HOME");
+    if (homedir)
+      userCredFilePath = rootdir / homedir / ".zypp/credentials"; 
+  }
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager::Impl 
+  //
+  struct CredentialManager::Impl
+  {
+    Impl(const CredManagerOptions & options);
+
+    ~Impl()
+    {}
+
+    
+    bool processCredentials(AuthData_Ptr & cred);
+
+    AuthData_Ptr getCred(const Url & url);
+
+    CredManagerOptions _options;
+
+    CredentialSet _credsGlobal;
+    CredentialSet _credsUser;
+    CredentialSet _credsTmp;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager::Impl 
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredentialManager::Impl::Impl(const CredManagerOptions & options)
+    : _options(options)
+  {
+    CredentialFileReader(
+        _options.globalCredFilePath,
+        bind(&Impl::processCredentials, this, _1));
+    _credsGlobal = _credsTmp; _credsTmp.clear();
+    DBG << "Got " << _credsGlobal.size() << " global records." << endl;
+
+    if (!_options.userCredFilePath.empty())
+    {
+      CredentialFileReader(
+          _options.userCredFilePath,
+          bind(&Impl::processCredentials, this, _1));
+      _credsUser = _credsTmp; _credsTmp.clear();
+      DBG << "Got " << _credsUser.size() << " user records." << endl;
+    }
+  }
+
+
+  bool CredentialManager::Impl::processCredentials(AuthData_Ptr & cred)
+  {
+    _credsTmp.insert(cred);
+    return true;
+  }
+
+  static AuthData_Ptr findIn(const CredentialManager::CredentialSet & set,
+                             const Url & url,
+                             url::ViewOption vopt)
+  {
+    for(CredentialManager::CredentialIterator it = set.begin(); it != set.end(); ++it)
+    {
+      if (url.asString(vopt) == (*it)->url().asString(vopt))
+        return *it;
+    }
+    
+    return AuthData_Ptr();
+  }
+
+
+  AuthData_Ptr CredentialManager::Impl::getCred(const Url & url)
+  {
+    AuthData_Ptr result;
+
+    // compare the urls via asString(), but ignore password
+    // default url::ViewOption will take care of that.
+    // operator==(Url,Url) compares the whole Url
+
+    // if the wanted URL does not contain username, ignore that, too
+    url::ViewOption vopt;
+    if (url.getUsername().empty())
+      vopt = vopt - url::ViewOption::WITH_USERNAME;
+
+    // search in global credentials
+    result = findIn(_credsGlobal, url, vopt);
+
+    // search in home credentials
+    if (!result)
+      result = findIn(_credsUser, url, vopt);
+
+    if (result)
+      DBG << "Found credentials for '" << url << "':" << endl << *result;
+    else
+      DBG << "No credentials for '" << url << "'" << endl;
+
+    return result;
+  }
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager 
+  //
+  //////////////////////////////////////////////////////////////////////
+
+  CredentialManager::CredentialManager(const CredManagerOptions & opts)
+    : _pimpl(new Impl(opts))
+  {}
+
+  AuthData_Ptr CredentialManager::getCred(const Url & url)
+  { return _pimpl->getCred(url); }
+
+
+  void CredentialManager::save(const AuthData & cred, bool global)
+  { global ? saveInGlobal(cred) : saveInUser(cred); }
+
+  void CredentialManager::saveInGlobal(const AuthData & cred)
+  {
+    //! \todo
+  }
+
+  void CredentialManager::saveInUser(const AuthData & cred)
+  {
+    //! \todo
+  }
+
+  void saveIn(const AuthData &, const Pathname & credFile)
+  {
+    //! \todo
+  }
+
+
+  CredentialManager::CredentialIterator CredentialManager::credsGlobalBegin() const
+  { return _pimpl->_credsGlobal.begin(); }
+
+  CredentialManager::CredentialIterator CredentialManager::credsGlobalEnd() const
+  { return _pimpl->_credsGlobal.end(); }
+
+  CredentialManager::CredentialSize CredentialManager::credsGlobalSize() const
+  { return _pimpl->_credsGlobal.size(); }
+
+  bool CredentialManager::credsGlobalEmpty() const
+  { return _pimpl->_credsGlobal.empty(); }
+
+
+  CredentialManager::CredentialIterator CredentialManager::credsUserBegin() const
+  { return _pimpl->_credsUser.begin(); }
+
+  CredentialManager::CredentialIterator CredentialManager::credsUserEnd() const
+  { return _pimpl->_credsUser.end(); }
+
+  CredentialManager::CredentialSize CredentialManager::credsUserSize() const
+  { return _pimpl->_credsUser.size(); }
+
+  bool CredentialManager::credsUserEmpty() const
+  { return _pimpl->_credsUser.empty(); }
+
+
+    ////////////////////////////////////////////////////////////////////
+  } // media
+  //////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // zypp
+//////////////////////////////////////////////////////////////////////
diff --git a/zypp/media/CredentialManager.h b/zypp/media/CredentialManager.h
new file mode 100644 (file)
index 0000000..5d2e190
--- /dev/null
@@ -0,0 +1,118 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/media/CredentialManager.h
+ *
+ */
+#ifndef ZYPP_MEDIA_CREDENTIALMANAGER_H
+#define ZYPP_MEDIA_CREDENTIALMANAGER_H
+
+#include <set>
+
+#include "zypp/Url.h"
+#include "zypp/Pathname.h"
+#include "zypp/media/MediaUserAuth.h"
+
+//////////////////////////////////////////////////////////////////////
+namespace zypp 
+{ ////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////
+  namespace media
+  { ////////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredManagerOptions 
+  //
+  struct CredManagerOptions
+  {
+    CredManagerOptions(const Pathname & rootdir = "");
+
+    Pathname globalCredFilePath;
+    Pathname userCredFilePath;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+
+  //////////////////////////////////////////////////////////////////////
+  //
+  // CLASS NAME : CredentialManager
+  //
+  /**
+   * 
+   */
+  class CredentialManager
+  {
+  public:
+    typedef std::set<AuthData_Ptr>        CredentialSet;
+    typedef CredentialSet::size_type      CredentialSize;
+    typedef CredentialSet::const_iterator CredentialIterator;
+
+
+    CredentialManager(const CredManagerOptions & opts = CredManagerOptions());
+    ~CredentialManager()
+    {}
+
+  public:
+    /**
+     * Get credentials for the specified \a url.
+     * 
+     * If the URL contains also username, it will be used to find the match
+     * for this user (in case mutliple are available).
+     * 
+     * \param url URL to find credentials for.
+     * \return Pointer to retrieved authentication data on success or an empty
+     *         AuthData_Ptr otherwise.
+     */
+    AuthData_Ptr getCred(const Url & url);
+
+
+    void save(const AuthData &, bool global = false);
+
+    /**
+     * 
+     */
+    void saveInGlobal(const AuthData & cred);
+
+    /**
+     * 
+     */
+    void saveInUser(const AuthData & cred);
+
+    /**
+     * 
+     */
+    void saveIn(const AuthData &, const Pathname & credFile);
+
+    CredentialIterator credsGlobalBegin() const;
+    CredentialIterator credsGlobalEnd()   const;
+    CredentialSize     credsGlobalSize()  const;
+    bool               credsGlobalEmpty() const;
+
+    CredentialIterator credsUserBegin() const;
+    CredentialIterator credsUserEnd()   const;
+    CredentialSize     credsUserSize()  const;
+    bool               credsUserEmpty() const;
+
+    class Impl;
+  private:
+    RW_pointer<Impl> _pimpl;
+  };
+  //////////////////////////////////////////////////////////////////////
+
+
+    ////////////////////////////////////////////////////////////////////
+  } // media
+  //////////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////////
+} // zypp
+//////////////////////////////////////////////////////////////////////
+
+#endif /* ZYPP_MEDIA_CREDENTIALMANAGER_H */
+
index 1ad12fc..e55d9a6 100644 (file)
@@ -24,6 +24,7 @@
 #include "zypp/media/proxyinfo/ProxyInfos.h"
 #include "zypp/media/ProxyInfo.h"
 #include "zypp/media/MediaUserAuth.h"
+#include "zypp/media/CredentialManager.h"
 #include "zypp/media/CurlConfig.h"
 #include "zypp/thread/Once.h"
 #include "zypp/Target.h"
@@ -770,7 +771,6 @@ void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target
   Url fileurl(getFileUrl(_url, filename));
 
   bool retry = false;
-  CurlAuthData auth_data;
 
   do
   {
@@ -782,43 +782,10 @@ void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target
     // retry with proper authentication data
     catch (MediaUnauthorizedException & ex_r)
     {
-      callback::SendReport<AuthenticationReport> auth_report;
-
-      if (!_url.getUsername().empty() && !retry)
-        auth_data.setUserName(_url.getUsername());
-
-      string prompt_msg;
-      if (retry || !_url.getUsername().empty())
-        prompt_msg = _("Invalid user name or password.");
-      else // first prompt
-        prompt_msg = boost::str(boost::format(
-          _("Authentication required for '%s'")) % _url.asString());
-
-      // set available authentication types from the exception
-      auth_data.setAuthType(ex_r.hint());
-
-      if (auth_report->prompt(_url, prompt_msg, auth_data))
-      {
-        DBG << "callback answer: retry" << endl
-            << "CurlAuthData: " << auth_data << endl;
-
-        if (auth_data.valid()) {
-          _userpwd = auth_data.getUserPwd();
-
-          // set username and password
-          CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
-          if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
-
-          // set auth type
-          ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
-          if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
-        }
-
+      if(authenticate(ex_r.hint(), !retry))
         retry = true;
-      }
       else
       {
-        DBG << "callback answer: cancel" << endl;
         report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserString());
         ZYPP_RETHROW(ex_r);
       }
@@ -836,10 +803,10 @@ void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target
   report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
 }
 
+
 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
 {
   bool retry = false;
-  CurlAuthData auth_data;
 
   do
   {
@@ -850,45 +817,10 @@ bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
     // authentication problem, retry with proper authentication data
     catch (MediaUnauthorizedException & ex_r)
     {
-      callback::SendReport<AuthenticationReport> auth_report;
-
-      if (!_url.getUsername().empty() && !retry)
-        auth_data.setUserName(_url.getUsername());
-
-      string prompt_msg;
-      if (retry || !_url.getUsername().empty())
-        prompt_msg = _("Invalid user name or password.");
-      else // first prompt
-        prompt_msg = boost::str(boost::format(
-          _("Authentication required for '%s'")) % _url.asString());
-
-      // set available authentication types from the exception
-      auth_data.setAuthType(ex_r.hint());
-
-      if (auth_report->prompt(_url, prompt_msg, auth_data))
-      {
-        DBG << "callback answer: retry" << endl
-            << "CurlAuthData: " << auth_data << endl;
-
-        if (auth_data.valid()) {
-          _userpwd = auth_data.getUserPwd();
-
-          // set username and password
-          CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
-          if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
-
-          // set auth type
-          ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, auth_data.authType());
-          if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
-        }
-
+      if(authenticate(ex_r.hint(), !retry))
         retry = true;
-      }
       else
-      {
-        DBG << "callback answer: cancel" << endl;
         ZYPP_RETHROW(ex_r);
-      }
     }
     // unexpected exception
     catch (MediaException & excpt_r)
@@ -1628,6 +1560,7 @@ int MediaCurl::progressCallback( void *clientp,
   return 0;
 }
 
+
 string MediaCurl::getAuthHint() const
 {
   long auth_info = CURLAUTH_NONE;
@@ -1643,6 +1576,85 @@ string MediaCurl::getAuthHint() const
   return "";
 }
 
+
+bool MediaCurl::authenticate(const string & availAuthTypes, bool firstTry) const
+{
+  //! \todo need a way to pass different CredManagerOptions here
+  CredentialManager cm;
+  CurlAuthData_Ptr credentials;
+
+  // get stored credentials
+  AuthData_Ptr cmcred = cm.getCred(_url);
+
+  if (cmcred)
+  {
+    credentials.reset(new CurlAuthData(*cmcred));
+    DBG << "got stored credentials:" << endl << *credentials << endl;
+  }
+  // if not found, ask user
+  else
+  {
+    CurlAuthData_Ptr curlcred;
+    curlcred.reset(new CurlAuthData());
+    callback::SendReport<AuthenticationReport> auth_report;
+
+    // preset the username if present in current url
+    if (!_url.getUsername().empty() && firstTry)
+      curlcred->setUserName(_url.getUsername());
+
+    string prompt_msg;
+    if (!firstTry || !_url.getUsername().empty())
+      prompt_msg = _("Invalid user name or password.");
+    else // first prompt
+      prompt_msg = boost::str(boost::format(
+        _("Authentication required for '%s'")) % _url.asString());
+
+    // set available authentication types from the exception
+    // might be needed in prompt
+    curlcred->setAuthType(availAuthTypes);
+
+    // ask user
+    if (auth_report->prompt(_url, prompt_msg, *curlcred))
+    {
+      DBG << "callback answer: retry" << endl
+          << "CurlAuthData: " << *curlcred << endl;
+
+      if (curlcred->valid())
+        credentials = curlcred;
+    }
+    else
+    {
+      DBG << "callback answer: cancel" << endl;
+    }
+  }
+
+  // set username and password
+  if (credentials)
+  {
+    _userpwd = credentials->getUserPwd();
+
+    // set username and password
+    CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _userpwd.c_str());
+    if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
+
+    // set available authentication types from the exception
+    if (credentials->authType() == CURLAUTH_NONE)
+      credentials->setAuthType(availAuthTypes);
+
+    // set auth type (seems this must be set _after_ setting the userpwd
+    if (credentials->authType() != CURLAUTH_NONE)
+    {
+      ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
+      if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
+    }
+
+    return true;
+  }
+
+  return false;
+}
+
+
   } // namespace media
 } // namespace zypp
 //
index 02ba82e..ea40610 100644 (file)
@@ -105,6 +105,8 @@ class MediaCurl : public MediaHandler {
      */
     std::string getAuthHint() const;
 
+    bool authenticate(const std::string & availAuthTypes, bool firstTry) const;
+
   private:
     CURL *_curl;
     char _curlError[ CURL_ERROR_SIZE ];
index c9f75bf..57ad8ca 100644 (file)
@@ -24,6 +24,14 @@ namespace zypp {
   namespace media {
 
 
+AuthData::AuthData(const Url & url)
+  : _url(url)
+{
+  _username = url.getUsername();
+  _password = url.getPassword();
+}
+
+
 bool AuthData::valid() const
 {
   return username().size() && password().size();
index 599c75a..a729ef1 100644 (file)
@@ -14,6 +14,8 @@
 
 #include <curl/curl.h>
 
+#include "zypp/base/PtrTypes.h"
+
 namespace zypp {
   namespace media {
 
@@ -27,9 +29,11 @@ namespace zypp {
 class AuthData
 {
 public:
-  AuthData() : _username(), _password()
+  AuthData()
   {}
 
+  AuthData(const Url & url);
+
   AuthData(std::string & username, std::string & password)
     : _username(username), _password(password)
   {}
@@ -43,9 +47,11 @@ public:
    */
   virtual bool valid() const;
 
-  void setUserName(std::string username) { _username = username; }
-  void setPassword(std::string password) { _password = password; }  
+  void setUrl(const Url & url) { _url = url; }
+  void setUserName(const std::string & username) { _username = username; }
+  void setPassword(const std::string & password) { _password = password; }  
 
+  Url url() const { return _url; }
   std::string username() const { return _username; }
   std::string password() const { return _password; } 
 
@@ -53,10 +59,13 @@ public:
 
 
 private:
+  Url _url;
   std::string _username;
   std::string _password;
 };
 
+typedef shared_ptr<AuthData> AuthData_Ptr;
+
 /**
  * Curl HTTP authentication data.
  */
@@ -69,6 +78,12 @@ public:
   CurlAuthData() : AuthData(), _auth_type_str(), _auth_type(CURLAUTH_NONE)
   {}
 
+  CurlAuthData(const AuthData & authData)
+    : AuthData(authData)
+    , _auth_type_str()
+    , _auth_type(CURLAUTH_NONE)
+  {}
+
   CurlAuthData(std::string & username, std::string & password, std::string & auth_type)
     : AuthData(username,password), _auth_type_str(auth_type)
   {
@@ -138,6 +153,7 @@ private:
   long _auth_type;
 };
 
+typedef shared_ptr<CurlAuthData> CurlAuthData_Ptr;
 
 std::ostream & operator << (std::ostream & str, AuthData & auth_data);
 std::ostream & operator << (std::ostream & str, CurlAuthData & auth_data);