Cutelyst  1.11.0
credentialhttp.cpp
1 /*
2  * Copyright (C) 2013-2017 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")
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->isAuthTypeDigest()) {
132  ret = d->authenticateDigest(c, realm, authinfo);
133  if (!ret.isNull()) {
134  return ret;
135  }
136  }
137 
138  if (d->isAuthTypeBasic()) {
139  ret = d->authenticateBasic(c, realm, authinfo);
140  if (!ret.isNull()) {
141  return ret;
142  }
143  }
144 
145  ret = d->authenticationFailed(c, realm, authinfo);
146  return ret;
147 }
148 
149 bool CredentialHttpPrivate::checkPassword(const AuthenticationUser &user, const ParamsMultiMap &authinfo)
150 {
151  QString password = authinfo.value(passwordField);
152  const QString storedPassword = user.value(passwordField).toString();
153 
154  if (passwordType == CredentialHttp::None) {
155  qCCritical(C_CREDENTIALHTTP) << "CredentialPassword is set to ignore password check";
156  return true;
157  } else if (passwordType == CredentialHttp::Clear) {
158  return storedPassword == password;
159  } else if (passwordType == CredentialHttp::Hashed) {
160  if (!passwordPreSalt.isEmpty()) {
161  password.prepend(password);
162  }
163 
164  if (!passwordPostSalt.isEmpty()) {
165  password.append(password);
166  }
167 
168  return CredentialPassword::validatePassword(password.toUtf8(), storedPassword.toUtf8());
169  } else if (passwordType == CredentialHttp::SelfCheck) {
170  return user.checkPassword(password);
171  }
172 
173  return false;
174 }
175 
176 AuthenticationUser CredentialHttpPrivate::authenticateDigest(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo)
177 {
178  qCDebug(C_CREDENTIALHTTP) << "Checking http digest authentication.";
179 
180  return AuthenticationUser();
181 }
182 
183 AuthenticationUser CredentialHttpPrivate::authenticateBasic(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo)
184 {
185  Q_UNUSED(authinfo)
186  AuthenticationUser user;
187  qCDebug(C_CREDENTIALHTTP) << "Checking http basic authentication.";
188 
189  const QPair<QString, QString> userPass = c->req()->headers().authorizationBasicPair();
190  if (userPass.first.isEmpty()) {
191  return user;
192  }
193 
194  ParamsMultiMap auth;
195  auth.insert(usernameField, userPass.first);
196  AuthenticationUser _user = realm->findUser(c, auth);
197  if (!_user.isNull()) {
198  auth.insert(passwordField, userPass.second);
199  if (checkPassword(_user, auth)) {
200  user = _user;
201  } else {
202  qCDebug(C_CREDENTIALHTTP) << "Password didn't match";
203  }
204  } else {
205  qCDebug(C_CREDENTIALHTTP) << "Unable to locate a user matching user info provided in realm";
206  }
207  return user;
208 }
209 
210 AuthenticationUser CredentialHttpPrivate::authenticationFailed(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo)
211 {
212  Response *res = c->response();
213  res->setStatus(Response::Unauthorized); // 401
214  res->setContentType(QStringLiteral("text/plain; charset=UTF-8"));
215 
216  if (authorizationRequiredMessage.isEmpty()) {
217  res->setBody(QStringLiteral("Authorization required."));
218  } else {
219  res->setBody(authorizationRequiredMessage);
220  }
221 
222  // Create Digest response
223  if (isAuthTypeDigest()) {
224 // _create_digest_auth_response TODO
225  }
226 
227  // Create Basic response
228  if (isAuthTypeBasic()) {
229  createBasicAuthResponse(c);
230  }
231 
232  return AuthenticationUser();
233 }
234 
235 bool CredentialHttpPrivate::isAuthTypeDigest() const
236 {
237  return type == CredentialHttp::Digest || type == CredentialHttp::Any;
238 
239 }
240 
241 bool CredentialHttpPrivate::isAuthTypeBasic() const
242 {
243  return type == CredentialHttp::Basic || type == CredentialHttp::Any;
244 }
245 
246 void CredentialHttpPrivate::createBasicAuthResponse(Context *c)
247 {
248  c->res()->headers().setWwwAuthenticate(joinAuthHeaderParts(QStringLiteral("Basic"),
249  buildAuthHeaderCommon()));
250 }
251 
252 QStringList CredentialHttpPrivate::buildAuthHeaderCommon() const
253 {
254  // TODO
255  // return realm="realmname"
256  // return domain="realmname"
257  return QStringList();
258 }
259 
260 QString CredentialHttpPrivate::joinAuthHeaderParts(const QString &type, const QStringList &parts) const
261 {
262  QString ret = type;
263  if (!parts.isEmpty()) {
264  ret.append(QLatin1Char(' ') + parts.join(QStringLiteral(", ")));
265  }
266  return ret;
267 }
268 
269 #include "moc_credentialhttp.cpp"
QMap< QString, QString > ParamsMultiMap
Response * res() const
Definition: context.cpp:117
QString passwordPostSalt() const
Returns the salt string to be appended to the password.
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 bool checkPassword(const QString &password) const
Verifies the user password.
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Tries to find the user with authinfo returning a non null AuthenticationUser on success.
void setPasswordType(PasswordType type)
Sets the type of password this class will be dealing with.
void setStatus(quint16 status)
Definition: response.cpp:90
The Cutelyst Context.
Definition: context.h:50
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)
void setPasswordPreSalt(const QString &passwordPreSalt)
Sets the salt string to be prepended to the password.
Headers & headers()
Definition: response.cpp:297
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
void setRequireSsl(bool require)
QString passwordPreSalt() const
Returns the salt string to be prepended to the password.
void setPasswordField(const QString &fieldName)
Sets the field to look for when authenticating the user.
Response * response() const
Definition: context.cpp:111
PasswordType passwordType() const
Returns the type of password this class will be dealing with.
void setWwwAuthenticate(const QString &value)
Definition: headers.cpp:287
QString usernameField() const
Returns the field to look for when authenticating the user.
bool isNull() const
Returns true if the object is null.
void setAuthorizationRequiredMessage(const QString &message)
void setBody(QIODevice *body)
Definition: response.cpp:119
QString passwordField() const
Returns the field to look for when authenticating the user.
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
Validates the given password against the correct hash.