Cutelyst  3.5.0
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 
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 {
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(QLatin1String("realm=\"") + realm->name() + QLatin1Char('"'));
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(QLatin1Char(' ') + parts.join(QStringLiteral(", ")));
230  }
231  return ret;
232 }
233 
234 #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:205
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:103
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(const QString &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.
void append(const T &value)
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:308
QString name() const
Definition: component.cpp:31
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.
typename QMap< Key, T >::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:326
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:101
Response * response() const noexcept
Definition: context.cpp:97
QString toString() const const
void setStatus(quint16 status) noexcept
Definition: response.cpp:72
Authorization authorizationBasicObject() const
Definition: headers.cpp:356
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
Validates the given password against the correct hash.
const T value(const Key &key, const T &defaultValue) const const
QByteArray toUtf8() const const