cutelyst 4.0.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
credentialhttp.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "authenticationrealm.h"
6#include "credentialhttp_p.h"
7#include "credentialpassword.h"
8
9#include <Cutelyst/Context>
10#include <Cutelyst/Response>
11
12#include <QLoggingCategory>
13#include <QUrl>
14
15using namespace Cutelyst;
16
17Q_LOGGING_CATEGORY(C_CREDENTIALHTTP, "cutelyst.plugin.credentialhttp", QtWarningMsg)
18
21 , d_ptr(new CredentialHttpPrivate)
22{
23}
24
25CredentialHttp::~CredentialHttp()
26{
27 delete d_ptr;
28}
29
30void 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
48void CredentialHttp::setPasswordField(const QString &fieldName)
49{
50 Q_D(CredentialHttp);
51 d->passwordField = fieldName;
52}
53
54CredentialHttp::PasswordType CredentialHttp::passwordType() const
55{
56 Q_D(const CredentialHttp);
57 return d->passwordType;
58}
59
60void 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
72void 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
84void 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
96void CredentialHttp::setUsernameField(const QString &fieldName)
97{
98 Q_D(CredentialHttp);
99 d->usernameField = fieldName;
100}
101
103{
104 Q_D(CredentialHttp);
105 d->requireSsl = require;
106}
107
109 AuthenticationRealm *realm,
110 const ParamsMultiMap &authinfo)
111{
112 Q_D(CredentialHttp);
113
115 if (d->requireSsl && !c->request()->secure()) {
116 ret = d->authenticationFailed(c, realm, authinfo);
117 return ret;
118 }
119
120 if (d->isAuthTypeBasic()) {
121 ret = d->authenticateBasic(c, realm, authinfo);
122 if (!ret.isNull()) {
123 return ret;
124 }
125 }
126
127 ret = d->authenticationFailed(c, realm, authinfo);
128 return ret;
129}
130
131bool CredentialHttpPrivate::checkPassword(const AuthenticationUser &user,
132 const ParamsMultiMap &authinfo)
133{
134 const QString password = passwordPreSalt + authinfo.value(passwordField) + passwordPostSalt;
135 const QString storedPassword = user.value(passwordField).toString();
136
137 if (Q_LIKELY(passwordType == CredentialHttp::Hashed)) {
138 return CredentialPassword::validatePassword(password.toUtf8(), storedPassword.toUtf8());
139 } else if (passwordType == CredentialHttp::Clear) {
140 return storedPassword == password;
141 } else if (passwordType == CredentialHttp::None) {
142 qCCritical(C_CREDENTIALHTTP) << "CredentialPassword is set to ignore password check";
143 return true;
144 }
145
146 return false;
147}
148
149AuthenticationUser CredentialHttpPrivate::authenticateBasic(Context *c,
150 AuthenticationRealm *realm,
151 const ParamsMultiMap &authinfo)
152{
153 Q_UNUSED(authinfo)
155 qCDebug(C_CREDENTIALHTTP) << "Checking http basic authentication.";
156
157 const auto userPass = c->req()->headers().authorizationBasicObject();
158 if (userPass.user.isEmpty()) {
159 return user;
160 }
161
162 ParamsMultiMap auth;
163 auth.insert(usernameField, userPass.user);
164 AuthenticationUser _user = realm->findUser(c, auth);
165 if (!_user.isNull()) {
166 auth.insert(passwordField, userPass.password);
167 if (checkPassword(_user, auth)) {
168 user = _user;
169 } else {
170 qCDebug(C_CREDENTIALHTTP) << "Password didn't match";
171 }
172 } else {
173 qCDebug(C_CREDENTIALHTTP) << "Unable to locate a user matching user info provided in realm";
174 }
175 return user;
176}
177
178AuthenticationUser CredentialHttpPrivate::authenticationFailed(Context *c,
179 AuthenticationRealm *realm,
180 const ParamsMultiMap &authinfo)
181{
182 Q_UNUSED(authinfo);
183 Response *res = c->response();
184 res->setStatus(Response::Unauthorized); // 401
185 res->setContentType("text/plain; charset=UTF-8"_qba);
186
187 if (authorizationRequiredMessage.isEmpty()) {
188 res->setBody("Authorization required."_qba);
189 } else {
190 res->setBody(authorizationRequiredMessage);
191 }
192
193 // Create Basic response
194 if (isAuthTypeBasic()) {
195 createBasicAuthResponse(c, realm);
196 }
197
198 return AuthenticationUser();
199}
200
201bool CredentialHttpPrivate::isAuthTypeBasic() const
202{
203 return type == CredentialHttp::Basic || type == CredentialHttp::Any;
204}
205
206void CredentialHttpPrivate::createBasicAuthResponse(Context *c, AuthenticationRealm *realm)
207{
209 joinAuthHeaderParts("Basic"_qba, buildAuthHeaderCommon(realm)));
210}
211
212QByteArrayList CredentialHttpPrivate::buildAuthHeaderCommon(AuthenticationRealm *realm) const
213{
214 QByteArrayList ret;
215 // TODO
216 // return realm="realmname"
217 // return domain="realmname"
218 if (!realm->name().isEmpty()) {
219 ret.append("realm=\"" + realm->name().toLatin1() + '"');
220 }
221 return ret;
222}
223
224QByteArray CredentialHttpPrivate::joinAuthHeaderParts(const QByteArray &type,
225 const QByteArrayList &parts) const
226{
227 QByteArray ret = type;
228 if (!parts.isEmpty()) {
229 ret.append(' ' + parts.join(", "));
230 }
231 return ret;
232}
233
234#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 noexcept
Definition: component.cpp:33
The Cutelyst Context.
Definition: context.h:38
Response * res() const noexcept
Definition: context.cpp:102
Response * response() const noexcept
Definition: context.cpp:96
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.
void setWwwAuthenticate(const QByteArray &value)
Definition: headers.cpp:326
Authorization authorizationBasicObject() const
Definition: headers.cpp:358
Headers headers() const noexcept
Definition: request.cpp:307
void setContentType(const QByteArray &type)
Definition: response.h:203
void setStatus(quint16 status) noexcept
Definition: response.cpp:72
void setBody(QIODevice *body)
Definition: response.cpp:102
Headers & headers() noexcept
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap