Imported Upstream version 15.0.0
[platform/upstream/libzypp.git] / zypp / media / CredentialManager.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/CredentialManager.cc
10  *
11  */
12 #include <iostream>
13 #include <fstream>
14
15 #include "zypp/ZConfig.h"
16 #include "zypp/base/Function.h"
17 #include "zypp/base/Logger.h"
18 #include "zypp/base/Easy.h"
19 #include "zypp/PathInfo.h"
20
21 #include "zypp/media/CredentialFileReader.h"
22
23 #include "zypp/media/CredentialManager.h"
24
25 #define USER_CREDENTIALS_FILE ".zypp/credentials.cat"
26
27 using namespace std;
28
29 //////////////////////////////////////////////////////////////////////
30 namespace zypp
31 { ////////////////////////////////////////////////////////////////////
32   //////////////////////////////////////////////////////////////////////
33   namespace media
34   { ////////////////////////////////////////////////////////////////////
35
36   //////////////////////////////////////////////////////////////////////
37   //
38   // CLASS NAME : AuthDataComparator
39   //
40   //////////////////////////////////////////////////////////////////////
41
42   bool
43   AuthDataComparator::operator()(
44       const AuthData_Ptr & lhs, const AuthData_Ptr & rhs)
45   {
46     static const url::ViewOption vopt =
47         url::ViewOption::DEFAULTS
48         - url::ViewOption::WITH_USERNAME
49         - url::ViewOption::WITH_PASSWORD
50         - url::ViewOption::WITH_QUERY_STR;
51
52     if (lhs->username() != rhs->username())
53       return true;
54
55     if (lhs->url().asString(vopt) != rhs->url().asString(vopt))
56       return true;
57
58     return false;
59   }
60
61   //////////////////////////////////////////////////////////////////////
62   //
63   // CLASS NAME : CredManagerOptions
64   //
65   //////////////////////////////////////////////////////////////////////
66
67   CredManagerOptions::CredManagerOptions(const Pathname & rootdir)
68     : globalCredFilePath(rootdir / ZConfig::instance().credentialsGlobalFile())
69     , customCredFileDir(rootdir / ZConfig::instance().credentialsGlobalDir())
70   {
71     char * homedir = getenv("HOME");
72     if (homedir)
73       userCredFilePath = rootdir / homedir / USER_CREDENTIALS_FILE;
74   }
75
76
77   //////////////////////////////////////////////////////////////////////
78   //
79   // CLASS NAME : CredentialManager::Impl
80   //
81   struct CredentialManager::Impl
82   {
83     Impl(const CredManagerOptions & options);
84
85     ~Impl()
86     {}
87
88     void init_globalCredentials();
89     void init_userCredentials();
90
91     bool processCredentials(AuthData_Ptr & cred);
92
93     AuthData_Ptr getCred(const Url & url) const;
94     AuthData_Ptr getCredFromFile(const Pathname & file);
95     void saveGlobalCredentials();
96     void saveUserCredentials();
97
98
99     CredManagerOptions _options;
100
101     CredentialSet _credsGlobal;
102     CredentialSet _credsUser;
103     CredentialSet _credsTmp;
104
105     bool _globalDirty;
106     bool _userDirty;
107   };
108   //////////////////////////////////////////////////////////////////////
109
110
111   //////////////////////////////////////////////////////////////////////
112   //
113   // CLASS NAME : CredentialManager::Impl
114   //
115   //////////////////////////////////////////////////////////////////////
116
117   CredentialManager::Impl::Impl(const CredManagerOptions & options)
118     : _options(options)
119     , _globalDirty(false)
120     , _userDirty(false)
121   {
122     init_globalCredentials();
123     init_userCredentials();
124   }
125
126
127   void CredentialManager::Impl::init_globalCredentials()
128   {
129     if (_options.globalCredFilePath.empty())
130       DBG << "global cred file not known";
131     else if (PathInfo(_options.globalCredFilePath).isExist())
132     {
133     /*  list<Pathname> entries;
134       if (filesystem::readdir(entries, _options.globalCredFilePath, false) != 0)
135         ZYPP_THROW(Exception("failed to read directory"));
136
137       for_(it, entries.begin(), entries.end())*/
138
139       CredentialFileReader(_options.globalCredFilePath,
140           bind(&Impl::processCredentials, this, _1));
141     }
142     else
143       DBG << "global cred file does not exist";
144
145     _credsGlobal = _credsTmp; _credsTmp.clear();
146     DBG << "Got " << _credsGlobal.size() << " global records." << endl;
147   }
148
149
150   void CredentialManager::Impl::init_userCredentials()
151   {
152     if (_options.userCredFilePath.empty())
153       DBG << "user cred file not known";
154     else if (PathInfo(_options.userCredFilePath).isExist())
155     {
156     /*  list<Pathname> entries;
157       if (filesystem::readdir(entries, _options.userCredFilePath, false ) != 0)
158         ZYPP_THROW(Exception("failed to read directory"));
159
160       for_(it, entries.begin(), entries.end())*/
161       CredentialFileReader(_options.userCredFilePath,
162           bind(&Impl::processCredentials, this, _1));
163     }
164     else
165       DBG << "user cred file does not exist" << endl;
166
167     _credsUser = _credsTmp; _credsTmp.clear();
168     DBG << "Got " << _credsUser.size() << " user records." << endl;
169   }
170
171
172   bool CredentialManager::Impl::processCredentials(AuthData_Ptr & cred)
173   {
174     _credsTmp.insert(cred);
175     return true;
176   }
177
178
179   static AuthData_Ptr findIn(const CredentialManager::CredentialSet & set,
180                              const Url & url,
181                              url::ViewOption vopt)
182   {
183     const string & username = url.getUsername();
184     for(CredentialManager::CredentialIterator it = set.begin(); it != set.end(); ++it)
185     {
186       // this ignores url params - not sure if it is good or bad...
187       if (url.asString(vopt).find((*it)->url().asString(vopt)) == 0)
188       {
189         if (username.empty() || username == (*it)->username())
190           return *it;
191       }
192     }
193
194     return AuthData_Ptr();
195   }
196
197
198   AuthData_Ptr CredentialManager::Impl::getCred(const Url & url) const
199   {
200     AuthData_Ptr result;
201
202     // compare the urls via asString(), but ignore password
203     // default url::ViewOption will take care of that.
204     // operator==(Url,Url) compares the whole Url
205
206     url::ViewOption vopt;
207     vopt = vopt
208       - url::ViewOption::WITH_USERNAME
209       - url::ViewOption::WITH_PASSWORD
210       - url::ViewOption::WITH_QUERY_STR;
211
212     // search in global credentials
213     result = findIn(_credsGlobal, url, vopt);
214
215     // search in home credentials
216     if (!result)
217       result = findIn(_credsUser, url, vopt);
218
219     if (result)
220       DBG << "Found credentials for '" << url << "':" << endl << *result;
221     else
222       DBG << "No credentials for '" << url << "'" << endl;
223
224     return result;
225   }
226
227
228   AuthData_Ptr CredentialManager::Impl::getCredFromFile(const Pathname & file)
229   {
230     AuthData_Ptr result;
231
232     Pathname credfile;
233     if (file.absolute())
234       // get from that file
235       credfile = file;
236     else
237       // get from /etc/zypp/credentials.d, delete the leading path
238       credfile = _options.customCredFileDir / file.basename();
239
240     CredentialFileReader(credfile, bind(&Impl::processCredentials, this, _1));
241     if (_credsTmp.empty())
242       WAR << file << " does not contain valid credentials or is not readable." << endl;
243     else
244     {
245       result = *_credsTmp.begin();
246       _credsTmp.clear();
247     }
248
249     return result;
250   }
251
252   static int save_creds_in_file(
253       const CredentialManager::CredentialSet creds,
254       const Pathname & file,
255       const mode_t mode)
256   {
257     int ret = 0;
258     filesystem::assert_dir(file.dirname());
259
260     std::ofstream fs(file.c_str());
261     if (!fs)
262       ret = 1;
263
264     for_(it, creds.begin(), creds.end())
265     {
266       (*it)->dumpAsIniOn(fs);
267       fs << endl;
268     }
269     fs.close();
270
271     filesystem::chmod(file, mode);
272
273     return ret;
274   }
275
276   void  CredentialManager::Impl::saveGlobalCredentials()
277   {
278     save_creds_in_file(_credsGlobal, _options.globalCredFilePath, 0640);
279   }
280
281   void  CredentialManager::Impl::saveUserCredentials()
282   {
283     save_creds_in_file(_credsUser, _options.userCredFilePath, 0600);
284   }
285
286
287   //////////////////////////////////////////////////////////////////////
288   //
289   // CLASS NAME : CredentialManager
290   //
291   //////////////////////////////////////////////////////////////////////
292
293   CredentialManager::CredentialManager(const CredManagerOptions & opts)
294     : _pimpl(new Impl(opts))
295   {}
296
297
298   AuthData_Ptr CredentialManager::getCred(const Url & url)
299   {
300     string credfile = url.getQueryParam("credentials");
301     if (credfile.empty())
302       return _pimpl->getCred(url);
303     return _pimpl->getCredFromFile(credfile);
304   }
305
306
307   AuthData_Ptr CredentialManager::getCredFromFile(const Pathname & file)
308   { return _pimpl->getCredFromFile(file); }
309
310
311   void CredentialManager::addCred(const AuthData & cred)
312   {
313     Pathname credfile = cred.url().getQueryParam("credentials");
314     if (credfile.empty())
315       //! \todo ask user where to store these creds. saving to user creds for now
316       addUserCred(cred);
317     else
318       saveInFile(cred, credfile);
319   }
320
321
322   void CredentialManager::addGlobalCred(const AuthData & cred)
323   {
324     AuthData_Ptr c_ptr;
325     c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
326     pair<CredentialIterator, bool> ret = _pimpl->_credsGlobal.insert(c_ptr);
327     if (ret.second)
328       _pimpl->_globalDirty = true;
329     else if ((*ret.first)->password() != cred.password())
330     {
331       _pimpl->_credsGlobal.erase(ret.first);
332       _pimpl->_credsGlobal.insert(c_ptr);
333       _pimpl->_globalDirty = true;
334     }
335   }
336
337
338   void CredentialManager::addUserCred(const AuthData & cred)
339   {
340     AuthData_Ptr c_ptr;
341     c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
342     pair<CredentialIterator, bool> ret = _pimpl->_credsUser.insert(c_ptr);
343     if (ret.second)
344       _pimpl->_userDirty = true;
345     else if ((*ret.first)->password() != cred.password())
346     {
347       _pimpl->_credsUser.erase(ret.first);
348       _pimpl->_credsUser.insert(c_ptr);
349       _pimpl->_userDirty = true;
350     }
351   }
352
353
354   void CredentialManager::save()
355   {
356     if (_pimpl->_globalDirty)
357       _pimpl->saveGlobalCredentials();
358     if (_pimpl->_userDirty)
359       _pimpl->saveUserCredentials();
360     _pimpl->_globalDirty = false;
361     _pimpl->_userDirty = false;
362   }
363
364
365   void CredentialManager::saveInGlobal(const AuthData & cred)
366   {
367     addGlobalCred(cred);
368     save();
369   }
370
371
372   void CredentialManager::saveInUser(const AuthData & cred)
373   {
374     addUserCred(cred);
375     save();
376   }
377
378
379   void CredentialManager::saveInFile(const AuthData & cred, const Pathname & credFile)
380   {
381     AuthData_Ptr c_ptr;
382     c_ptr.reset(new AuthData(cred)); // FIX for child classes if needed
383     c_ptr->setUrl(Url()); // don't save url in custom creds file
384     CredentialManager::CredentialSet creds;
385     creds.insert(c_ptr);
386
387     int ret;
388     if (credFile.absolute())
389       ret = save_creds_in_file(creds, credFile, 0640);
390     else
391       ret = save_creds_in_file(
392           creds, _pimpl->_options.customCredFileDir / credFile, 0600);
393
394     if (!ret)
395     {
396       //! \todo figure out the reason(?), call back to user
397       ERR << "error saving the credentials" << endl;
398     }
399   }
400
401
402   void CredentialManager::clearAll(bool global)
403   {
404     if (global)
405     {
406       if (!filesystem::unlink(_pimpl->_options.globalCredFilePath))
407         ERR << "could not delete user credentials file "
408             << _pimpl->_options.globalCredFilePath << endl;
409       _pimpl->_credsUser.clear();
410     }
411     else
412     {
413       if (!filesystem::unlink(_pimpl->_options.userCredFilePath))
414         ERR << "could not delete global credentials file"
415             << _pimpl->_options.userCredFilePath << endl;
416       _pimpl->_credsGlobal.clear();
417     }
418   }
419
420
421   CredentialManager::CredentialIterator CredentialManager::credsGlobalBegin() const
422   { return _pimpl->_credsGlobal.begin(); }
423
424   CredentialManager::CredentialIterator CredentialManager::credsGlobalEnd() const
425   { return _pimpl->_credsGlobal.end(); }
426
427   CredentialManager::CredentialSize CredentialManager::credsGlobalSize() const
428   { return _pimpl->_credsGlobal.size(); }
429
430   bool CredentialManager::credsGlobalEmpty() const
431   { return _pimpl->_credsGlobal.empty(); }
432
433
434   CredentialManager::CredentialIterator CredentialManager::credsUserBegin() const
435   { return _pimpl->_credsUser.begin(); }
436
437   CredentialManager::CredentialIterator CredentialManager::credsUserEnd() const
438   { return _pimpl->_credsUser.end(); }
439
440   CredentialManager::CredentialSize CredentialManager::credsUserSize() const
441   { return _pimpl->_credsUser.size(); }
442
443   bool CredentialManager::credsUserEmpty() const
444   { return _pimpl->_credsUser.empty(); }
445
446
447     ////////////////////////////////////////////////////////////////////
448   } // media
449   //////////////////////////////////////////////////////////////////////
450   ////////////////////////////////////////////////////////////////////
451 } // zypp
452 //////////////////////////////////////////////////////////////////////