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