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
15Q_LOGGING_CATEGORY(CUTELYST_UTILS_AUTH, "cutelyst.utils.auth", QtWarningMsg)
16Q_LOGGING_CATEGORY(C_AUTHENTICATION, "cutelyst.plugin.authentication", QtWarningMsg)
17
18using namespace Cutelyst;
19
20char *Authentication::defaultRealm = const_cast<char *>("cutelyst_authentication_default_realm");
22 const_cast<char *>("cutelyst_authentication_default_realm");
23
24static 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
39Authentication::~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{
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) {
166 realm->removePersistedUser(c);
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
178AuthenticationRealm *AuthenticationPrivate::realm(const QString &realmName) const
179{
180 return realms.value(realmName.isNull() ? defaultRealm : realmName);
181}
182
183AuthenticationUser AuthenticationPrivate::restoreUser(Context *c,
184 const QVariant &frozenUser,
185 const QString &realmName)
186{
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
210AuthenticationRealm *AuthenticationPrivate::findRealmForPersistedUser(
211 Context *c,
212 const QMap<QString, AuthenticationRealm *> &realms,
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
234void 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
248void 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
261void 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
279
280Cutelyst::AuthenticationCredential::~AuthenticationCredential()
281{
282}
283
284#include "moc_authentication.cpp"
The Cutelyst Application.
Definition application.h:43
void postForked(Cutelyst::Application *app)
AuthenticationCredential(QObject *parent=nullptr)
Constructs a new AuthenticationCredential object with the given parent.
virtual AuthenticationUser authenticate(Context *c, const ParamsMultiMap &authinfo)
Tries to authenticate the user with authinfo returning a non null AuthenticationUser on success.
static char * defaultRealm
default realm name
QVariant userIsRestorable(Context *c)
Checks if user can be retrieved.
AuthenticationUser persistUser(Context *c, const AuthenticationUser &user)
Stores the user on the session.
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Tries to find the user with authinfo returning a non null AuthenticationUser on success.
AuthenticationUser restoreUser(Context *c, const QVariant &frozenUser)
Retrieves the user from the store.
bool isNull() const
Returns true if the object is null.
void addRealm(AuthenticationRealm *realm)
Adds the realm with name.
static bool userInRealm(Context *c, const QString &realmName=QLatin1String(defaultRealm))
static bool userExists(Context *c)
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
default realm name
Authentication(Application *parent)
Constructs a new Authentication object with the given parent.
static AuthenticationUser user(Context *c)
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...
AuthenticationRealm * realm(const QString &name=QLatin1String(defaultRealm)) const
Returns an AuthenticationRealm object that was registered with name.
The Cutelyst Context.
Definition context.h:39
void stash(const QVariantHash &unite)
Definition context.cpp:566
void setStash(const QString &key, const QVariant &value)
Definition context.cpp:217
Plugin(Application *parent)
Definition plugin.cpp:12
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition session.cpp:170
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:8
QMultiMap< QString, QString > ParamsMultiMap
const T value(const Key &key, const T &defaultValue) const const
QObject(QObject *parent)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
bool isNull() const const
QVariant fromValue(const T &value)
bool isNull() const const
QString toString() const const