cutelyst  4.4.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
authentication.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "application.h"
6 #include "authentication_p.h"
7 #include "authenticationrealm.h"
8 #include "authenticationstore.h"
9 #include "context.h"
10 
11 #include <Cutelyst/Plugins/Session/session.h>
12 
13 #include <QLoggingCategory>
14 
15 Q_LOGGING_CATEGORY(C_AUTHENTICATION, "cutelyst.plugin.authentication", QtWarningMsg)
16 
17 using namespace Cutelyst;
18 
19 char *Authentication::defaultRealm = const_cast<char *>("cutelyst_authentication_default_realm");
21  const_cast<char *>("cutelyst_authentication_default_realm");
22 
23 static thread_local Authentication *auth = nullptr;
24 
25 #define AUTHENTICATION_USER QStringLiteral("_c_authentication_user")
26 #define AUTHENTICATION_USER_REALM QStringLiteral("_c_authentication_user_realm")
27 
29  : Plugin(parent)
30  , d_ptr(new AuthenticationPrivate)
31 {
32  qRegisterMetaType<AuthenticationUser>();
33 }
34 
36 {
37  delete d_ptr;
38 }
39 
40 void Authentication::addRealm(std::shared_ptr<Cutelyst::AuthenticationRealm> realm)
41 {
42  Q_D(Authentication);
43  realm->setParent(nullptr);
44  d->realms.insert(realm->objectName(), realm);
45  d->realmsOrder.append(realm->objectName());
46 }
47 
49  std::shared_ptr<Cutelyst::AuthenticationStore> store,
50  std::shared_ptr<Cutelyst::AuthenticationCredential> credential,
51  const QString &name)
52 {
53  addRealm(std::make_shared<AuthenticationRealm>(store, credential, name));
54 }
55 
56 std::shared_ptr<Cutelyst::AuthenticationRealm> Authentication::realm(const QString &name) const
57 {
58  Q_D(const Authentication);
59  return d->realms.value(name);
60 }
61 
63  const ParamsMultiMap &userinfo,
64  const QString &realm)
65 {
66  if (!auth) {
67  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
68  return false;
69  }
70 
71  std::shared_ptr<AuthenticationRealm> realmPtr = auth->d_ptr->realm(realm);
72  if (realmPtr) {
73  const AuthenticationUser user = realmPtr->authenticate(c, userinfo);
74  if (!user.isNull()) {
75  AuthenticationPrivate::setAuthenticated(c, user, realm, realmPtr);
76  }
77 
78  return !user.isNull();
79  }
80 
81  qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
82  return false;
83 }
84 
86  const ParamsMultiMap &userinfo,
87  const QString &realm)
88 {
90  if (!auth) {
91  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
92  return ret;
93  }
94 
95  auto realmPtr = auth->d_ptr->realm(realm);
96  if (!realmPtr) {
97  qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
98  return ret;
99  }
100 
101  ret = realmPtr->findUser(c, userinfo);
102  return ret;
103 }
104 
106 {
107  AuthenticationUser ret;
108  const QVariant user = c->stash(AUTHENTICATION_USER);
109  if (user.isNull()) {
110  ret = AuthenticationPrivate::restoreUser(c, QVariant(), QString());
111  } else {
112  ret = user.value<AuthenticationUser>();
113  }
114  return ret;
115 }
116 
118 {
119  if (!c->stash(AUTHENTICATION_USER).isNull()) {
120  return true;
121  } else {
122  if (auth) {
123  if (AuthenticationPrivate::findRealmForPersistedUser(
124  c, auth->d_ptr->realms, auth->d_ptr->realmsOrder)) {
125  return true;
126  }
127  } else {
128  qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
129  }
130  return false;
131  }
132 }
133 
135 {
136  const QVariant user = c->stash(AUTHENTICATION_USER);
137  if (!user.isNull()) {
138  return user.value<AuthenticationUser>().authRealm() == realmName;
139  } else {
140  if (!auth) {
141  qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
142  return false;
143  }
144 
145  auto realm = AuthenticationPrivate::findRealmForPersistedUser(
146  c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
147  if (realm) {
148  return realm->name() == realmName;
149  } else {
150  return false;
151  }
152  }
153 }
154 
156 {
157  AuthenticationPrivate::setUser(c, AuthenticationUser());
158 
159  if (auth) {
160  auto realm = AuthenticationPrivate::findRealmForPersistedUser(
161  c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
162  if (realm) {
163  realm->removePersistedUser(c);
164  }
165  } else {
166  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
167  }
168 }
169 
171 {
172  return connect(app, &Application::postForked, this, [this] { auth = this; });
173 }
174 
175 std::shared_ptr<AuthenticationRealm> AuthenticationPrivate::realm(const QString &realmName) const
176 {
177  return realms.value(realmName.isNull() ? defaultRealm : realmName);
178 }
179 
180 AuthenticationUser AuthenticationPrivate::restoreUser(Context *c,
181  const QVariant &frozenUser,
182  const QString &realmName)
183 {
184  AuthenticationUser ret;
185  if (!auth) {
186  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
187  return ret;
188  }
189 
190  auto realmPtr = auth->d_ptr->realm(realmName);
191  if (!realmPtr) {
192  realmPtr = AuthenticationPrivate::findRealmForPersistedUser(
193  c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
194  }
195 
196  if (!realmPtr) {
197  return ret;
198  }
199 
200  ret = realmPtr->restoreUser(c, frozenUser);
201 
202  AuthenticationPrivate::setUser(c, ret);
203 
204  return ret;
205 }
206 
207 std::shared_ptr<AuthenticationRealm> AuthenticationPrivate::findRealmForPersistedUser(
208  Context *c,
209  const QMap<QString, std::shared_ptr<AuthenticationRealm>> &realms,
210  const QStringList &realmsOrder)
211 {
212  const QVariant realmVariant = Session::value(c, AUTHENTICATION_USER_REALM);
213  if (!realmVariant.isNull()) {
214  std::shared_ptr<AuthenticationRealm> realm = realms.value(realmVariant.toString());
215  if (realm && !realm->userIsRestorable(c).isNull()) {
216  return realm;
217  }
218  } else {
219  // we have no choice but to ask each realm whether it has a persisted user.
220  for (const QString &realmName : realmsOrder) {
221  std::shared_ptr<AuthenticationRealm> realm = realms.value(realmName);
222  if (realm && !realm->userIsRestorable(c).isNull()) {
223  return realm;
224  }
225  }
226  }
227 
228  return nullptr;
229 }
230 
231 void AuthenticationPrivate::setAuthenticated(Context *c,
232  const AuthenticationUser &user,
233  const QString &realmName,
234  std::shared_ptr<AuthenticationRealm> realm)
235 {
236  AuthenticationPrivate::setUser(c, user, realmName);
237 
238  if (!realm) {
239  qCWarning(C_AUTHENTICATION) << "Called with invalid realm" << realmName;
240  }
241 
242  AuthenticationPrivate::persistUser(c, user, realmName, realm);
243 }
244 
245 void AuthenticationPrivate::setUser(Context *c,
246  const AuthenticationUser &user,
247  const QString &realmName)
248 {
249  if (user.isNull()) {
250  c->setStash(AUTHENTICATION_USER, QVariant());
251  c->setStash(AUTHENTICATION_USER_REALM, QVariant());
252  } else {
253  c->setStash(AUTHENTICATION_USER, QVariant::fromValue(user));
254  c->setStash(AUTHENTICATION_USER_REALM, realmName);
255  }
256 }
257 
258 void AuthenticationPrivate::persistUser(Context *c,
259  const AuthenticationUser &user,
260  const QString &realmName,
261  std::shared_ptr<AuthenticationRealm> realm)
262 {
263  if (Authentication::userInRealm(c, realmName)) {
264  Session::setValue(c, AUTHENTICATION_USER_REALM, realmName);
265 
266  if (realm) {
267  realm->persistUser(c, user);
268  }
269  }
270 }
271 
273  : QObject(parent)
274 {
275 }
276 
278 {
279 }
280 
281 #include "moc_authentication.cpp"
The Cutelyst application.
Definition: application.h:66
void postForked(Cutelyst::Application *app)
AuthenticationCredential(QObject *parent=nullptr)
Container for user data retrieved from an AuthenticationStore.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Main class to manage user authentication.
void addRealm(std::shared_ptr< AuthenticationRealm > realm)
static bool userInRealm(Context *c, const QString &realmName=QLatin1String(defaultRealm))
std::shared_ptr< AuthenticationRealm > realm(const QString &name=QLatin1String(defaultRealm)) const
static bool userExists(Context *c)
virtual ~Authentication() override
virtual bool setup(Application *app) override
static void logout(Context *c)
static bool authenticate(Context *c, const ParamsMultiMap &userinfo, const QString &realm=QLatin1String(defaultRealm))
static char * defaultRealm
Authentication(Application *parent)
static AuthenticationUser user(Context *c)
static AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo, const QString &realm=QLatin1String(defaultRealm))
The Cutelyst Context.
Definition: context.h:42
void stash(const QVariantHash &unite)
Definition: context.cpp:562
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:212
Base class for Cutelyst Plugins.
Definition: plugin.h:25
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition: session.cpp:168
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition: session.cpp:183
The Cutelyst namespace holds all public Cutelyst API.
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isNull() const const
QVariant fromValue(T &&value)
bool isNull() const const
QString toString() const const