cutelyst  3.7.0
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 "credentialhttp_p.h"
6 #include "credentialpassword.h"
7 
8 #include "authenticationrealm.h"
9 
10 #include <Cutelyst/Context>
11 #include <Cutelyst/Response>
12 
13 #include <QUrl>
14 #include <QLoggingCategory>
15 
16 using namespace Cutelyst;
17 
18 Q_LOGGING_CATEGORY(C_CREDENTIALHTTP, "cutelyst.plugin.credentialhttp", QtWarningMsg)
19 
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 
48 void CredentialHttp::setPasswordField(const QString &fieldName)
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 
96 void CredentialHttp::setUsernameField(const QString &fieldName)
97 {
98  Q_D(CredentialHttp);
99  d->usernameField = fieldName;
100 }
101 
103 {
104  Q_D(CredentialHttp);
105  d->requireSsl = require;
106 }
107 
109 {
110  Q_D(CredentialHttp);
111 
112  AuthenticationUser ret;
113  if (d->requireSsl && !c->request()->secure()) {
114  ret = d->authenticationFailed(c, realm, authinfo);
115  return ret;
116  }
117 
118  if (d->isAuthTypeBasic()) {
119  ret = d->authenticateBasic(c, realm, authinfo);
120  if (!ret.isNull()) {
121  return ret;
122  }
123  }
124 
125  ret = d->authenticationFailed(c, realm, authinfo);
126  return ret;
127 }
128 
129 bool CredentialHttpPrivate::checkPassword(const AuthenticationUser &user, const ParamsMultiMap &authinfo)
130 {
131  QString password = authinfo.value(passwordField);
132  const QString storedPassword = user.value(passwordField).toString();
133 
134  if (Q_LIKELY(passwordType == CredentialHttp::Hashed)) {
135  if (!passwordPreSalt.isEmpty()) {
136  password.prepend(password);
137  }
138 
139  if (!passwordPostSalt.isEmpty()) {
140  password.append(password);
141  }
142 
143  return CredentialPassword::validatePassword(password.toUtf8(), storedPassword.toUtf8());
144  } else if (passwordType == CredentialHttp::Clear) {
145  return storedPassword == password;
146  } else if (passwordType == CredentialHttp::None) {
147  qCCritical(C_CREDENTIALHTTP) << "CredentialPassword is set to ignore password check";
148  return true;
149  }
150 
151  return false;
152 }
153 
154 AuthenticationUser CredentialHttpPrivate::authenticateBasic(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo)
155 {
156  Q_UNUSED(authinfo)
157  AuthenticationUser user;
158  qCDebug(C_CREDENTIALHTTP) << "Checking http basic authentication.";
159 
160  const auto userPass = c->req()->headers().authorizationBasicObject();
161  if (userPass.user.isEmpty()) {
162  return user;
163  }
164 
165  ParamsMultiMap auth;
166  auth.insert(usernameField, userPass.user);
167  AuthenticationUser _user = realm->findUser(c, auth);
168  if (!_user.isNull()) {
169  auth.insert(passwordField, userPass.password);
170  if (checkPassword(_user, auth)) {
171  user = _user;
172  } else {
173  qCDebug(C_CREDENTIALHTTP) << "Password didn't match";
174  }
175  } else {
176  qCDebug(C_CREDENTIALHTTP) << "Unable to locate a user matching user info provided in realm";
177  }
178  return user;
179 }
180 
181 AuthenticationUser CredentialHttpPrivate::authenticationFailed(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo)
182 {
183  Q_UNUSED(authinfo);
184  Response *res = c->response();
185  res->setStatus(Response::Unauthorized); // 401
186  res->setContentType(QStringLiteral("text/plain; charset=UTF-8"));
187 
188  if (authorizationRequiredMessage.isEmpty()) {
189  res->setBody(QStringLiteral("Authorization required."));
190  } else {
191  res->setBody(authorizationRequiredMessage);
192  }
193 
194  // Create Basic response
195  if (isAuthTypeBasic()) {
196  createBasicAuthResponse(c, realm);
197  }
198 
199  return AuthenticationUser();
200 }
201 
202 bool CredentialHttpPrivate::isAuthTypeBasic() const
203 {
204  return type == CredentialHttp::Basic || type == CredentialHttp::Any;
205 }
206 
207 void CredentialHttpPrivate::createBasicAuthResponse(Context *c, AuthenticationRealm *realm)
208 {
209  c->res()->headers().setWwwAuthenticate(joinAuthHeaderParts(QStringLiteral("Basic"),
210  buildAuthHeaderCommon(realm)));
211 }
212 
213 QStringList CredentialHttpPrivate::buildAuthHeaderCommon(AuthenticationRealm *realm) const
214 {
215  QStringList ret;
216  // TODO
217  // return realm="realmname"
218  // return domain="realmname"
219  if (!realm->name().isEmpty()) {
220  ret.append(u"realm=\"" + realm->name() + u'"');
221  }
222  return ret;
223 }
224 
225 QString CredentialHttpPrivate::joinAuthHeaderParts(const QString &type, const QStringList &parts) const
226 {
227  QString ret = type;
228  if (!parts.isEmpty()) {
229  ret.append(u' ' + parts.join(u", "));
230  }
231  return ret;
232 }
233 
234 #include "moc_credentialhttp.cpp"
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Tries to find the user with authinfo returning a non null AuthenticationUser on success.
bool isNull() const
Returns true if the object is null.
QString name() const
Definition: component.cpp:31
The Cutelyst Context.
Definition: context.h:39
Response * res() const noexcept
Definition: context.cpp:103
Response * response() const noexcept
Definition: context.cpp:97
QString usernameField() const
Returns the field to look for when authenticating the user.
void setPasswordType(PasswordType type)
Sets the type of password this class will be dealing with.
void setUsernameField(const QString &fieldName)
Sets the field to look for when authenticating the user.
QString passwordPreSalt() const
Returns the salt string to be prepended to the password.
QString passwordPostSalt() const
Returns the salt string to be appended to the password.
void setPasswordPreSalt(const QString &passwordPreSalt)
Sets the salt string to be prepended to the password.
QString passwordField() const
Returns the field to look for when authenticating the user.
PasswordType passwordType() const
Returns the type of password this class will be dealing with.
void setType(CredentialHttp::AuthType type)
AuthenticationUser authenticate(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo) final
Tries to authenticate the authinfo using the give realm.
void setPasswordField(const QString &fieldName)
Sets the field to look for when authenticating the user.
void setPasswordPostSalt(const QString &passwordPostSalt)
Sets the salt string to be appended to the password.
void setRequireSsl(bool require)
void setAuthorizationRequiredMessage(const QString &message)
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
Validates the given password against the correct hash.
Authorization authorizationBasicObject() const
Definition: headers.cpp:356
void setWwwAuthenticate(const QString &value)
Definition: headers.cpp:326
Headers headers() const noexcept
Definition: request.cpp:308
void setStatus(quint16 status) noexcept
Definition: response.cpp:72
Headers & headers() noexcept
void setBody(QIODevice *body)
Definition: response.cpp:101
void setContentType(const QString &type)
Definition: response.h:217
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap