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