cutelyst 5.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;
16using namespace Qt::Literals::StringLiterals;
17
18Q_LOGGING_CATEGORY(C_CREDENTIALHTTP, "cutelyst.plugin.credentialhttp", QtWarningMsg)
19
22 , d_ptr(new CredentialHttpPrivate)
23{
24}
25
27{
28 delete d_ptr;
29}
30
32{
33 Q_D(CredentialHttp);
34 d->type = type;
35}
36
38{
39 Q_D(CredentialHttp);
40 d->authorizationRequiredMessage = message;
41}
42
44{
45 Q_D(const CredentialHttp);
46 return d->passwordField;
47}
48
50{
51 Q_D(CredentialHttp);
52 d->passwordField = fieldName;
53}
54
56{
57 Q_D(const CredentialHttp);
58 return d->passwordType;
59}
60
62{
63 Q_D(CredentialHttp);
64 d->passwordType = type;
65}
66
68{
69 Q_D(const CredentialHttp);
70 return d->passwordPreSalt;
71}
72
73void CredentialHttp::setPasswordPreSalt(const QString &passwordPreSalt)
74{
75 Q_D(CredentialHttp);
76 d->passwordPreSalt = passwordPreSalt;
77}
78
80{
81 Q_D(const CredentialHttp);
82 return d->passwordPostSalt;
83}
84
85void CredentialHttp::setPasswordPostSalt(const QString &passwordPostSalt)
86{
87 Q_D(CredentialHttp);
88 d->passwordPostSalt = passwordPostSalt;
89}
90
92{
93 Q_D(const CredentialHttp);
94 return d->usernameField;
95}
96
98{
99 Q_D(CredentialHttp);
100 d->usernameField = fieldName;
101}
102
104{
105 Q_D(CredentialHttp);
106 d->requireSsl = require;
107}
108
110 AuthenticationRealm *realm,
111 const ParamsMultiMap &authinfo)
112{
113 Q_D(CredentialHttp);
114
116 if (d->requireSsl && !c->request()->secure()) {
117 ret = d->authenticationFailed(c, realm, authinfo);
118 return ret;
119 }
120
121 if (d->isAuthTypeBasic()) {
122 ret = d->authenticateBasic(c, realm, authinfo);
123 if (!ret.isNull()) {
124 return ret;
125 }
126 }
127
128 ret = d->authenticationFailed(c, realm, authinfo);
129 return ret;
130}
131
132bool CredentialHttpPrivate::checkPassword(const AuthenticationUser &user,
133 const ParamsMultiMap &authinfo)
134{
135 const QString password = passwordPreSalt + authinfo.value(passwordField) + passwordPostSalt;
136 const QString storedPassword = user.value(passwordField).toString();
137
138 if (Q_LIKELY(passwordType == CredentialHttp::Hashed)) {
139 return CredentialPassword::validatePassword(password.toUtf8(), storedPassword.toUtf8());
140 } else if (passwordType == CredentialHttp::Clear) {
141 return storedPassword == password;
142 } else if (passwordType == CredentialHttp::None) {
143 qCCritical(C_CREDENTIALHTTP) << "CredentialPassword is set to ignore password check";
144 return true;
145 }
146
147 return false;
148}
149
150AuthenticationUser CredentialHttpPrivate::authenticateBasic(Context *c,
151 AuthenticationRealm *realm,
152 const ParamsMultiMap &authinfo)
153{
154 Q_UNUSED(authinfo)
156 qCDebug(C_CREDENTIALHTTP) << "Checking http basic authentication.";
157
158 const auto userPass = c->req()->headers().authorizationBasicObject();
159 if (userPass.user.isEmpty()) {
160 return user;
161 }
162
163 ParamsMultiMap auth;
164 auth.insert(usernameField, userPass.user);
165 AuthenticationUser _user = realm->findUser(c, auth);
166 if (!_user.isNull()) {
167 auth.insert(passwordField, userPass.password);
168 if (checkPassword(_user, auth)) {
169 user = _user;
170 } else {
171 qCDebug(C_CREDENTIALHTTP) << "Password didn't match";
172 }
173 } else {
174 qCDebug(C_CREDENTIALHTTP) << "Unable to locate a user matching user info provided in realm";
175 }
176 return user;
177}
178
179AuthenticationUser CredentialHttpPrivate::authenticationFailed(Context *c,
180 AuthenticationRealm *realm,
181 const ParamsMultiMap &authinfo)
182{
183 Q_UNUSED(authinfo);
184 Response *res = c->response();
185 res->setStatus(Response::Unauthorized); // 401
186 res->setContentType("text/plain; charset=UTF-8"_ba);
187
188 if (authorizationRequiredMessage.isEmpty()) {
189 res->setBody("Authorization required."_ba);
190 } else {
191 res->setBody(authorizationRequiredMessage);
192 }
193
194 // Create Basic response
195 if (isAuthTypeBasic()) {
196 createBasicAuthResponse(c, realm);
197 }
198
199 return AuthenticationUser();
200}
201
202bool CredentialHttpPrivate::isAuthTypeBasic() const
203{
204 return type == CredentialHttp::Basic || type == CredentialHttp::Any;
205}
206
207void CredentialHttpPrivate::createBasicAuthResponse(Context *c, AuthenticationRealm *realm)
208{
210 joinAuthHeaderParts("Basic"_ba, buildAuthHeaderCommon(realm)));
211}
212
213QByteArrayList CredentialHttpPrivate::buildAuthHeaderCommon(AuthenticationRealm *realm) const
214{
215 QByteArrayList ret;
216 // TODO
217 // return realm="realmname"
218 // return domain="realmname"
219 if (!realm->name().isEmpty()) {
220 ret.append("realm=\"" + realm->name().toLatin1() + '"');
221 }
222 return ret;
223}
224
225QByteArray CredentialHttpPrivate::joinAuthHeaderParts(const QByteArray &type,
226 const QByteArrayList &parts) const
227{
228 QByteArray ret = type;
229 if (!parts.isEmpty()) {
230 ret.append(' ' + parts.join(", "));
231 }
232 return ret;
233}
234
235#include "moc_credentialhttp.cpp"
Abstract class to validate authentication credentials like user name and password.
Combines user store and credential validation into a named realm.
virtual AuthenticationUser findUser(Context *c, const ParamsMultiMap &userinfo)
Container for user data retrieved from an AuthenticationStore.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
QString name() const noexcept
Definition component.cpp:33
The Cutelyst Context.
Definition context.h:42
Response * res() const noexcept
Definition context.cpp:104
Request * request
Definition context.h:71
Request * req
Definition context.h:66
Response * response() const noexcept
Definition context.cpp:98
Use HTTP basic authentication to authenticate a user.
QString usernameField() const
void setPasswordType(PasswordType type)
void setUsernameField(const QString &fieldName)
QString passwordPreSalt() const
QString passwordPostSalt() const
void setPasswordPreSalt(const QString &passwordPreSalt)
QString passwordField() const
PasswordType passwordType() const
void setType(CredentialHttp::AuthType type)
AuthenticationUser authenticate(Context *c, AuthenticationRealm *realm, const ParamsMultiMap &authinfo) final
void setPasswordField(const QString &fieldName)
void setPasswordPostSalt(const QString &passwordPostSalt)
void setRequireSsl(bool require)
void setAuthorizationRequiredMessage(const QString &message)
static bool validatePassword(const QByteArray &password, const QByteArray &correctHash)
void setWwwAuthenticate(const QByteArray &value)
Definition headers.cpp:327
Authorization authorizationBasicObject() const
Definition headers.cpp:359
Headers headers() const noexcept
Definition request.cpp:313
A Cutelyst response.
Definition response.h:29
void setContentType(const QByteArray &type)
Definition response.h:230
void setStatus(quint16 status) noexcept
Definition response.cpp:74
void setBody(QIODevice *body)
Definition response.cpp:105
Headers & headers() noexcept
The Cutelyst namespace holds all public Cutelyst API.
QByteArray & append(QByteArrayView data)
QByteArray join(QByteArrayView separator) const const
void append(QList::parameter_type value)
bool isEmpty() const const
QMultiMap::iterator insert(QMultiMap::const_iterator pos, const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
bool isEmpty() const const
QByteArray toLatin1() const const
QByteArray toUtf8() const const
QString toString() const const