cutelyst 4.3.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
15Q_LOGGING_CATEGORY(C_AUTHENTICATION, "cutelyst.plugin.authentication", QtWarningMsg)
16
17using namespace Cutelyst;
18
19char *Authentication::defaultRealm = const_cast<char *>("cutelyst_authentication_default_realm");
21 const_cast<char *>("cutelyst_authentication_default_realm");
22
23static 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
40void 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
56std::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{
108 const QVariant user = c->stash(AUTHENTICATION_USER);
109 if (user.isNull()) {
110 ret = AuthenticationPrivate::restoreUser(c, QVariant(), QString());
111 } else {
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
175std::shared_ptr<AuthenticationRealm> AuthenticationPrivate::realm(const QString &realmName) const
176{
177 return realms.value(realmName.isNull() ? defaultRealm : realmName);
178}
179
180AuthenticationUser AuthenticationPrivate::restoreUser(Context *c,
181 const QVariant &frozenUser,
182 const QString &realmName)
183{
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
207std::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
231void 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
245void 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
258void 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
276
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))
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(const T &value)
bool isNull() const const
QString toString() const const