cutelyst 4.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(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}
35
36Authentication::~Authentication()
37{
38 delete d_ptr;
39}
40
41void Authentication::addRealm(std::shared_ptr<Cutelyst::AuthenticationRealm> realm)
42{
43 Q_D(Authentication);
44 realm->setParent(nullptr);
45 d->realms.insert(realm->objectName(), realm);
46 d->realmsOrder.append(realm->objectName());
47}
48
50 std::shared_ptr<Cutelyst::AuthenticationStore> store,
51 std::shared_ptr<Cutelyst::AuthenticationCredential> credential,
52 const QString &name)
53{
54 addRealm(std::make_shared<AuthenticationRealm>(store, credential, name));
55}
56
57std::shared_ptr<Cutelyst::AuthenticationRealm> Authentication::realm(const QString &name) const
58{
59 Q_D(const Authentication);
60 return d->realms.value(name);
61}
62
64 const ParamsMultiMap &userinfo,
65 const QString &realm)
66{
67 if (!auth) {
68 qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
69 return false;
70 }
71
72 std::shared_ptr<AuthenticationRealm> realmPtr = auth->d_ptr->realm(realm);
73 if (realmPtr) {
74 const AuthenticationUser user = realmPtr->authenticate(c, userinfo);
75 if (!user.isNull()) {
76 AuthenticationPrivate::setAuthenticated(c, user, realm, realmPtr);
77 }
78
79 return !user.isNull();
80 }
81
82 qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
83 return false;
84}
85
87 const ParamsMultiMap &userinfo,
88 const QString &realm)
89{
91 if (!auth) {
92 qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
93 return ret;
94 }
95
96 auto realmPtr = auth->d_ptr->realm(realm);
97 if (!realmPtr) {
98 qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
99 return ret;
100 }
101
102 ret = realmPtr->findUser(c, userinfo);
103 return ret;
104}
105
107{
109 const QVariant user = c->stash(AUTHENTICATION_USER);
110 if (user.isNull()) {
111 ret = AuthenticationPrivate::restoreUser(c, QVariant(), QString());
112 } else {
113 ret = user.value<AuthenticationUser>();
114 }
115 return ret;
116}
117
119{
120 if (!c->stash(AUTHENTICATION_USER).isNull()) {
121 return true;
122 } else {
123 if (auth) {
124 if (AuthenticationPrivate::findRealmForPersistedUser(
125 c, auth->d_ptr->realms, auth->d_ptr->realmsOrder)) {
126 return true;
127 }
128 } else {
129 qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
130 }
131 return false;
132 }
133}
134
135bool Authentication::userInRealm(Cutelyst::Context *c, const QString &realmName)
136{
137 const QVariant user = c->stash(AUTHENTICATION_USER);
138 if (!user.isNull()) {
139 return user.value<AuthenticationUser>().authRealm() == realmName;
140 } else {
141 if (!auth) {
142 qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
143 return false;
144 }
145
146 auto realm = AuthenticationPrivate::findRealmForPersistedUser(
147 c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
148 if (realm) {
149 return realm->name() == realmName;
150 } else {
151 return false;
152 }
153 }
154}
155
157{
158 AuthenticationPrivate::setUser(c, AuthenticationUser());
159
160 if (auth) {
161 auto realm = AuthenticationPrivate::findRealmForPersistedUser(
162 c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
163 if (realm) {
164 realm->removePersistedUser(c);
165 }
166 } else {
167 qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
168 }
169}
170
172{
173 return connect(app, &Application::postForked, this, [this] { auth = this; });
174}
175
176std::shared_ptr<AuthenticationRealm> AuthenticationPrivate::realm(const QString &realmName) const
177{
178 return realms.value(realmName.isNull() ? defaultRealm : realmName);
179}
180
181AuthenticationUser AuthenticationPrivate::restoreUser(Context *c,
182 const QVariant &frozenUser,
183 const QString &realmName)
184{
186 if (!auth) {
187 qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
188 return ret;
189 }
190
191 auto realmPtr = auth->d_ptr->realm(realmName);
192 if (!realmPtr) {
193 realmPtr = AuthenticationPrivate::findRealmForPersistedUser(
194 c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
195 }
196
197 if (!realmPtr) {
198 return ret;
199 }
200
201 ret = realmPtr->restoreUser(c, frozenUser);
202
203 AuthenticationPrivate::setUser(c, ret);
204
205 return ret;
206}
207
208std::shared_ptr<AuthenticationRealm> AuthenticationPrivate::findRealmForPersistedUser(
209 Context *c,
210 const QMap<QString, std::shared_ptr<AuthenticationRealm>> &realms,
211 const QStringList &realmsOrder)
212{
213 const QVariant realmVariant = Session::value(c, AUTHENTICATION_USER_REALM);
214 if (!realmVariant.isNull()) {
215 std::shared_ptr<AuthenticationRealm> realm = realms.value(realmVariant.toString());
216 if (realm && !realm->userIsRestorable(c).isNull()) {
217 return realm;
218 }
219 } else {
220 // we have no choice but to ask each realm whether it has a persisted user.
221 for (const QString &realmName : realmsOrder) {
222 std::shared_ptr<AuthenticationRealm> realm = realms.value(realmName);
223 if (realm && !realm->userIsRestorable(c).isNull()) {
224 return realm;
225 }
226 }
227 }
228
229 return nullptr;
230}
231
232void AuthenticationPrivate::setAuthenticated(Context *c,
233 const AuthenticationUser &user,
234 const QString &realmName,
235 std::shared_ptr<AuthenticationRealm> realm)
236{
237 AuthenticationPrivate::setUser(c, user, realmName);
238
239 if (!realm) {
240 qCWarning(C_AUTHENTICATION) << "Called with invalid realm" << realmName;
241 }
242
243 AuthenticationPrivate::persistUser(c, user, realmName, realm);
244}
245
246void AuthenticationPrivate::setUser(Context *c,
247 const AuthenticationUser &user,
248 const QString &realmName)
249{
250 if (user.isNull()) {
251 c->setStash(AUTHENTICATION_USER, QVariant());
252 c->setStash(AUTHENTICATION_USER_REALM, QVariant());
253 } else {
254 c->setStash(AUTHENTICATION_USER, QVariant::fromValue(user));
255 c->setStash(AUTHENTICATION_USER_REALM, realmName);
256 }
257}
258
259void AuthenticationPrivate::persistUser(Context *c,
260 const AuthenticationUser &user,
261 const QString &realmName,
262 std::shared_ptr<AuthenticationRealm> realm)
263{
264 if (Authentication::userInRealm(c, realmName)) {
265 Session::setValue(c, AUTHENTICATION_USER_REALM, realmName);
266
267 if (realm) {
268 realm->persistUser(c, user);
269 }
270 }
271}
272
274 : QObject(parent)
275{
276}
277
278Cutelyst::AuthenticationCredential::~AuthenticationCredential()
279{
280}
281
282#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.
static char * defaultRealm
default realm name
bool isNull() const
Returns true if the object is null.
void addRealm(std::shared_ptr< AuthenticationRealm > realm)
Adds the realm with name.
static bool userInRealm(Context *c, const QString &realmName=QLatin1String(defaultRealm))
std::shared_ptr< AuthenticationRealm > realm(const QString &name=QLatin1String(defaultRealm)) const
Returns an AuthenticationRealm object that was registered with name.
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...
The Cutelyst Context.
Definition: context.h:38
void stash(const QVariantHash &unite)
Definition: context.cpp:553
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:211
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition: session.cpp:158
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition: session.cpp:173
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap