cutelyst  4.9.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 "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 using namespace Qt::Literals::StringLiterals;
17 
18 Q_LOGGING_CATEGORY(C_CREDENTIALHTTP, "cutelyst.plugin.credentialhttp", QtWarningMsg)
19 
21  : AuthenticationCredential(parent)
22  , d_ptr(new CredentialHttpPrivate)
23 {
24 }
25 
27 {
28  delete d_ptr;
29 }
30 
32 {
33  Q_D(CredentialHttp);
34  d->type = type;
35 }
36 
38 {
39  Q_D(CredentialHttp);
40  d->authorizationRequiredMessage = message;
41 }
42 
44 {
45  Q_D(const CredentialHttp);
46  return d->passwordField;
47 }
48 
50 {
51  Q_D(CredentialHttp);
52  d->passwordField = fieldName;
53 }
54 
56 {
57  Q_D(const CredentialHttp);
58  return d->passwordType;
59 }
60 
62 {
63  Q_D(CredentialHttp);
64  d->passwordType = type;
65 }
66 
68 {
69  Q_D(const CredentialHttp);
70  return d->passwordPreSalt;
71 }
72 
73 void CredentialHttp::setPasswordPreSalt(const QString &passwordPreSalt)
74 {
75  Q_D(CredentialHttp);
76  d->passwordPreSalt = passwordPreSalt;
77 }
78 
80 {
81  Q_D(const CredentialHttp);
82  return d->passwordPostSalt;
83 }
84 
85 void CredentialHttp::setPasswordPostSalt(const QString &passwordPostSalt)
86 {
87  Q_D(CredentialHttp);
88  d->passwordPostSalt = passwordPostSalt;
89 }
90 
92 {
93  Q_D(const CredentialHttp);
94  return d->usernameField;
95 }
96 
98 {
99  Q_D(CredentialHttp);
100  d->usernameField = fieldName;
101 }
102 
104 {
105  Q_D(CredentialHttp);
106  d->requireSsl = require;
107 }
108 
110  AuthenticationRealm *realm,
111  const ParamsMultiMap &authinfo)
112 {
113  Q_D(CredentialHttp);
114 
115  AuthenticationUser ret;
116  if (d->requireSsl && !c->request()->secure()) {
117  ret = d->authenticationFailed(c, realm, authinfo);
118  return ret;
119  }
120 
121  if (d->isAuthTypeBasic()) {
122  ret = d->authenticateBasic(c, realm, authinfo);
123  if (!ret.isNull()) {
124  return ret;
125  }
126  }
127 
128  ret = d->authenticationFailed(c, realm, authinfo);
129  return ret;
130 }
131 
132 bool CredentialHttpPrivate::checkPassword(const AuthenticationUser &user,
133  const ParamsMultiMap &authinfo)
134 {
135  const QString password = passwordPreSalt + authinfo.value(passwordField) + passwordPostSalt;
136  const QString storedPassword = user.value(passwordField).toString();
137 
138  if (Q_LIKELY(passwordType == CredentialHttp::Hashed)) {
139  return CredentialPassword::validatePassword(password.toUtf8(), storedPassword.toUtf8());
140  } else if (passwordType == CredentialHttp::Clear) {
141  return storedPassword == password;
142  } else if (passwordType == CredentialHttp::None) {
143  qCCritical(C_CREDENTIALHTTP) << "CredentialPassword is set to ignore password check";
144  return true;
145  }
146 
147  return false;
148 }
149 
150 AuthenticationUser CredentialHttpPrivate::authenticateBasic(Context *c,
151  AuthenticationRealm *realm,
152  const ParamsMultiMap &authinfo)
153 {
154  Q_UNUSED(authinfo)
155  AuthenticationUser user;
156  qCDebug(C_CREDENTIALHTTP) << "Checking http basic authentication.";
157 
158  const auto userPass = c->req()->headers().authorizationBasicObject();
159  if (userPass.user.isEmpty()) {
160  return user;
161  }
162 
163  ParamsMultiMap auth;
164  auth.insert(usernameField, userPass.user);
165  AuthenticationUser _user = realm->findUser(c, auth);
166  if (!_user.isNull()) {
167  auth.insert(passwordField, userPass.password);
168  if (checkPassword(_user, auth)) {
169  user = _user;
170  } else {
171  qCDebug(C_CREDENTIALHTTP) << "Password didn't match";
172  }
173  } else {
174  qCDebug(C_CREDENTIALHTTP) << "Unable to locate a user matching user info provided in realm";
175  }
176  return user;
177 }
178 
179 AuthenticationUser CredentialHttpPrivate::authenticationFailed(Context *c,
180  AuthenticationRealm *realm,
181  const ParamsMultiMap &authinfo)
182 {
183  Q_UNUSED(authinfo);
184  Response *res = c->response();
185  res->setStatus(Response::Unauthorized); // 401
186  res->setContentType("text/plain; charset=UTF-8"_ba);
187 
188  if (authorizationRequiredMessage.isEmpty()) {
189  res->setBody("Authorization required."_ba);
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 {
210  joinAuthHeaderParts("Basic"_ba, buildAuthHeaderCommon(realm)));
211 }
212 
213 QByteArrayList CredentialHttpPrivate::buildAuthHeaderCommon(AuthenticationRealm *realm) const
214 {
215  QByteArrayList ret;
216  // TODO
217  // return realm="realmname"
218  // return domain="realmname"
219  if (!realm->name().isEmpty()) {
220  ret.append("realm=\"" + realm->name().toLatin1() + '"');
221  }
222  return ret;
223 }
224 
225 QByteArray CredentialHttpPrivate::joinAuthHeaderParts(const QByteArray &type,
226  const QByteArrayList &parts) const
227 {
228  QByteArray ret = type;
229  if (!parts.isEmpty()) {
230  ret.append(' ' + parts.join(", "));
231  }
232  return ret;
233 }
234 
235 #include "moc_credentialhttp.cpp"
Request request
Definition: context.h:72
void setPasswordPostSalt(const QString &passwordPostSalt)
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Headers & headers() noexcept
Response * res() const noexcept
Definition: context.cpp:104
QString passwordPreSalt() const
void setPasswordType(PasswordType type)
Request req
Definition: context.h:67
QString passwordPostSalt() const
Abstract class to validate authentication credentials like user name and password.
void setWwwAuthenticate(const QByteArray &value)
Definition: headers.cpp:327
void setContentType(const QByteArray &type)
Definition: response.h:238
Combines user store and credential validation into a named realm.
A Cutelyst response.
Definition: response.h:28
The Cutelyst Context.
Definition: context.h:42
QString usernameField() const
QByteArray join(QByteArrayView separator) const const
void setUsernameField(const QString &fieldName)
AuthenticationUser authenticate(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo) final
void setType(CredentialHttp::AuthType type)
Headers headers() const noexcept
Definition: request.cpp:313
QString passwordField() const
bool isEmpty() const const
bool isEmpty() const const
void setPasswordPreSalt(const QString &passwordPreSalt)
QString name() const noexcept
Definition: component.cpp:33
iterator insert(const Key &key, const T &value)
The Cutelyst namespace holds all public Cutelyst API.
Use HTTP basic authentication to authenticate a user.
void setRequireSsl(bool require)
QByteArray & append(QByteArrayView data)
void setPasswordField(const QString &fieldName)
Container for user data retrieved from an AuthenticationStore.
QByteArray toLatin1() const const
void append(QList< T > &&value)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
PasswordType passwordType() const
void setAuthorizationRequiredMessage(const QString &message)
void setBody(QIODevice *body)
Definition: response.cpp:104
Response * response() const noexcept
Definition: context.cpp:98
QString toString() const const
void setStatus(quint16 status) noexcept
Definition: response.cpp:73
Authorization authorizationBasicObject() const
Definition: headers.cpp:359
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
T value(const Key &key, const T &defaultValue) const const
QByteArray toUtf8() const const