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