cutelyst 5.0.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;
18using namespace Qt::StringLiterals;
19
20char *Authentication::defaultRealm = const_cast<char *>("cutelyst_authentication_default_realm");
22 const_cast<char *>("cutelyst_authentication_default_realm");
23namespace {
24thread_local Authentication *auth = nullptr;
25
26const auto AUTHENTICATION_USER = u"_c_authentication_user"_s;
27const 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
42void 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 const QString &name)
54{
55 addRealm(std::make_shared<AuthenticationRealm>(store, credential, name));
56}
57
58std::shared_ptr<Cutelyst::AuthenticationRealm> Authentication::realm(const QString &name) const
59{
60 Q_D(const Authentication);
61 return d->realms.value(name);
62}
63
65 const ParamsMultiMap &userinfo,
66 const QString &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 user = realmPtr->authenticate(c, userinfo);
76 if (!user.isNull()) {
77 AuthenticationPrivate::setAuthenticated(c, user, realm, realmPtr);
78 }
79
80 return !user.isNull();
81 }
82
83 qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
84 return false;
85}
86
88 const ParamsMultiMap &userinfo,
89 const QString &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{
110 const QVariant user = c->stash(AUTHENTICATION_USER);
111 if (user.isNull()) {
112 ret = AuthenticationPrivate::restoreUser(c, {}, {});
113 } else {
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 user = c->stash(AUTHENTICATION_USER);
139 if (!user.isNull()) {
140 return user.value<AuthenticationUser>().authRealm() == realmName;
141 } else {
142 if (!auth) {
143 qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
144 return false;
145 }
146
147 auto realm = AuthenticationPrivate::findRealmForPersistedUser(
148 c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
149 if (realm) {
150 return realm->name() == realmName;
151 } else {
152 return false;
153 }
154 }
155}
156
158{
159 AuthenticationPrivate::setUser(c, AuthenticationUser());
160
161 if (auth) {
162 auto realm = AuthenticationPrivate::findRealmForPersistedUser(
163 c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
164 if (realm) {
165 realm->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
177std::shared_ptr<AuthenticationRealm> AuthenticationPrivate::realm(const QString &realmName) const
178{
179 return realms.value(realmName.isNull() ? defaultRealm : realmName);
180}
181
182AuthenticationUser AuthenticationPrivate::restoreUser(Context *c,
183 const QVariant &frozenUser,
184 const QString &realmName)
185{
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
209std::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> realm = realms.value(realmVariant.toString());
217 if (realm && !realm->userIsRestorable(c).isNull()) {
218 return realm;
219 }
220 } else {
221 // we have no choice but to ask each realm whether it has a persisted user.
222 for (const QString &realmName : realmsOrder) {
223 std::shared_ptr<AuthenticationRealm> realm = realms.value(realmName);
224 if (realm && !realm->userIsRestorable(c).isNull()) {
225 return realm;
226 }
227 }
228 }
229
230 return nullptr;
231}
232
233void AuthenticationPrivate::setAuthenticated(Context *c,
234 const AuthenticationUser &user,
235 const QString &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
247void AuthenticationPrivate::setUser(Context *c,
248 const AuthenticationUser &user,
249 const QString &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);
257 }
258}
259
260void AuthenticationPrivate::persistUser(Context *c,
261 const AuthenticationUser &user,
262 const QString &realmName,
263 std::shared_ptr<AuthenticationRealm> realm)
264{
265 if (Authentication::userInRealm(c, realmName)) {
266 Session::setValue(c, AUTHENTICATION_USER_REALM, realmName);
267
268 if (realm) {
269 realm->persistUser(c, user);
270 }
271 }
272}
273
278
282
283#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:563
void setStash(const QString &key, const QVariant &value)
Definition context.cpp:213
Base class for Cutelyst Plugins.
Definition plugin.h:25
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition session.cpp:171
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition session.cpp:186
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