libzypp  17.32.4
credentialmanager.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include "credentialmanager.h"
14 
15 #include <iostream>
16 #include <fstream>
17 
18 #include <utility>
19 #include <zypp-media/MediaConfig>
21 #include <zypp-core/base/Logger.h>
22 #include <zypp-core/base/Easy.h>
23 #include <zypp-core/fs/PathInfo.h>
24 
25 #include <zypp-media/auth/CredentialFileReader>
26 #include <zypp-media/MediaException>
27 
28 #include <boost/interprocess/sync/file_lock.hpp>
29 #include <boost/interprocess/sync/scoped_lock.hpp>
30 #include <boost/interprocess/sync/sharable_lock.hpp>
31 
32 namespace bpci = boost::interprocess;
33 
34 
35 using std::endl;
36 
37 #define USER_CREDENTIALS_FILE ".zypp/credentials.cat"
38 
40 namespace zypp
41 {
42  namespace media
44  {
45 
47  //
48  // CLASS NAME : AuthDataComparator
49  //
51 
52  bool AuthDataComparator::operator()( const AuthData_Ptr & lhs, const AuthData_Ptr & rhs ) const
53  {
54  static const url::ViewOption vopt = url::ViewOption::DEFAULTS
58  // std::less semantic!
59  int cmp = lhs->url().asString(vopt).compare( rhs->url().asString(vopt) );
60  if ( ! cmp )
61  cmp = lhs->username().compare( rhs->username() );
62  return( cmp < 0 );
63  }
64 
66  //
67  // CLASS NAME : CredManagerOptions
68  //
70 
72  : globalCredFilePath(rootdir / MediaConfig::instance().credentialsGlobalFile())
73  , customCredFileDir(rootdir / MediaConfig::instance().credentialsGlobalDir())
74  {
75  char * homedir = getenv("HOME");
76  if (homedir)
77  userCredFilePath = rootdir / homedir / USER_CREDENTIALS_FILE;
78  }
79 
80 
82  //
83  // CLASS NAME : CredentialManager::Impl
84  //
86  {
87  Impl(CredManagerOptions &&options);
88 
89  Impl(const Impl &) = delete;
90  Impl(Impl &&) = delete;
91  Impl &operator=(const Impl &) = delete;
92  Impl &operator=(Impl &&) = delete;
93 
95  {}
96 
98  void init_userCredentials();
99 
100  bool processCredentials(AuthData_Ptr & cred);
101 
102  AuthData_Ptr getCred(const Url & url) const;
103  AuthData_Ptr getCredFromFile(const Pathname & file);
104  void saveGlobalCredentials();
105  void saveUserCredentials();
106 
107 
109 
113 
116  };
118 
119 
121  //
122  // CLASS NAME : CredentialManager::Impl
123  //
125 
127  : _options(std::move(options))
128  , _globalDirty(false)
129  , _userDirty(false)
130  {
133  }
134 
135 
137  {
138  if (_options.globalCredFilePath.empty())
139  DBG << "global cred file not known";
140  else if (PathInfo(_options.globalCredFilePath).isExist())
141  {
142  /* list<Pathname> entries;
143  if (filesystem::readdir(entries, _options.globalCredFilePath, false) != 0)
144  ZYPP_THROW(Exception("failed to read directory"));
145 
146  for_(it, entries.begin(), entries.end())*/
147 
148  CredentialFileReader(_options.globalCredFilePath,
149  bind(&Impl::processCredentials, this, _1));
150  }
151  else
152  DBG << "global cred file does not exist";
153 
154  _credsGlobal = _credsTmp; _credsTmp.clear();
155  DBG << "Got " << _credsGlobal.size() << " global records." << endl;
156  }
157 
158 
160  {
161  if (_options.userCredFilePath.empty())
162  DBG << "user cred file not known";
163  else if (PathInfo(_options.userCredFilePath).isExist())
164  {
165  /* list<Pathname> entries;
166  if (filesystem::readdir(entries, _options.userCredFilePath, false ) != 0)
167  ZYPP_THROW(Exception("failed to read directory"));
168 
169  for_(it, entries.begin(), entries.end())*/
170  CredentialFileReader(_options.userCredFilePath,
171  bind(&Impl::processCredentials, this, _1));
172  }
173  else
174  DBG << "user cred file does not exist" << endl;
175 
176  _credsUser = _credsTmp; _credsTmp.clear();
177  DBG << "Got " << _credsUser.size() << " user records." << endl;
178  }
179 
180 
182  {
183  _credsTmp.insert(cred);
184  return true;
185  }
186 
187 
189  const Url & url,
190  url::ViewOption vopt)
191  {
192  const std::string & username = url.getUsername();
193  for( CredentialManager::CredentialIterator it = set.begin(); it != set.end(); ++it )
194  {
195  if ( !(*it)->url().isValid() )
196  continue;
197 
198  // this ignores url params - not sure if it is good or bad...
199  if ( url.asString(vopt).find((*it)->url().asString(vopt)) == 0 )
200  {
201  if ( username.empty() || username == (*it)->username() )
202  return *it;
203  }
204  }
205 
206  return AuthData_Ptr();
207  }
208 
210  {
211  AuthData_Ptr result;
212 
213  // compare the urls via asString(), but ignore password
214  // default url::ViewOption will take care of that.
215  // operator==(Url,Url) compares the whole Url
216 
217  url::ViewOption vopt;
218  vopt = vopt
222 
223  // search in global credentials
224  result = findIn(_credsGlobal, url, vopt);
225 
226  // search in home credentials
227  if (!result)
228  result = findIn(_credsUser, url, vopt);
229 
230  if (result)
231  DBG << "Found credentials for '" << url << "':" << endl << *result;
232  else
233  DBG << "No credentials for '" << url << "'" << endl;
234 
235  return result;
236  }
237 
238 
240  {
241  AuthData_Ptr result;
242 
243  Pathname credfile;
244  if (file.absolute())
245  // get from that file
246  credfile = file;
247  else
248  // get from /etc/zypp/credentials.d, delete the leading path
249  credfile = _options.customCredFileDir / file.basename();
250 
251  PathInfo pi { credfile };
252  if ( pi.userMayR() ) try {
253  // make sure only our thread accesses the file
254  bpci::file_lock lockFile ( credfile.c_str() );
255  bpci::scoped_lock lock( lockFile );
256 
257  CredentialFileReader(credfile, bind(&Impl::processCredentials, this, _1));
258  }
259  catch ( ... ) {
260  WAR << pi << " failed to lock file for reading." << endl;
261  }
262 
263  if (_credsTmp.empty())
264  WAR << pi << " does not contain valid credentials or is not readable." << endl;
265  else
266  {
267  result = *_credsTmp.begin();
268  _credsTmp.clear();
269  }
270 
271  return result;
272  }
273 
274  static int save_creds_in_file(
276  const Pathname & file,
277  const mode_t mode)
278  {
279  int ret = 0;
280  filesystem::assert_file_mode( file, mode );
281 
282  const auto now = time( nullptr );
283 
284  PathInfo pi { file };
285  if ( pi.userMayRW() ) try {
286  // make sure only our thread accesses the file
287  bpci::file_lock lockFile ( file.c_str() );
288  bpci::scoped_lock lock( lockFile );
289 
290  std::ofstream fs(file.c_str());
291  for_(it, creds.begin(), creds.end())
292  {
293  (*it)->dumpAsIniOn(fs);
294  (*it)->setLastDatabaseUpdate( now );
295  fs << endl;
296  }
297  if ( !fs ) {
298  WAR << pi << " failed to write credentials to file." << endl;
299  ret = 1;
300  }
301  fs.close();
302  }
303  catch ( ... ) {
304  WAR << pi << " failed to lock file for writing." << endl;
305  ret = 1;
306  }
307 
308  return ret;
309  }
310 
312  {
313  save_creds_in_file(_credsGlobal, _options.globalCredFilePath, 0640);
314  }
315 
317  {
318  save_creds_in_file(_credsUser, _options.userCredFilePath, 0600);
319  }
320 
321 
323  //
324  // CLASS NAME : CredentialManager
325  //
327 
329  : _pimpl(new Impl(std::move(opts)))
330  {}
331 
332 
334  {
335  std::string credfile = url.getQueryParam("credentials");
336  if (credfile.empty())
337  return _pimpl->getCred(url);
338  return _pimpl->getCredFromFile(credfile);
339  }
340 
341 
343  { return _pimpl->getCredFromFile(file); }
344 
345 
347  {
348  if ( !cred.url().isValid() )
349  ZYPP_THROW( MediaInvalidCredentialsException( "URL must be valid in order to save AuthData." ) );
350 
351  Pathname credfile = cred.url().getQueryParam("credentials");
352  if (credfile.empty())
354  addUserCred(cred);
355  else
356  saveInFile(cred, credfile);
357  }
358 
360  {
361  Pathname credfile;
362  if ( url.isValid() ) {
363  credfile = url.getQueryParam("credentials");
364  }
365 
366  if (credfile.empty())
367  credfile = _pimpl->_options.userCredFilePath;
368 
369  zypp::PathInfo pi(credfile);
370  if ( pi.isExist() && pi.isFile() )
371  return pi.mtime();
372 
373  return 0;
374  }
375 
377  {
378  if ( !cred.url().isValid() )
379  ZYPP_THROW( MediaInvalidCredentialsException( "URL must be valid in order to save AuthData." ) );
380 
381  AuthData_Ptr c_ptr;
382  c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
383  std::pair<CredentialIterator, bool> ret = _pimpl->_credsGlobal.insert(c_ptr);
384  if (ret.second)
385  _pimpl->_globalDirty = true;
386  else if ((*ret.first)->password() != cred.password())
387  {
388  _pimpl->_credsGlobal.erase(ret.first);
389  _pimpl->_credsGlobal.insert(c_ptr);
390  _pimpl->_globalDirty = true;
391  }
392  }
393 
394 
396  {
397  if ( !cred.url().isValid() )
398  ZYPP_THROW( MediaInvalidCredentialsException( "URL must be valid in order to save AuthData." ) );
399 
400  AuthData_Ptr c_ptr;
401  c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
402  std::pair<CredentialIterator, bool> ret = _pimpl->_credsUser.insert(c_ptr);
403  if (ret.second)
404  _pimpl->_userDirty = true;
405  else if ((*ret.first)->password() != cred.password())
406  {
407  _pimpl->_credsUser.erase(ret.first);
408  _pimpl->_credsUser.insert(c_ptr);
409  _pimpl->_userDirty = true;
410  }
411  }
412 
413 
415  {
416  if (_pimpl->_globalDirty)
418  if (_pimpl->_userDirty)
420  _pimpl->_globalDirty = false;
421  _pimpl->_userDirty = false;
422  }
423 
424 
426  {
427  addGlobalCred(cred);
428  save();
429  }
430 
431 
433  {
434  addUserCred(cred);
435  save();
436  }
437 
438 
439  void CredentialManager::saveInFile(const AuthData & cred, const Pathname & credFile)
440  {
441  AuthData_Ptr c_ptr;
442  c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
443  c_ptr->setUrl(Url()); // don't save url in custom creds file
445  creds.insert(c_ptr);
446 
447  int ret = 0;
448  if (credFile.absolute())
449  ret = save_creds_in_file(creds, credFile, 0640);
450  else
451  ret = save_creds_in_file(
452  creds, _pimpl->_options.customCredFileDir / credFile, 0600);
453 
454  if (!ret)
455  {
457  ERR << "error saving the credentials" << endl;
458  }
459  }
460 
461 
462  void CredentialManager::clearAll(bool global)
463  {
464  if (global)
465  {
467  ERR << "could not delete user credentials file "
468  << _pimpl->_options.globalCredFilePath << endl;
469  _pimpl->_credsUser.clear();
470  }
471  else
472  {
474  ERR << "could not delete global credentials file"
475  << _pimpl->_options.userCredFilePath << endl;
476  _pimpl->_credsGlobal.clear();
477  }
478  }
479 
480 
482  { return _pimpl->_credsGlobal.begin(); }
483 
485  { return _pimpl->_credsGlobal.end(); }
486 
488  { return _pimpl->_credsGlobal.size(); }
489 
491  { return _pimpl->_credsGlobal.empty(); }
492 
493 
495  { return _pimpl->_credsUser.begin(); }
496 
498  { return _pimpl->_credsUser.end(); }
499 
501  { return _pimpl->_credsUser.size(); }
502 
504  { return _pimpl->_credsUser.empty(); }
505 
506 
508  } // media
511 } // zypp
AuthData_Ptr getCred(const Url &url) const
static const ViewOption WITH_USERNAME
Option to include username in the URL string.
Definition: UrlBase.h:58
std::string password() const
Definition: authdata.h:57
CredentialIterator credsGlobalEnd() const
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:429
void saveInFile(const AuthData &, const Pathname &credFile)
Saves given cred to user specified credentials file.
void save()
Saves any unsaved credentials added via addUserCred() or addGlobalCred() methods. ...
void addGlobalCred(const AuthData &cred)
Add new global credentials.
#define USER_CREDENTIALS_FILE
CredentialSet::const_iterator CredentialIterator
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
time_t mtime() const
Definition: PathInfo.h:377
const char * c_str() const
String representation.
Definition: Pathname.h:110
Definition: Arch.h:363
AuthData_Ptr getCred(const Url &url)
Get credentials for the specified url.
std::string basename() const
Return the last component of this path.
Definition: Pathname.h:128
void saveInGlobal(const AuthData &cred)
Saves given cred to global credentials file.
time_t timestampForCredDatabase(const zypp::Url &url)
Url::asString() view options.
Definition: UrlBase.h:39
#define ERR
Definition: Logger.h:98
CredManagerOptions(const Pathname &rootdir="")
CredentialSet::size_type CredentialSize
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
Definition: PathInfo.cc:1206
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:501
std::string getQueryParam(const std::string &param, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
Definition: Url.cc:664
std::set< AuthData_Ptr, AuthDataComparator > CredentialSet
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:701
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:282
CredentialManager(CredManagerOptions opts=CredManagerOptions())
static int save_creds_in_file(CredentialManager::CredentialSet &creds, const Pathname &file, const mode_t mode)
bool processCredentials(AuthData_Ptr &cred)
CredentialSize credsUserSize() const
#define WAR
Definition: Logger.h:97
zypp::media::AuthData AuthData
Definition: authdata.h:21
shared_ptr< AuthData > AuthData_Ptr
Definition: authdata.h:81
void addUserCred(const AuthData &cred)
Add new user credentials.
Url url() const
Definition: authdata.h:55
Parse credentials files and catalogs.
bool absolute() const
Test for an absolute path.
Definition: Pathname.h:116
bool isValid() const
Verifies the Url.
Definition: Url.cc:493
CredentialIterator credsGlobalBegin() const
Class for handling media authentication data.
Definition: authdata.h:30
static const ViewOption WITH_QUERY_STR
Option to include query string in the URL string.
Definition: UrlBase.h:101
static const ViewOption WITH_PASSWORD
Option to include password in the URL string.
Definition: UrlBase.h:67
CredentialIterator credsUserBegin() const
void clearAll(bool global=false)
Remove all global or user credentials from memory and disk.
CredentialIterator credsUserEnd() const
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:221
Impl & operator=(const Impl &)=delete
static const ViewOption DEFAULTS
Default combination of view options.
Definition: UrlBase.h:177
static AuthData_Ptr findIn(const CredentialManager::CredentialSet &set, const Url &url, url::ViewOption vopt)
AuthData_Ptr getCredFromFile(const Pathname &file)
Read credentials from a file.
AuthData_Ptr getCredFromFile(const Pathname &file)
CredentialSize credsGlobalSize() const
void addCred(const AuthData &cred)
Add new credentials with user callbacks.
Impl(CredManagerOptions &&options)
Easy-to use interface to the ZYPP dependency resolver.
Definition: Application.cc:19
Url manipulation class.
Definition: Url.h:91
bool operator()(const AuthData_Ptr &lhs, const AuthData_Ptr &rhs) const
void saveInUser(const AuthData &cred)
Saves given cred to user&#39;s credentials file.
#define DBG
Definition: Logger.h:95
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:576