swift
databaseauthentication.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QByteArray>
7 #include <QJsonObject>
8 #include <QNetworkReply>
9 #include <QNetworkRequest>
10 #include <QScopedPointer>
11 #include <QScopedPointerDeleteLater>
12 #include <QString>
13 #include <QUrl>
14 #include <QUrlQuery>
15 
16 #include "core/application.h"
17 #include "core/data/globalsetup.h"
18 #include "misc/json.h"
19 #include "misc/logcategories.h"
20 #include "misc/logmessage.h"
23 #include "misc/network/rolelist.h"
24 #include "misc/network/url.h"
25 #include "misc/statusmessage.h"
26 
27 using namespace swift::misc;
28 using namespace swift::misc::network;
29 
30 namespace swift::core::db
31 {
32  const QStringList &CDatabaseAuthenticationService::getLogCategories()
33  {
34  static const QStringList cats { CLogCategories::swiftDbWebservice() };
35  return cats;
36  }
37 
38  CDatabaseAuthenticationService::CDatabaseAuthenticationService(QObject *parent) : QObject(parent)
39  {
40  // void
41  }
42 
44  {
45  if (m_shutdown) { return; }
46  m_shutdown = true;
47  this->logoff();
48  }
49 
51 
53  {
54  const CAuthenticatedUser user(this->getDbUser());
55  return user.isAuthenticated();
56  }
57 
58  CStatusMessageList CDatabaseAuthenticationService::login(const QString &username, const QString &password)
59  {
60  CStatusMessageList msgs;
61  static const CLogCategoryList cats(CLogCategoryList(this).withValidation());
62 
63  if (!sApp || m_shutdown)
64  {
65  msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"Shutdown in progress"));
66  return msgs;
67  }
68 
69  const QString un(username.trimmed());
70  const QString pw(password.trimmed());
71  if (un.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"No user name/id")); }
72  if (pw.isEmpty()) { msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"No password")); }
73  if (!msgs.isEmpty()) { return msgs; }
74 
76  QString msg;
77  if (!CNetworkUtils::canConnect(url, msg))
78  {
80  return msgs;
81  }
82 
83  QUrlQuery params;
84  params.addQueryItem("username", un);
85  params.addQueryItem("password", pw);
86  if (sApp->getGlobalSetup().dbDebugFlag()) { CNetworkUtils::addDebugFlag(params); }
87 
88  const QString query = params.toString();
89  const QNetworkRequest request(CNetworkUtils::getSwiftNetworkRequest(url, CNetworkUtils::PostUrlEncoded,
91  sApp->postToNetwork(request, CApplication::NoLogRequestId, query.toUtf8(),
92  { this, &CDatabaseAuthenticationService::parseServerResponse });
93  static const QString rm("Sent request to authentication server '%1'");
95  return msgs;
96  }
97 
99  {
100  if (!sApp) { return; }
102  url.setQuery("logoff=true");
103  QNetworkRequest request(CNetworkUtils::getSwiftNetworkRequest(url));
104  sApp->getFromNetwork(request, { this, &CDatabaseAuthenticationService::parseServerResponse });
105  m_swiftDbUser.set(CAuthenticatedUser());
106  }
107 
108  void CDatabaseAuthenticationService::parseServerResponse(QNetworkReply *nwReplyPtr)
109  {
110  // always cleanup reply
111  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
112 
113  if (m_shutdown) { return; }
114  QString urlString(nwReply->url().toString());
115  if (urlString.contains("logoff", Qt::CaseInsensitive))
116  {
118  emit this->logoffFinished();
119  return;
120  }
121 
122  if (nwReply->error() == QNetworkReply::NoError)
123  {
124  const QString json(nwReply->readAll().trimmed());
125  if (json.isEmpty())
126  {
127  CLogMessage(this).error(u"Authentication failed, no response from '%1'") << urlString;
128  return;
129  }
130  if (!json::looksLikeJson(json))
131  {
132  CLogMessage(this).error(u"Illegal JSON object: %1")
133  << CNetworkUtils::removeHtmlPartsFromPhpErrorMessage(json);
134  return;
135  }
136 
137  static const CLogCategoryList cats(CLogCategoryList(this).withValidation());
138  const QJsonObject jsonObj(json::jsonObjectFromString(json));
139  CAuthenticatedUser user =
140  CAuthenticatedUser::fromDatabaseJson(jsonObj.contains("user") ? jsonObj["user"].toObject() : jsonObj);
141  CStatusMessageList msgs;
142  if (jsonObj.contains("messages"))
143  {
144  msgs = CStatusMessageList::fromDatabaseJson(jsonObj["messages"].toArray());
145  msgs.setCategories(cats);
146  }
147 
148  // allow auto enabled for SSO users
149  if (user.isValid() && !user.isEnabled())
150  {
151  if (user.getRoles().hasRole("VATSIMUSER"))
152  {
153  user.setEnabled(true);
154  msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityInfo, u"Auto enabled SSO user"));
155  }
156  }
157 
158  if (!user.isAuthenticated() || !user.isValid())
159  {
160  if (!msgs.hasErrorMessages())
161  {
162  msgs.push_back(
163  CStatusMessage(cats, CStatusMessage::SeverityError, u"Cannot login, user or password wrong"));
164  }
165  }
166  else
167  {
168  if (!user.isEnabled())
169  {
170  msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"User is disabled"));
171  }
172  if (user.getRoles().isEmpty())
173  {
174  msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, u"User has no roles"));
175  }
176  }
177  m_swiftDbUser.set(user);
178  emit userAuthenticationFinished(user, msgs);
179  }
180  else
181  {
182  CLogMessage(this).error(u"Authentication failed, %1") << nwReply->errorString();
183  return;
184  }
185  }
186 
187  void CDatabaseAuthenticationService::userChanged()
188  {
189  // this->logoff();
190  }
191 } // namespace swift::core::db
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
QNetworkReply * getFromNetwork(const swift::misc::network::CUrl &url, const CallbackSlot &callback, int maxRedirects=DefaultMaxRedirects)
Request to get network reply.
data::CGlobalSetup getGlobalSetup() const
Global setup.
static constexpr int NoLogRequestId
network request without logging
Definition: application.h:413
QNetworkReply * postToNetwork(const QNetworkRequest &request, int logId, const QByteArray &data, const CallbackSlot &callback)
Post to network.
void deleteAllCookies()
Delete all cookies from cookie manager.
const QString & getApplicationNameAndVersion() const
Application name and version.
bool dbDebugFlag() const
Debug flag.
Definition: globalsetup.cpp:68
swift::misc::network::CUrl getDbLoginServiceUrl() const
Login service.
Definition: globalsetup.cpp:61
void userAuthenticationFinished(const swift::misc::network::CAuthenticatedUser &user, const swift::misc::CStatusMessageList &loginStatus)
User authenticated.
swift::misc::CStatusMessageList login(const QString &id, const QString &password)
Try to login to authentication web service.
swift::misc::network::CAuthenticatedUser getDbUser() const
DB user.
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
CStatusMessage set(const typename Trait::type &value, qint64 timestamp=0)
Write a new value. Must be called from the thread in which the owner lives.
Definition: datacache.h:350
static const QString & swiftDbWebservice()
Webservice with swift DB.
A sequence of log categories.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Streamable status message, e.g.
constexpr static auto SeverityError
Status severities.
constexpr static auto SeverityInfo
Status severities.
Status messages, e.g. from Core -> GUI.
void setCategories(const CLogCategoryList &categories)
Reset the categories of all messages in the list.
bool hasErrorMessages() const
Error messages.
static CStatusMessageList fromDatabaseJson(const QJsonArray &array)
From our database JSON format.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
Value object encapsulating information of an authentiated user.
bool isAuthenticated() const
Authenticated.
const CRoleList & getRoles() const
Roles.
bool isValid() const
Valid user object?
bool hasRole(const QString &roleName) const
Has role?
Definition: rolelist.cpp:16
Value object encapsulating information of a location, kind of simplified CValueObject compliant versi...
Definition: url.h:27
void setQuery(const QString &query)
Set query.
Definition: url.cpp:58
QJsonObject jsonObjectFromString(const QString &json, bool acceptCacheFormat)
JSON Object from string.
Definition: json.cpp:413
Classes interacting with the swift database (aka "datastore").
Free functions in swift::misc.