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