Cutelyst  2.3.0
authentication.cpp
1 /*
2  * Copyright (C) 2013-2018 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "authentication_p.h"
19 
20 #include "authenticationstore.h"
21 #include "authenticationrealm.h"
22 
23 #include "context.h"
24 #include "application.h"
25 
26 #include <Cutelyst/Plugins/Session/session.h>
27 
28 #include <QLoggingCategory>
29 
30 Q_LOGGING_CATEGORY(CUTELYST_UTILS_AUTH, "cutelyst.utils.auth")
31 Q_LOGGING_CATEGORY(C_AUTHENTICATION, "cutelyst.plugin.authentication")
32 
33 using namespace Cutelyst;
34 
35 char *Authentication::defaultRealm = const_cast<char *>("cutelyst_authentication_default_realm");
36 char *AuthenticationRealm::defaultRealm = const_cast<char *>("cutelyst_authentication_default_realm");
37 
38 static thread_local Authentication *auth = nullptr;
39 
40 #define AUTHENTICATION_USER QStringLiteral("_c_authentication_user")
41 #define AUTHENTICATION_USER_REALM QStringLiteral("_c_authentication_user_realm")
42 
43 Authentication::Authentication(Application *parent) : Plugin(parent)
44  , d_ptr(new AuthenticationPrivate)
45 {
46  qRegisterMetaType<AuthenticationUser>();
47  qRegisterMetaTypeStreamOperators<AuthenticationUser>();
48 }
49 
50 Authentication::~Authentication()
51 {
52  delete d_ptr;
53 }
54 
56 {
57  Q_D(Authentication);
58  realm->setParent(this);
59  d->realms.insert(realm->objectName(), realm);
60  d->realmsOrder.append(realm->objectName());
61 }
62 
64 {
65  addRealm(new AuthenticationRealm(store, credential, name, this));
66 }
67 
68 AuthenticationRealm *Authentication::realm(const QString &name) const
69 {
70  Q_D(const Authentication);
71  return d->realms.value(name);
72 }
73 
74 bool Authentication::authenticate(Cutelyst::Context *c, const ParamsMultiMap &userinfo, const QString &realm)
75 {
76  if (!auth) {
77  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
78  return false;
79  }
80 
81  AuthenticationRealm *realmPtr = auth->d_ptr->realm(realm);
82  if (realmPtr) {
83  const AuthenticationUser user = realmPtr->authenticate(c, userinfo);
84  if (!user.isNull()) {
85  AuthenticationPrivate::setAuthenticated(c, user, realm, auth->d_ptr->realm(realm));
86  }
87 
88  return !user.isNull();
89  }
90 
91  qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
92  return false;
93 }
94 
96 {
98  if (!auth) {
99  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
100  return ret;
101  }
102 
103  AuthenticationRealm *realmPtr = auth->d_ptr->realm(realm);
104  if (!realmPtr) {
105  qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
106  return ret;
107  }
108 
109  ret = realmPtr->findUser(c, userinfo);
110  return ret;
111 }
112 
114 {
115  AuthenticationUser ret;
116  const QVariant user = c->stash(AUTHENTICATION_USER);
117  if (user.isNull()) {
118  ret = AuthenticationPrivate::restoreUser(c, QVariant(), QString());
119  } else {
120  ret = user.value<AuthenticationUser>();
121  }
122  return ret;
123 }
124 
126 {
127  if (!c->stash(AUTHENTICATION_USER).isNull()) {
128  return true;
129  } else {
130  if (auth) {
131  if (AuthenticationPrivate::findRealmForPersistedUser(c, auth->d_ptr->realms, auth->d_ptr->realmsOrder)) {
132  return true;
133  }
134  } else {
135  qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
136  }
137  return false;
138  }
139 }
140 
141 bool Authentication::userInRealm(Cutelyst::Context *c, const QString &realmName)
142 {
143  const QVariant user = c->stash(AUTHENTICATION_USER);
144  if (!user.isNull()) {
145  return user.value<AuthenticationUser>().authRealm() == realmName;
146  } else {
147  if (!auth) {
148  qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
149  return false;
150  }
151 
152  AuthenticationRealm *realm = AuthenticationPrivate::findRealmForPersistedUser(c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
153  if (realm) {
154  return realm->name() == realmName;
155  } else {
156  return false;
157  }
158  }
159 }
160 
162 {
163  AuthenticationPrivate::setUser(c, AuthenticationUser());
164 
165  if (auth) {
166  AuthenticationRealm *realm = AuthenticationPrivate::findRealmForPersistedUser(c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
167  if (realm) {
168  realm->removePersistedUser(c);
169  }
170  } else {
171  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
172  }
173 }
174 
176 {
177  connect(app, &Application::postForked, this, &AuthenticationPrivate::_q_postFork);
178  return true;
179 }
180 
181 AuthenticationRealm *AuthenticationPrivate::realm(const QString &realmName) const
182 {
183  return realms.value(realmName.isNull() ? defaultRealm : realmName);
184 }
185 
186 AuthenticationUser AuthenticationPrivate::restoreUser(Context *c, const QVariant &frozenUser, const QString &realmName)
187 {
188  AuthenticationUser ret;
189  if (!auth) {
190  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
191  return ret;
192  }
193 
194  AuthenticationRealm *realmPtr = auth->d_ptr->realm(realmName);
195  if (!realmPtr) {
196  realmPtr = AuthenticationPrivate::findRealmForPersistedUser(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(Context *c, const QMap<QString, AuthenticationRealm *> &realms, const QStringList &realmsOrder)
211 {
212  const QVariant realmVariant = Session::value(c, AUTHENTICATION_USER_REALM);
213  if (!realmVariant.isNull()) {
214  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  AuthenticationRealm *realm = realms.value(realmName);
222  if (realm && !realm->userIsRestorable(c).isNull()) {
223  return realm;
224  }
225  }
226  }
227 
228  return nullptr;
229 }
230 
231 void AuthenticationPrivate::setAuthenticated(Context *c, const AuthenticationUser &user, const QString &realmName, AuthenticationRealm *realm)
232 {
233  AuthenticationPrivate::setUser(c, user);
234 
235  if (!realm) {
236  qCWarning(C_AUTHENTICATION) << "Called with invalid realm" << realmName;
237  }
238 
239  AuthenticationPrivate::persistUser(c, user, realmName, realm);
240 }
241 
242 void AuthenticationPrivate::setUser(Context *c, const AuthenticationUser &user, const QString &realmName)
243 {
244  if (user.isNull()) {
245  c->setStash(AUTHENTICATION_USER, QVariant());
246  c->setStash(AUTHENTICATION_USER_REALM, QVariant());
247  } else {
248  c->setStash(AUTHENTICATION_USER, QVariant::fromValue(user));
249  c->setStash(AUTHENTICATION_USER_REALM, realmName);
250  }
251 }
252 
253 void AuthenticationPrivate::persistUser(Context *c, const AuthenticationUser &user, const QString &realmName, AuthenticationRealm *realm)
254 {
256  if (Session::isValid(c)) {
257  Session::setValue(c, AUTHENTICATION_USER_REALM, realmName);
258  }
259 
260  if (realm) {
261  realm->persistUser(c, user);
262  }
263  }
264 }
265 
266 void AuthenticationPrivate::_q_postFork(Application *app)
267 {
268  auth = app->plugin<Authentication *>();
269 }
270 
272 {
273 
274 }
275 
276 Cutelyst::AuthenticationCredential::~AuthenticationCredential()
277 {
278 
279 }
280 
281 #include "moc_authentication.cpp"
QMap< QString, QString > ParamsMultiMap
AuthenticationUser restoreUser(Context *c, const QVariant &frozenUser)
Retrieves the user from the store.
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Tries to find the user with authinfo returning a non null AuthenticationUser on success.
AuthenticationUser persistUser(Context *c, const AuthenticationUser &user)
Stores the user on the session.
bool isNull() const
Returns true if the object is null.
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:207
static void logout(Context *c)
T plugin()
Returns the registered plugin that casts to the template type T.
Definition: application.h:115
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:50
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))
QString name() const
Definition: component.cpp:39
static bool isValid(Context *c)
Definition: session.cpp:247
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition: session.cpp:176
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
virtual bool setup(Application *app) override
static bool userExists(Context *c)
void postForked(Application *app)
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:161
The Cutelyst Application.
Definition: application.h:55
static AuthenticationUser user(Context *c)
void stash(const QVariantHash &unite)
Definition: context.h:515
void removePersistedUser(Context *c)
Removes the user from the session.