cutelyst 4.3.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
26{
27 delete d_ptr;
28}
29
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
55{
56 Q_D(const CredentialHttp);
57 return d->passwordType;
58}
59
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
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"
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:103
Request * request
Definition context.h:71
Request * req
Definition context.h:66
Response * response() const noexcept
Definition context.cpp:97
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:326
Authorization authorizationBasicObject() const
Definition headers.cpp:358
Headers headers() const noexcept
Definition request.cpp:312
A Cutelyst response.
Definition response.h:29
void setContentType(const QByteArray &type)
Definition response.h:238
void setStatus(quint16 status) noexcept
Definition response.cpp:72
void setBody(QIODevice *body)
Definition response.cpp:103
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