cutelyst  3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
credentialhttp.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "authenticationrealm.h"
6 #include "credentialhttp_p.h"
7 #include "credentialpassword.h"
8 
9 #include <Cutelyst/Context>
10 #include <Cutelyst/Response>
11 
12 #include <QLoggingCategory>
13 #include <QUrl>
14 
15 using namespace Cutelyst;
16 
17 Q_LOGGING_CATEGORY(C_CREDENTIALHTTP, "cutelyst.plugin.credentialhttp", QtWarningMsg)
18 
20  : AuthenticationCredential(parent)
21  , d_ptr(new CredentialHttpPrivate)
22 {
23 }
24 
25 CredentialHttp::~CredentialHttp()
26 {
27  delete d_ptr;
28 }
29 
30 void CredentialHttp::setType(CredentialHttp::AuthType type)
31 {
32  Q_D(CredentialHttp);
33  d->type = type;
34 }
35 
37 {
38  Q_D(CredentialHttp);
39  d->authorizationRequiredMessage = message;
40 }
41 
43 {
44  Q_D(const CredentialHttp);
45  return d->passwordField;
46 }
47 
49 {
50  Q_D(CredentialHttp);
51  d->passwordField = fieldName;
52 }
53 
54 CredentialHttp::PasswordType CredentialHttp::passwordType() const
55 {
56  Q_D(const CredentialHttp);
57  return d->passwordType;
58 }
59 
60 void CredentialHttp::setPasswordType(CredentialHttp::PasswordType type)
61 {
62  Q_D(CredentialHttp);
63  d->passwordType = type;
64 }
65 
67 {
68  Q_D(const CredentialHttp);
69  return d->passwordPreSalt;
70 }
71 
72 void CredentialHttp::setPasswordPreSalt(const QString &passwordPreSalt)
73 {
74  Q_D(CredentialHttp);
75  d->passwordPreSalt = passwordPreSalt;
76 }
77 
79 {
80  Q_D(const CredentialHttp);
81  return d->passwordPostSalt;
82 }
83 
84 void CredentialHttp::setPasswordPostSalt(const QString &passwordPostSalt)
85 {
86  Q_D(CredentialHttp);
87  d->passwordPostSalt = passwordPostSalt;
88 }
89 
91 {
92  Q_D(const CredentialHttp);
93  return d->usernameField;
94 }
95 
97 {
98  Q_D(CredentialHttp);
99  d->usernameField = fieldName;
100 }
101 
103 {
104  Q_D(CredentialHttp);
105  d->requireSsl = require;
106 }
107 
109  AuthenticationRealm *realm,
110  const ParamsMultiMap &authinfo)
111 {
112  Q_D(CredentialHttp);
113 
114  AuthenticationUser ret;
115  if (d->requireSsl && !c->request()->secure()) {
116  ret = d->authenticationFailed(c, realm, authinfo);
117  return ret;
118  }
119 
120  if (d->isAuthTypeBasic()) {
121  ret = d->authenticateBasic(c, realm, authinfo);
122  if (!ret.isNull()) {
123  return ret;
124  }
125  }
126 
127  ret = d->authenticationFailed(c, realm, authinfo);
128  return ret;
129 }
130 
131 bool CredentialHttpPrivate::checkPassword(const AuthenticationUser &user,
132  const ParamsMultiMap &authinfo)
133 {
134  QString password = authinfo.value(passwordField);
135  const QString storedPassword = user.value(passwordField).toString();
136 
137  if (Q_LIKELY(passwordType == CredentialHttp::Hashed)) {
138  if (!passwordPreSalt.isEmpty()) {
139  password.prepend(password);
140  }
141 
142  if (!passwordPostSalt.isEmpty()) {
143  password.append(password);
144  }
145 
146  return CredentialPassword::validatePassword(password.toUtf8(), storedPassword.toUtf8());
147  } else if (passwordType == CredentialHttp::Clear) {
148  return storedPassword == password;
149  } else if (passwordType == CredentialHttp::None) {
150  qCCritical(C_CREDENTIALHTTP) << "CredentialPassword is set to ignore password check";
151  return true;
152  }
153 
154  return false;
155 }
156 
157 AuthenticationUser CredentialHttpPrivate::authenticateBasic(Context *c,
158  AuthenticationRealm *realm,
159  const ParamsMultiMap &authinfo)
160 {
161  Q_UNUSED(authinfo)
162  AuthenticationUser user;
163  qCDebug(C_CREDENTIALHTTP) << "Checking http basic authentication.";
164 
165  const auto userPass = c->req()->headers().authorizationBasicObject();
166  if (userPass.user.isEmpty()) {
167  return user;
168  }
169 
170  ParamsMultiMap auth;
171  auth.insert(usernameField, userPass.user);
172  AuthenticationUser _user = realm->findUser(c, auth);
173  if (!_user.isNull()) {
174  auth.insert(passwordField, userPass.password);
175  if (checkPassword(_user, auth)) {
176  user = _user;
177  } else {
178  qCDebug(C_CREDENTIALHTTP) << "Password didn't match";
179  }
180  } else {
181  qCDebug(C_CREDENTIALHTTP) << "Unable to locate a user matching user info provided in realm";
182  }
183  return user;
184 }
185 
186 AuthenticationUser CredentialHttpPrivate::authenticationFailed(Context *c,
187  AuthenticationRealm *realm,
188  const ParamsMultiMap &authinfo)
189 {
190  Q_UNUSED(authinfo);
191  Response *res = c->response();
192  res->setStatus(Response::Unauthorized); // 401
193  res->setContentType(QStringLiteral("text/plain; charset=UTF-8"));
194 
195  if (authorizationRequiredMessage.isEmpty()) {
196  res->setBody(QStringLiteral("Authorization required."));
197  } else {
198  res->setBody(authorizationRequiredMessage);
199  }
200 
201  // Create Basic response
202  if (isAuthTypeBasic()) {
203  createBasicAuthResponse(c, realm);
204  }
205 
206  return AuthenticationUser();
207 }
208 
209 bool CredentialHttpPrivate::isAuthTypeBasic() const
210 {
211  return type == CredentialHttp::Basic || type == CredentialHttp::Any;
212 }
213 
214 void CredentialHttpPrivate::createBasicAuthResponse(Context *c, AuthenticationRealm *realm)
215 {
217  joinAuthHeaderParts(QStringLiteral("Basic"), buildAuthHeaderCommon(realm)));
218 }
219 
220 QStringList CredentialHttpPrivate::buildAuthHeaderCommon(AuthenticationRealm *realm) const
221 {
222  QStringList ret;
223  // TODO
224  // return realm="realmname"
225  // return domain="realmname"
226  if (!realm->name().isEmpty()) {
227  ret.append(u"realm=\"" + realm->name() + u'"');
228  }
229  return ret;
230 }
231 
232 QString CredentialHttpPrivate::joinAuthHeaderParts(const QString &type,
233  const QStringList &parts) const
234 {
235  QString ret = type;
236  if (!parts.isEmpty()) {
237  ret.append(u' ' + parts.join(u", "));
238  }
239  return ret;
240 }
241 
242 #include "moc_credentialhttp.cpp"
QString & append(QChar ch)
void setPasswordPostSalt(const QString &passwordPostSalt)
Sets the salt string to be appended to the password.
void setContentType(const QString &type)
Definition: response.h:220
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Tries to find the user with authinfo returning a non null AuthenticationUser on success.
Headers & headers() noexcept
QString & prepend(QChar ch)
bool isNull() const
Returns true if the object is null.
Response * res() const noexcept
Definition: context.cpp:102
QString passwordPreSalt() const
Returns the salt string to be prepended to the password.
void setPasswordType(PasswordType type)
Sets the type of password this class will be dealing with.
QString join(QChar separator) const const
QString passwordPostSalt() const
Returns the salt string to be appended to the password.
The Cutelyst Context.
Definition: context.h:38
QString usernameField() const
Returns the field to look for when authenticating the user.
void setUsernameField(const QString &fieldName)
Sets the field to look for when authenticating the user.
AuthenticationUser authenticate(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo) final
Tries to authenticate the authinfo using the give realm.
void setType(CredentialHttp::AuthType type)
Headers headers() const noexcept
Definition: request.cpp:310
QString name() const
Definition: component.cpp:33
QString passwordField() const
Returns the field to look for when authenticating the user.
bool isEmpty() const const
bool isEmpty() const const
void setPasswordPreSalt(const QString &passwordPreSalt)
Sets the salt string to be prepended to the password.
iterator insert(const Key &key, const T &value)
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
void setRequireSsl(bool require)
void setPasswordField(const QString &fieldName)
Sets the field to look for when authenticating the user.
void setWwwAuthenticate(const QString &value)
Definition: headers.cpp:322
void append(QList< T > &&value)
PasswordType passwordType() const
Returns the type of password this class will be dealing with.
void setAuthorizationRequiredMessage(const QString &message)
void setBody(QIODevice *body)
Definition: response.cpp:100
Response * response() const noexcept
Definition: context.cpp:96
QString toString() const const
void setStatus(quint16 status) noexcept
Definition: response.cpp:70
Authorization authorizationBasicObject() const
Definition: headers.cpp:352
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
Validates the given password against the correct hash.
T value(const Key &key, const T &defaultValue) const const
QByteArray toUtf8() const const