Cutelyst  3.1.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 
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 
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 auto userPass = c->req()->headers().authorizationBasicObject();
174  if (userPass.user.isEmpty()) {
175  return user;
176  }
177 
178  ParamsMultiMap auth;
179  auth.insert(usernameField, userPass.user);
180  AuthenticationUser _user = realm->findUser(c, auth);
181  if (!_user.isNull()) {
182  auth.insert(passwordField, userPass.password);
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  Q_UNUSED(authinfo);
197  Response *res = c->response();
198  res->setStatus(Response::Unauthorized); // 401
199  res->setContentType(QStringLiteral("text/plain; charset=UTF-8"));
200 
201  if (authorizationRequiredMessage.isEmpty()) {
202  res->setBody(QStringLiteral("Authorization required."));
203  } else {
204  res->setBody(authorizationRequiredMessage);
205  }
206 
207  // Create Basic response
208  if (isAuthTypeBasic()) {
209  createBasicAuthResponse(c, realm);
210  }
211 
212  return AuthenticationUser();
213 }
214 
215 bool CredentialHttpPrivate::isAuthTypeBasic() const
216 {
217  return type == CredentialHttp::Basic || type == CredentialHttp::Any;
218 }
219 
220 void CredentialHttpPrivate::createBasicAuthResponse(Context *c, AuthenticationRealm *realm)
221 {
222  c->res()->headers().setWwwAuthenticate(joinAuthHeaderParts(QStringLiteral("Basic"),
223  buildAuthHeaderCommon(realm)));
224 }
225 
226 QStringList CredentialHttpPrivate::buildAuthHeaderCommon(AuthenticationRealm *realm) const
227 {
228  QStringList ret;
229  // TODO
230  // return realm="realmname"
231  // return domain="realmname"
232  if (!realm->name().isEmpty()) {
233  ret.append(QLatin1String("realm=\"") + realm->name() + QLatin1Char('"'));
234  }
235  return ret;
236 }
237 
238 QString CredentialHttpPrivate::joinAuthHeaderParts(const QString &type, const QStringList &parts) const
239 {
240  QString ret = type;
241  if (!parts.isEmpty()) {
242  ret.append(QLatin1Char(' ') + parts.join(QStringLiteral(", ")));
243  }
244  return ret;
245 }
246 
247 #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:44
The Cutelyst Context.
Definition: context.h:52
Response * res() const
Definition: context.cpp:116
Response * response() const
Definition: context.cpp:110
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:369
void setWwwAuthenticate(const QString &value)
Definition: headers.cpp:339
Headers headers() const
Definition: request.cpp:321
Headers & headers()
void setBody(QIODevice *body)
Definition: response.cpp:114
void setStatus(quint16 status)
Definition: response.cpp:85
void setContentType(const QString &type)
Definition: response.h:218
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
void append(const T &value)
bool isEmpty() const const
const T value(const Key &key, const T &defaultValue) const const
typename QMap< Key, T >::iterator insert(const Key &key, const T &value)
QString & append(QChar ch)
bool isEmpty() const const
QString & prepend(QChar ch)
QByteArray toUtf8() const const
QString join(const QString &separator) const const
QString toString() const const