ssu
ssuurlresolver.cpp
Go to the documentation of this file.
1 
8 #include "ssuurlresolver.h"
9 
10 #include <QCoreApplication>
11 #include <QFileInfo>
12 #include <QStringList>
13 #include <QUrl>
14 #include <systemd/sd-journal.h>
15 
16 #include "libssu/sandbox_p.h"
17 #include "libssu/ssulog_p.h"
18 
19 SsuUrlResolver::SsuUrlResolver()
20  : QObject()
21 {
22  QObject::connect(this, SIGNAL(done()),
23  QCoreApplication::instance(), SLOT(quit()),
24  Qt::QueuedConnection);
25 }
26 
27 void SsuUrlResolver::error(const QString &message)
28 {
29  SsuLog *ssuLog = SsuLog::instance();
30  ssuLog->print(LOG_WARNING, message);
31 
32  PluginFrame out("ERROR");
33  out.setBody(message.toStdString());
34  out.writeTo(std::cout);
35  QCoreApplication::exit(1);
36 }
37 
38 void SsuUrlResolver::ack() const
39 {
40  zypp::PluginFrame out(zypp::PluginFrame::ackCommand());
41  out.writeTo(std::cout);
42 }
43 
44 bool SsuUrlResolver::writeZyppCredentialsIfNeeded(const QString &credentialsScope)
45 {
46  QString filePath = Sandbox::map("/etc/zypp/credentials.d/" + credentialsScope);
47  QFileInfo credentialsFileInfo(filePath);
48 
51  if (credentialsFileInfo.exists() &&
52  credentialsFileInfo.lastModified() > ssu.lastCredentialsUpdate() &&
53  credentialsScope != "store") {
54  // zypp credentials up to date
55  return true;
56  }
57 
58  QFile credentialsFile(filePath);
59  QPair<QString, QString> credentials = ssu.credentials(credentialsScope);
60  SsuLog *ssuLog = SsuLog::instance();
61 
62  if (credentials.first.isEmpty() || credentials.second.isEmpty()) {
63  ssuLog->print(LOG_WARNING, "Returned credentials are empty, skip writing");
64  return false;
65  }
66 
67  if (!credentialsFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
68  ssuLog->print(LOG_WARNING, "Unable to open credentials file for writing");
69  return false;
70  }
71 
72  QTextStream out(&credentialsFile);
73  out << "[" << ssu.credentialsUrl(credentialsScope) << "]\n";
74  out << "username=" << credentials.first << "\n";
75  out << "password=" << credentials.second << "\n";
76  out.flush();
77  credentialsFile.close();
78  return true;
79 }
80 
81 void SsuUrlResolver::run()
82 {
83  while (true) {
84  zypp::PluginFrame inputFrame(std::cin);
85  if (inputFrame.command() == "RESOLVEURL") {
86  resolve(inputFrame);
87  } else if (inputFrame.command() == "_DISCONNECT") {
88  ack();
89  break;
90  } else {
91  error(QStringLiteral("Unknown plugin command"));
92  break;
93  }
94  }
95  emit done();
96 }
97 
98 void SsuUrlResolver::resolve(PluginFrame &in)
99 {
100  QHash<QString, QString> repoParameters;
101  QString repo;
102  bool isRnd = false;
103  SsuLog *ssuLog = SsuLog::instance();
104 
105  if (in.headerEmpty()) {
106  error("Received empty header list. Most likely your ssu setup is broken");
107  }
108 
109  PluginFrame::HeaderListIterator it;
110  QStringList headerList;
111 
112  /*
113  hasKey() for some reason makes zypper run into timeouts, so we have
114  to handle special keys here
115  */
116  for (it = in.headerBegin(); it != in.headerEnd(); it++) {
117  QString first = QString::fromStdString((*it).first);
118  QString second = QString::fromStdString((*it).second);
119 
120  if (first == "repo") {
121  repo = second;
122  } else if (first == "rnd") {
123  isRnd = true;
124  } else if (first == "deviceFamily") {
125  repoParameters.insert(first, second);
126  } else if (first == "arch") {
127  repoParameters.insert(first, second);
128  } else if (first == "debug") {
129  repoParameters.insert("debugSplit", "debug");
130  } else {
131  if ((*it).second.empty())
132  headerList.append(first);
133  else
134  headerList.append(QString("%1=%2")
135  .arg(first)
136  .arg(second));
137  }
138  }
139 
140  if (!ssu.useSslVerify())
141  headerList.append("ssl_verify=no");
142 
143  if (ssu.isRegistered()) {
144  SignalWait w;
145  connect(&ssu, SIGNAL(done()), &w, SLOT(finished()));
146  ssu.updateCredentials();
147  w.sleep();
148 
149  // error can be found in ssu.log, so just exit
150  // TODO: figure out if there's better eror handling for
151  // zypper plugins than 'blow up'
152  if (ssu.error()) {
153  error(ssu.lastError());
154  }
155  } else {
156  ssuLog->print(LOG_DEBUG, "Device not registered -- skipping credential update");
157  }
158 
159  // resolve base url
160  QString resolvedUrl = ssu.repoUrl(repo, isRnd, repoParameters);
161 
162  QString credentialsScope = ssu.credentialsScope(repo, isRnd);
163  // only do credentials magic on secure connections
164  if (resolvedUrl.startsWith("https://") && !credentialsScope.isEmpty() &&
165  (ssu.isRegistered() || credentialsScope == "store")) {
166  // TODO: check for credentials scope required for repository; check if the file exists;
167  // compare with configuration, and dump credentials to file if necessary
168  ssuLog->print(LOG_DEBUG, QString("Requesting credentials for '%1' with RND status %2...").arg(repo).arg(isRnd));
169 
170  // personal store repositories as well as the ones listed in the
171  // store-auth-repos domain setting use store credentials. Refresh
172  // here, as we only know after checking scope if we need to have
173  // store credentials at all
174  if (credentialsScope == "store") {
176  if (ssu.error())
177  error (ssu.lastError());
178  }
179  headerList.append(QString("credentials=%1").arg(credentialsScope));
180  writeZyppCredentialsIfNeeded(credentialsScope);
181  } else {
182  ssuLog->print(LOG_DEBUG, QString("Skipping credential for %1 with scope %2").arg(repo).arg(credentialsScope));
183  }
184 
185  if (!headerList.isEmpty() && !resolvedUrl.isEmpty()) {
186  QUrl url(resolvedUrl);
187 
188  if (url.hasQuery()) {
189  url.setQuery(url.query() + "&" + headerList.join("&"));
190  } else
191  url.setQuery(headerList.join("&"));
192 
193  resolvedUrl = url.toString();
194  }
195 
196  // TODO, we should bail out here if the configuration specifies that the repo
197  // is protected, but device is not registered and/or we don't have credentials
198  ssuLog->print(LOG_INFO, QString("%1 resolved to %2").arg(repo).arg(resolvedUrl));
199 
200  if (resolvedUrl.isEmpty()) {
201  error("URL for repository is not set.");
202  } else if (resolvedUrl.indexOf(QRegExp("[a-z]*://", Qt::CaseInsensitive)) != 0) {
203  error("URL for repository is invalid.");
204  } else {
205  PluginFrame out("RESOLVEDURL");
206  out.setBody(resolvedUrl.toStdString());
207  out.writeTo(std::cout);
208  }
209 }
SignalWait
Definition: ssuurlresolver.h:23
Ssu::updateCredentials
void updateCredentials(bool force=false)
Definition: ssu.cpp:628
Ssu::credentialsUrl
QString credentialsUrl(const QString &scope)
Definition: ssu.cpp:144
SsuLog
Definition: ssulog_p.h:15
Ssu::useSslVerify
Q_INVOKABLE bool useSslVerify()
See SsuCoreConfig::useSslVerify.
Definition: ssu.cpp:221
Ssu::isRegistered
Q_INVOKABLE bool isRegistered()
See SsuCoreConfig::isRegistered.
Definition: ssu.cpp:179
Ssu::credentials
QPair< QString, QString > credentials(const QString &scope)
Definition: ssu.cpp:95
Ssu::lastCredentialsUpdate
Q_INVOKABLE QDateTime lastCredentialsUpdate()
See SsuCoreConfig::lastCredentialsUpdate.
Definition: ssu.cpp:185
Ssu::updateStoreCredentials
void updateStoreCredentials()
Definition: ssu.cpp:704
ssuurlresolver.h
Ssu::repoUrl
QString repoUrl(const QString &repoName, bool rndRepo=false, QHash< QString, QString > repoParameters=QHash< QString, QString >(), QHash< QString, QString > parametersOverride=QHash< QString, QString >())
Definition: ssu.cpp:312
Ssu::credentialsScope
QString credentialsScope(const QString &repoName, bool rndRepo=false)
Definition: ssu.cpp:101
Ssu::lastError
Q_INVOKABLE QString lastError()
Definition: ssu.cpp:231
SsuLog::print
void print(int priority, const QString &message)
Definition: ssulog.cpp:27
sandbox_p.h
Ssu::error
Q_INVOKABLE bool error()
Definition: ssu.cpp:150
ssulog_p.h