Cutelyst  1.11.0
authentication.cpp
1 /*
2  * Copyright (C) 2013-2017 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 
37 static thread_local Authentication *auth = nullptr;
38 
39 #define AUTHENTICATION_USER "__authentication_user"
40 #define AUTHENTICATION_USER_REALM "__authentication_user_realm"
41 
42 Authentication::Authentication(Application *parent) : Plugin(parent)
43  , d_ptr(new AuthenticationPrivate)
44 {
45  qRegisterMetaType<AuthenticationUser>();
46  qRegisterMetaTypeStreamOperators<AuthenticationUser>();
47 }
48 
49 Authentication::~Authentication()
50 {
51  delete d_ptr;
52 }
53 
55 {
56  Q_D(Authentication);
57  realm->setObjectName(name);
58  realm->setName(name);
59  realm->setParent(this);
60  d->realms.insert(name, realm);
61  d->realmsOrder.append(name);
62 }
63 
65 {
66  addRealm(new AuthenticationRealm(store, credential, this), name);
67 }
68 
69 AuthenticationRealm *Authentication::realm(const QString &name) const
70 {
71  Q_D(const Authentication);
72  return d->realms.value(name);
73 }
74 
75 bool Authentication::authenticate(Cutelyst::Context *c, const ParamsMultiMap &userinfo, const QString &realm)
76 {
77  if (!auth) {
78  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
79  return false;
80  }
81 
82  AuthenticationRealm *realmPtr = auth->d_ptr->realm(realm);
83  if (realmPtr) {
84  const AuthenticationUser user = realmPtr->authenticate(c, userinfo);
85  if (!user.isNull()) {
86  AuthenticationPrivate::setAuthenticated(c, user, realm, auth->d_ptr->realm(realm));
87  }
88 
89  return !user.isNull();
90  }
91 
92  qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
93  return false;
94 }
95 
97 {
99  if (!auth) {
100  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
101  return ret;
102  }
103 
104  AuthenticationRealm *realmPtr = auth->d_ptr->realm(realm);
105  if (!realmPtr) {
106  qCWarning(C_AUTHENTICATION) << "Could not find realm" << realm;
107  return ret;
108  }
109 
110  ret = realmPtr->findUser(c, userinfo);
111  return ret;
112 }
113 
115 {
116  AuthenticationUser ret;
117  QVariant user = c->property(AUTHENTICATION_USER);
118  if (user.isNull()) {
119  ret = AuthenticationPrivate::restoreUser(c, QVariant(), QString());
120  } else {
121  ret = user.value<AuthenticationUser>();
122  }
123  return ret;
124 }
125 
127 {
128  if (!c->property(AUTHENTICATION_USER).isNull()) {
129  return true;
130  } else {
131  if (auth) {
132  if (AuthenticationPrivate::findRealmForPersistedUser(c, auth->d_ptr->realms, auth->d_ptr->realmsOrder)) {
133  return true;
134  }
135  } else {
136  qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
137  }
138  return false;
139  }
140 }
141 
142 bool Authentication::userInRealm(Cutelyst::Context *c, const QString &realmName)
143 {
144  QVariant user = c->property(AUTHENTICATION_USER);
145  if (!user.isNull()) {
146  return user.value<AuthenticationUser>().authRealm()->name() == realmName;
147  } else {
148  if (!auth) {
149  qCCritical(C_AUTHENTICATION, "Authentication plugin not registered!");
150  return false;
151  }
152 
153  AuthenticationRealm *realm = AuthenticationPrivate::findRealmForPersistedUser(c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
154  if (realm) {
155  return realm->name() == realmName;
156  } else {
157  return false;
158  }
159  }
160 }
161 
163 {
164  AuthenticationPrivate::setUser(c, AuthenticationUser());
165 
166  if (auth) {
167  AuthenticationRealm *realm = AuthenticationPrivate::findRealmForPersistedUser(c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
168  if (realm) {
169  realm->removePersistedUser(c);
170  }
171  } else {
172  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
173  }
174 }
175 
177 {
178  connect(app, &Application::postForked, this, &AuthenticationPrivate::_q_postFork);
179  return true;
180 }
181 
182 AuthenticationRealm *AuthenticationPrivate::realm(const QString &realmName) const
183 {
184  return realms.value(realmName.isNull() ? defaultRealm : realmName);
185 }
186 
187 AuthenticationUser AuthenticationPrivate::restoreUser(Context *c, const QVariant &frozenUser, const QString &realmName)
188 {
189  AuthenticationUser ret;
190  if (!auth) {
191  qCCritical(C_AUTHENTICATION) << "Authentication plugin not registered";
192  return ret;
193  }
194 
195  AuthenticationRealm *realmPtr = auth->d_ptr->realm(realmName);
196  if (!realmPtr) {
197  realmPtr = AuthenticationPrivate::findRealmForPersistedUser(c, auth->d_ptr->realms, auth->d_ptr->realmsOrder);
198  }
199 
200  if (!realmPtr) {
201  return ret;
202  }
203 
204  ret = realmPtr->restoreUser(c, frozenUser);
205 
206  AuthenticationPrivate::setUser(c, ret);
207  // TODO
208  // $user->auth_realm($realm->name) if $user;
209 
210  return ret;
211 }
212 
213 AuthenticationRealm *AuthenticationPrivate::findRealmForPersistedUser(Context *c, const QMap<QString, AuthenticationRealm *> &realms, const QStringList &realmsOrder)
214 {
216 
217  const QVariant realmVariant = Session::value(c, QStringLiteral(AUTHENTICATION_USER_REALM));
218  if (!realmVariant.isNull()) {
219  realm = realms.value(realmVariant.toString());
220  if (realm && !realm->userIsRestorable(c).isNull()) {
221  return realm;
222  }
223  } else {
224  // we have no choice but to ask each realm whether it has a persisted user.
225  for (const QString &realmName : realmsOrder) {
226  AuthenticationRealm *realm = realms.value(realmName);
227  if (realm && !realm->userIsRestorable(c).isNull()) {
228  return realm;
229  }
230  }
231  }
232 
233  return nullptr;
234 }
235 
236 void AuthenticationPrivate::setAuthenticated(Context *c, const AuthenticationUser &user, const QString &realmName, AuthenticationRealm *realm)
237 {
238  AuthenticationPrivate::setUser(c, user);
239 
240  if (!realm) {
241  qCWarning(C_AUTHENTICATION) << "Called with invalid realm" << realmName;
242  }
243  // TODO implement a user class
244 // $user->auth_realm($realm->name);
245 
246  AuthenticationPrivate::persistUser(c, user, realmName, realm);
247 }
248 
249 void AuthenticationPrivate::setUser(Context *c, const AuthenticationUser &user, const QString &realmName)
250 {
251  if (user.isNull()) {
252  c->setProperty(AUTHENTICATION_USER, QVariant());
253  c->setProperty(AUTHENTICATION_USER_REALM, QVariant());
254  } else {
255  c->setProperty(AUTHENTICATION_USER, QVariant::fromValue(user));
256  c->setProperty(AUTHENTICATION_USER_REALM, realmName);
257  }
258 }
259 
260 void AuthenticationPrivate::persistUser(Context *c, const AuthenticationUser &user, const QString &realmName, AuthenticationRealm *realm)
261 {
263  if (Session::isValid(c)) {
264  Session::setValue(c, QStringLiteral(AUTHENTICATION_USER_REALM), realmName);
265  }
266 
267  if (realm) {
268  realm->persistUser(c, user);
269  }
270  }
271 }
272 
273 void AuthenticationPrivate::_q_postFork(Application *app)
274 {
275  auth = app->plugin<Authentication *>();
276 }
277 
279 {
280 
281 }
282 
283 Cutelyst::AuthenticationCredential::~AuthenticationCredential()
284 {
285 
286 }
287 
288 #include "moc_authentication.cpp"
QMap< QString, QString > ParamsMultiMap
void setName(const QString &name)
Definition: component.cpp:45
AuthenticationUser restoreUser(Context *c, const QVariant &frozenUser)
Retrieves the user from the store TODO move out on Cutelyst2.
QString name() const
Definition: component.cpp:39
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 TODO move out on Cutelyst2.
virtual AuthenticationUser authenticate(Context *c, const ParamsMultiMap &authinfo)
Tries to authenticate the user with authinfo returning a non null AuthenticationUser on success...
static void logout(Context *c)
T plugin()
Returns the registered plugin that casts to the template type T.
Definition: application.h:114
AuthenticationRealm * realm(const QString &name=QLatin1String(defaultRealm)) const
Returns an AuthenticationRealm object that was registered with name.
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 TODO move out on Cutelyst2.
The Cutelyst Context.
Definition: context.h:50
static bool authenticate(Context *c, const ParamsMultiMap &userinfo, const QString &realm=QLatin1String(defaultRealm))
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
void addRealm(AuthenticationRealm *realm, const QString &name=QLatin1String(defaultRealm))
Adds the realm with name.
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
bool isNull() const
Returns true if the object is null.
The Cutelyst Application.
Definition: application.h:54
static AuthenticationUser user(Context *c)
void removePersistedUser(Context *c)
Removes the user from the session TODO move out on Cutelyst2.