Cutelyst  2.5.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 
86 {
87  Q_D(CredentialHttp);
88  d->passwordPreSalt = passwordPreSalt;
89 }
90 
92 {
93  Q_D(const CredentialHttp);
94  return d->passwordPostSalt;
95 }
96 
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"
QMap< QString, QString > ParamsMultiMap
void setPasswordPostSalt(const QString &passwordPostSalt)
Sets the salt string to be appended to the password.
void setContentType(const QString &type)
Definition: response.h:206
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.
Response * res() const
Definition: context.cpp:116
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.
void setStatus(quint16 status)
Definition: response.cpp:85
QString passwordPostSalt() const
Returns the salt string to be appended to the password.
The Cutelyst Context.
Definition: context.h:50
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)
QString name() const
Definition: component.cpp:39
QString passwordField() const
Returns the field to look for when authenticating the user.
void setPasswordPreSalt(const QString &passwordPreSalt)
Sets the salt string to be prepended to the password.
Headers & headers()
Definition: response.cpp:290
Response * response() const
Definition: context.cpp:110
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:289
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:114
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
Validates the given password against the correct hash.