cutelyst 3.9.1
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
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
73{
74 Q_D(CredentialHttp);
75 d->passwordPreSalt = passwordPreSalt;
76}
77
79{
80 Q_D(const CredentialHttp);
81 return d->passwordPostSalt;
82}
83
85{
86 Q_D(CredentialHttp);
87 d->passwordPostSalt = passwordPostSalt;
88}
89
91{
92 Q_D(const CredentialHttp);
93 return d->usernameField;
94}
95
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 QString password = authinfo.value(passwordField);
135 const QString storedPassword = user.value(passwordField).toString();
136
137 if (Q_LIKELY(passwordType == CredentialHttp::Hashed)) {
138 if (!passwordPreSalt.isEmpty()) {
139 password.prepend(password);
140 }
141
142 if (!passwordPostSalt.isEmpty()) {
143 password.append(password);
144 }
145
146 return CredentialPassword::validatePassword(password.toUtf8(), storedPassword.toUtf8());
147 } else if (passwordType == CredentialHttp::Clear) {
148 return storedPassword == password;
149 } else if (passwordType == CredentialHttp::None) {
150 qCCritical(C_CREDENTIALHTTP) << "CredentialPassword is set to ignore password check";
151 return true;
152 }
153
154 return false;
155}
156
157AuthenticationUser CredentialHttpPrivate::authenticateBasic(Context *c,
158 AuthenticationRealm *realm,
159 const ParamsMultiMap &authinfo)
160{
161 Q_UNUSED(authinfo)
163 qCDebug(C_CREDENTIALHTTP) << "Checking http basic authentication.";
164
165 const auto userPass = c->req()->headers().authorizationBasicObject();
166 if (userPass.user.isEmpty()) {
167 return user;
168 }
169
170 ParamsMultiMap auth;
171 auth.insert(usernameField, userPass.user);
172 AuthenticationUser _user = realm->findUser(c, auth);
173 if (!_user.isNull()) {
174 auth.insert(passwordField, userPass.password);
175 if (checkPassword(_user, auth)) {
176 user = _user;
177 } else {
178 qCDebug(C_CREDENTIALHTTP) << "Password didn't match";
179 }
180 } else {
181 qCDebug(C_CREDENTIALHTTP) << "Unable to locate a user matching user info provided in realm";
182 }
183 return user;
184}
185
186AuthenticationUser CredentialHttpPrivate::authenticationFailed(Context *c,
187 AuthenticationRealm *realm,
188 const ParamsMultiMap &authinfo)
189{
190 Q_UNUSED(authinfo);
191 Response *res = c->response();
192 res->setStatus(Response::Unauthorized); // 401
193 res->setContentType(QStringLiteral("text/plain; charset=UTF-8"));
194
195 if (authorizationRequiredMessage.isEmpty()) {
196 res->setBody(QStringLiteral("Authorization required."));
197 } else {
198 res->setBody(authorizationRequiredMessage);
199 }
200
201 // Create Basic response
202 if (isAuthTypeBasic()) {
203 createBasicAuthResponse(c, realm);
204 }
205
206 return AuthenticationUser();
207}
208
209bool CredentialHttpPrivate::isAuthTypeBasic() const
210{
211 return type == CredentialHttp::Basic || type == CredentialHttp::Any;
212}
213
214void CredentialHttpPrivate::createBasicAuthResponse(Context *c, AuthenticationRealm *realm)
215{
217 joinAuthHeaderParts(QStringLiteral("Basic"), buildAuthHeaderCommon(realm)));
218}
219
220QStringList CredentialHttpPrivate::buildAuthHeaderCommon(AuthenticationRealm *realm) const
221{
222 QStringList ret;
223 // TODO
224 // return realm="realmname"
225 // return domain="realmname"
226 if (!realm->name().isEmpty()) {
227 ret.append(u"realm=\"" + realm->name() + u'"');
228 }
229 return ret;
230}
231
232QString CredentialHttpPrivate::joinAuthHeaderParts(const QString &type,
233 const QStringList &parts) const
234{
235 QString ret = type;
236 if (!parts.isEmpty()) {
237 ret.append(u' ' + parts.join(u", "));
238 }
239 return ret;
240}
241
242#include "moc_credentialhttp.cpp"
AuthenticationCredential(QObject *parent=nullptr)
Constructs a new AuthenticationCredential object with the given parent.
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:33
The Cutelyst Context.
Definition context.h:39
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)
CredentialHttp(QObject *parent=nullptr)
Constructs a new CredentialHttp object with the given parent.
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:352
void setWwwAuthenticate(const QString &value)
Definition headers.cpp:322
Headers headers() const noexcept
Definition request.cpp:310
Headers & headers() noexcept
Definition response.cpp:339
void setStatus(quint16 status) noexcept
Definition response.cpp:70
void setBody(QIODevice *body)
Definition response.cpp:100
void setContentType(const QString &type)
Definition response.h:220
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap
void append(const T &value)
bool isEmpty() const const
typename QMap< Key, T >::iterator insert(const Key &key, const T &value)
QObject(QObject *parent)
QObject * parent() const const
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