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