libzypp  17.31.31
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 <zypp-media/MediaConfig>
19 #include <zypp-core/base/Function.h>
20 #include <zypp-core/base/Logger.h>
21 #include <zypp-core/base/Easy.h>
22 #include <zypp-core/fs/PathInfo.h>
23 
24 #include <zypp-media/auth/CredentialFileReader>
25 #include <zypp-media/MediaException>
26 
27 #include <boost/interprocess/sync/file_lock.hpp>
28 #include <boost/interprocess/sync/scoped_lock.hpp>
29 #include <boost/interprocess/sync/sharable_lock.hpp>
30 
31 namespace bpci = boost::interprocess;
32 
33 
34 using std::endl;
35 
36 #define USER_CREDENTIALS_FILE ".zypp/credentials.cat"
37 
39 namespace zypp
40 {
41  namespace media
43  {
44 
46  //
47  // CLASS NAME : AuthDataComparator
48  //
50 
51  bool AuthDataComparator::operator()( const AuthData_Ptr & lhs, const AuthData_Ptr & rhs ) const
52  {
53  static const url::ViewOption vopt = url::ViewOption::DEFAULTS
57  // std::less semantic!
58  int cmp = lhs->url().asString(vopt).compare( rhs->url().asString(vopt) );
59  if ( ! cmp )
60  cmp = lhs->username().compare( rhs->username() );
61  return( cmp < 0 );
62  }
63 
65  //
66  // CLASS NAME : CredManagerOptions
67  //
69 
71  : globalCredFilePath(rootdir / MediaConfig::instance().credentialsGlobalFile())
72  , customCredFileDir(rootdir / MediaConfig::instance().credentialsGlobalDir())
73  {
74  char * homedir = getenv("HOME");
75  if (homedir)
76  userCredFilePath = rootdir / homedir / USER_CREDENTIALS_FILE;
77  }
78 
79 
81  //
82  // CLASS NAME : CredentialManager::Impl
83  //
85  {
86  Impl(const CredManagerOptions & options);
87 
89  {}
90 
92  void init_userCredentials();
93 
94  bool processCredentials(AuthData_Ptr & cred);
95 
96  AuthData_Ptr getCred(const Url & url) const;
98  void saveGlobalCredentials();
99  void saveUserCredentials();
100 
101 
103 
107 
110  };
112 
113 
115  //
116  // CLASS NAME : CredentialManager::Impl
117  //
119 
121  : _options(options)
122  , _globalDirty(false)
123  , _userDirty(false)
124  {
127  }
128 
129 
131  {
132  if (_options.globalCredFilePath.empty())
133  DBG << "global cred file not known";
134  else if (PathInfo(_options.globalCredFilePath).isExist())
135  {
136  /* list<Pathname> entries;
137  if (filesystem::readdir(entries, _options.globalCredFilePath, false) != 0)
138  ZYPP_THROW(Exception("failed to read directory"));
139 
140  for_(it, entries.begin(), entries.end())*/
141 
142  CredentialFileReader(_options.globalCredFilePath,
143  bind(&Impl::processCredentials, this, _1));
144  }
145  else
146  DBG << "global cred file does not exist";
147 
148  _credsGlobal = _credsTmp; _credsTmp.clear();
149  DBG << "Got " << _credsGlobal.size() << " global records." << endl;
150  }
151 
152 
154  {
155  if (_options.userCredFilePath.empty())
156  DBG << "user cred file not known";
157  else if (PathInfo(_options.userCredFilePath).isExist())
158  {
159  /* list<Pathname> entries;
160  if (filesystem::readdir(entries, _options.userCredFilePath, false ) != 0)
161  ZYPP_THROW(Exception("failed to read directory"));
162 
163  for_(it, entries.begin(), entries.end())*/
164  CredentialFileReader(_options.userCredFilePath,
165  bind(&Impl::processCredentials, this, _1));
166  }
167  else
168  DBG << "user cred file does not exist" << endl;
169 
170  _credsUser = _credsTmp; _credsTmp.clear();
171  DBG << "Got " << _credsUser.size() << " user records." << endl;
172  }
173 
174 
176  {
177  _credsTmp.insert(cred);
178  return true;
179  }
180 
181 
183  const Url & url,
184  url::ViewOption vopt)
185  {
186  const std::string & username = url.getUsername();
187  for( CredentialManager::CredentialIterator it = set.begin(); it != set.end(); ++it )
188  {
189  if ( !(*it)->url().isValid() )
190  continue;
191 
192  // this ignores url params - not sure if it is good or bad...
193  if ( url.asString(vopt).find((*it)->url().asString(vopt)) == 0 )
194  {
195  if ( username.empty() || username == (*it)->username() )
196  return *it;
197  }
198  }
199 
200  return AuthData_Ptr();
201  }
202 
204  {
205  AuthData_Ptr result;
206 
207  // compare the urls via asString(), but ignore password
208  // default url::ViewOption will take care of that.
209  // operator==(Url,Url) compares the whole Url
210 
211  url::ViewOption vopt;
212  vopt = vopt
216 
217  // search in global credentials
218  result = findIn(_credsGlobal, url, vopt);
219 
220  // search in home credentials
221  if (!result)
222  result = findIn(_credsUser, url, vopt);
223 
224  if (result)
225  DBG << "Found credentials for '" << url << "':" << endl << *result;
226  else
227  DBG << "No credentials for '" << url << "'" << endl;
228 
229  return result;
230  }
231 
232 
234  {
235  AuthData_Ptr result;
236 
237  Pathname credfile;
238  if (file.absolute())
239  // get from that file
240  credfile = file;
241  else
242  // get from /etc/zypp/credentials.d, delete the leading path
243  credfile = _options.customCredFileDir / file.basename();
244 
245  PathInfo pi { credfile };
246  if ( pi.userMayR() ) try {
247  // make sure only our thread accesses the file
248  bpci::file_lock lockFile ( credfile.c_str() );
249  bpci::scoped_lock lock( lockFile );
250 
251  CredentialFileReader(credfile, bind(&Impl::processCredentials, this, _1));
252  }
253  catch ( ... ) {
254  WAR << pi << " failed to lock file for reading." << endl;
255  }
256 
257  if (_credsTmp.empty())
258  WAR << pi << " does not contain valid credentials or is not readable." << endl;
259  else
260  {
261  result = *_credsTmp.begin();
262  _credsTmp.clear();
263  }
264 
265  return result;
266  }
267 
268  static int save_creds_in_file(
270  const Pathname & file,
271  const mode_t mode)
272  {
273  int ret = 0;
274  filesystem::assert_file_mode( file, mode );
275 
276  const auto now = time( nullptr );
277 
278  PathInfo pi { file };
279  if ( pi.userMayRW() ) try {
280  // make sure only our thread accesses the file
281  bpci::file_lock lockFile ( file.c_str() );
282  bpci::scoped_lock lock( lockFile );
283 
284  std::ofstream fs(file.c_str());
285  for_(it, creds.begin(), creds.end())
286  {
287  (*it)->dumpAsIniOn(fs);
288  (*it)->setLastDatabaseUpdate( now );
289  fs << endl;
290  }
291  if ( !fs ) {
292  WAR << pi << " failed to write credentials to file." << endl;
293  ret = 1;
294  }
295  fs.close();
296  }
297  catch ( ... ) {
298  WAR << pi << " failed to lock file for writing." << endl;
299  ret = 1;
300  }
301 
302  return ret;
303  }
304 
306  {
307  save_creds_in_file(_credsGlobal, _options.globalCredFilePath, 0640);
308  }
309 
311  {
312  save_creds_in_file(_credsUser, _options.userCredFilePath, 0600);
313  }
314 
315 
317  //
318  // CLASS NAME : CredentialManager
319  //
321 
323  : _pimpl(new Impl(opts))
324  {}
325 
326 
328  {
329  std::string credfile = url.getQueryParam("credentials");
330  if (credfile.empty())
331  return _pimpl->getCred(url);
332  return _pimpl->getCredFromFile(credfile);
333  }
334 
335 
337  { return _pimpl->getCredFromFile(file); }
338 
339 
341  {
342  if ( !cred.url().isValid() )
343  ZYPP_THROW( MediaInvalidCredentialsException( "URL must be valid in order to save AuthData." ) );
344 
345  Pathname credfile = cred.url().getQueryParam("credentials");
346  if (credfile.empty())
348  addUserCred(cred);
349  else
350  saveInFile(cred, credfile);
351  }
352 
354  {
355  Pathname credfile;
356  if ( url.isValid() ) {
357  credfile = url.getQueryParam("credentials");
358  }
359 
360  if (credfile.empty())
361  credfile = _pimpl->_options.userCredFilePath;
362 
363  zypp::PathInfo pi(credfile);
364  if ( pi.isExist() && pi.isFile() )
365  return pi.mtime();
366 
367  return 0;
368  }
369 
371  {
372  if ( !cred.url().isValid() )
373  ZYPP_THROW( MediaInvalidCredentialsException( "URL must be valid in order to save AuthData." ) );
374 
375  AuthData_Ptr c_ptr;
376  c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
377  std::pair<CredentialIterator, bool> ret = _pimpl->_credsGlobal.insert(c_ptr);
378  if (ret.second)
379  _pimpl->_globalDirty = true;
380  else if ((*ret.first)->password() != cred.password())
381  {
382  _pimpl->_credsGlobal.erase(ret.first);
383  _pimpl->_credsGlobal.insert(c_ptr);
384  _pimpl->_globalDirty = true;
385  }
386  }
387 
388 
390  {
391  if ( !cred.url().isValid() )
392  ZYPP_THROW( MediaInvalidCredentialsException( "URL must be valid in order to save AuthData." ) );
393 
394  AuthData_Ptr c_ptr;
395  c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
396  std::pair<CredentialIterator, bool> ret = _pimpl->_credsUser.insert(c_ptr);
397  if (ret.second)
398  _pimpl->_userDirty = true;
399  else if ((*ret.first)->password() != cred.password())
400  {
401  _pimpl->_credsUser.erase(ret.first);
402  _pimpl->_credsUser.insert(c_ptr);
403  _pimpl->_userDirty = true;
404  }
405  }
406 
407 
409  {
410  if (_pimpl->_globalDirty)
412  if (_pimpl->_userDirty)
414  _pimpl->_globalDirty = false;
415  _pimpl->_userDirty = false;
416  }
417 
418 
420  {
421  addGlobalCred(cred);
422  save();
423  }
424 
425 
427  {
428  addUserCred(cred);
429  save();
430  }
431 
432 
433  void CredentialManager::saveInFile(const AuthData & cred, const Pathname & credFile)
434  {
435  AuthData_Ptr c_ptr;
436  c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
437  c_ptr->setUrl(Url()); // don't save url in custom creds file
439  creds.insert(c_ptr);
440 
441  int ret;
442  if (credFile.absolute())
443  ret = save_creds_in_file(creds, credFile, 0640);
444  else
445  ret = save_creds_in_file(
446  creds, _pimpl->_options.customCredFileDir / credFile, 0600);
447 
448  if (!ret)
449  {
451  ERR << "error saving the credentials" << endl;
452  }
453  }
454 
455 
456  void CredentialManager::clearAll(bool global)
457  {
458  if (global)
459  {
461  ERR << "could not delete user credentials file "
462  << _pimpl->_options.globalCredFilePath << endl;
463  _pimpl->_credsUser.clear();
464  }
465  else
466  {
468  ERR << "could not delete global credentials file"
469  << _pimpl->_options.userCredFilePath << endl;
470  _pimpl->_credsGlobal.clear();
471  }
472  }
473 
474 
476  { return _pimpl->_credsGlobal.begin(); }
477 
479  { return _pimpl->_credsGlobal.end(); }
480 
482  { return _pimpl->_credsGlobal.size(); }
483 
485  { return _pimpl->_credsGlobal.empty(); }
486 
487 
489  { return _pimpl->_credsUser.begin(); }
490 
492  { return _pimpl->_credsUser.end(); }
493 
495  { return _pimpl->_credsUser.size(); }
496 
498  { return _pimpl->_credsUser.empty(); }
499 
500 
502  } // media
505 } // 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:55
CredentialIterator credsGlobalEnd() const
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:428
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
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
time_t mtime() const
Definition: PathInfo.h:376
const char * c_str() const
String representation.
Definition: Pathname.h:110
CredentialSet::size_type CredentialSize
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
Impl(const CredManagerOptions &options)
#define ERR
Definition: Logger.h:98
CredManagerOptions(const Pathname &rootdir="")
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:1202
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:497
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:660
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:700
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:281
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
void addUserCred(const AuthData &cred)
Add new user credentials.
Url url() const
Definition: authdata.h:53
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:489
CredentialIterator credsGlobalBegin() const
Class for handling media authentication data.
Definition: authdata.h:28
static const ViewOption WITH_QUERY_STR
Option to include query string in the URL string.
Definition: UrlBase.h:101
shared_ptr< AuthData > AuthData_Ptr
Definition: authdata.h:79
static const ViewOption WITH_PASSWORD
Option to include password in the URL string.
Definition: UrlBase.h:67
std::set< AuthData_Ptr, AuthDataComparator > CredentialSet
CredentialIterator credsUserBegin() const
CredentialSet::const_iterator CredentialIterator
CredentialManager(const CredManagerOptions &opts=CredManagerOptions())
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:220
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.
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
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:572