cutelyst 4.3.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validatorpwquality.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2018-2023 Matthias Fehring <mf@huessenbergnetz.de>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6#include "validatorpwquality_p.h"
7
8#include <pwquality.h>
9
10#include <QLoggingCategory>
11
12using namespace Cutelyst;
13
15 int threshold,
16 const QVariant &options,
17 const QString &userName,
18 const QString &oldPassword,
19 const ValidatorMessages &messages)
20 : ValidatorRule(*new ValidatorPwQualityPrivate(field,
21 threshold,
22 options,
23 userName,
24 oldPassword,
25 messages))
26{
27}
28
30
32 const QVariant &options,
33 const QString &oldPassword,
34 const QString &user)
35{
36 int rv = 0;
37
38 if (!value.isEmpty()) {
39
40 pwquality_settings_t *pwq = pwquality_default_settings();
41 if (pwq) {
42
43 bool optionsSet = false;
44 if (options.isValid()) {
45 if (options.typeId() == QMetaType::QVariantMap) {
46 const QVariantMap map = options.toMap();
47 if (!map.empty()) {
48 auto i = map.constBegin();
49 while (i != map.constEnd()) {
50 const QString opt = i.key() + QLatin1Char('=') + i.value().toString();
51 const int orv = pwquality_set_option(pwq, opt.toUtf8().constData());
52 if (orv != 0) {
53 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
54 qCWarning(C_VALIDATOR).noquote().nospace()
55 << "ValidatorPwQuality: Failed to set pwquality option " << opt
56 << ": "
57 << pwquality_strerror(buf.data(), buf.size(), orv, nullptr);
58 }
59 ++i;
60 }
61 optionsSet = true;
62 }
63 } else if (options.typeId() == QMetaType::QString) {
64 const QString configFile = options.toString();
65 if (!configFile.isEmpty()) {
66 if (C_VALIDATOR().isWarningEnabled()) {
67 void *auxerror = nullptr;
68 const int rcrv = pwquality_read_config(
69 pwq, configFile.toUtf8().constData(), &auxerror);
70 if (rcrv != 0) {
71 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
72 qCWarning(C_VALIDATOR).noquote().nospace()
73 << "ValidatorPwQuality: Failed to read configuration file "
74 << configFile << ": "
75 << pwquality_strerror(buf.data(), buf.size(), rcrv, auxerror);
76 }
77 } else {
78 pwquality_read_config(pwq, configFile.toUtf8().constData(), nullptr);
79 }
80 optionsSet = true;
81 }
82 }
83 }
84
85 if (!optionsSet) {
86 if (C_VALIDATOR().isWarningEnabled()) {
87 void *auxerror = nullptr;
88 const int rcrv = pwquality_read_config(pwq, nullptr, &auxerror);
89 if (rcrv != 0) {
90 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
91 qCWarning(C_VALIDATOR).noquote()
92 << "VaidatorPwQuality: Failed to read default configuration file:"
93 << pwquality_strerror(buf.data(), buf.size(), rcrv, auxerror);
94 }
95 } else {
96 pwquality_read_config(pwq, nullptr, nullptr);
97 }
98 }
99
100 const QByteArray pwba = value.toUtf8();
101 const char *pw = pwba.constData();
102 const QByteArray opwba = oldPassword.toUtf8();
103 const char *opw = opwba.isEmpty() ? nullptr : opwba.constData();
104 const QByteArray uba = user.toUtf8();
105 const char *u = uba.isEmpty() ? nullptr : uba.constData();
106
107 rv = pwquality_check(pwq, pw, opw, u, nullptr);
108
109 pwquality_free_settings(pwq);
110
111 } else {
112 rv = PWQ_ERROR_MEM_ALLOC;
113 }
114 } else {
115 rv = PWQ_ERROR_EMPTY_PASSWORD;
116 }
117
118 return rv;
119}
120
122 int returnValue,
123 const QString &label,
124 int threshold)
125{
126 if (label.isEmpty()) {
127 switch (returnValue) {
128 case PWQ_ERROR_MEM_ALLOC:
129 //% "Password quality check failed because of a memory allocation error."
130 return c->qtTrId("cutelyst-valpwq-err-memalloc");
131 case PWQ_ERROR_SAME_PASSWORD:
132 //% "The password is the same as the old one."
133 return c->qtTrId("cutelyst-valpwq-err-samepass");
134 case PWQ_ERROR_PALINDROME:
135 //% "The password is a palindrome."
136 return c->qtTrId("cutelyst-valpwq-err-palindrome");
137 case PWQ_ERROR_CASE_CHANGES_ONLY:
138 //% "The password differs with case changes only from the old one."
139 return c->qtTrId("cutelyst-valpwq-err-casechangesonly");
140 case PWQ_ERROR_TOO_SIMILAR:
141 //% "The password is too similar to the old one."
142 return c->qtTrId("cutelyst-valpwq-err-toosimilar");
143 case PWQ_ERROR_USER_CHECK:
144 //% "The password contains the user name in some form."
145 return c->qtTrId("cutelyst-valpwq-err-usercheck");
146 case PWQ_ERROR_GECOS_CHECK:
147 //% "The password contains words from the real name of the user in some form."
148 return c->qtTrId("cutelyst-valpwq-err-gecoscheck");
149 case PWQ_ERROR_BAD_WORDS:
150 //% "The password contains forbidden words in some form."
151 return c->qtTrId("cutelyst-valpwq-err-badwords");
152 case PWQ_ERROR_MIN_DIGITS:
153 //% "The password does not contain enough digits."
154 return c->qtTrId("cutelyst-valpwq-err-mindigits");
155 case PWQ_ERROR_MIN_UPPERS:
156 //% "The password does not contain enough uppercase letters."
157 return c->qtTrId("cutelyst-valpwq-err-minuppers");
158 case PWQ_ERROR_MIN_LOWERS:
159 //% "The password does not contain enough lowercase letters."
160 return c->qtTrId("cutelyst-valpwq-err-minlowers");
161 case PWQ_ERROR_MIN_OTHERS:
162 //% "The password does not contain enough non-alphanumeric characters."
163 return c->qtTrId("cutelyst-valpwq-err-minothers");
164 case PWQ_ERROR_MIN_LENGTH:
165 //% "The password is too short."
166 return c->qtTrId("cutelyst-valpwq-err-minlength");
167 case PWQ_ERROR_ROTATED:
168 //% "The password is just the rotated old one."
169 return c->qtTrId("cutelyst-valpwq-err-rotated");
170 case PWQ_ERROR_MIN_CLASSES:
171 //% "The password does not contain enough different character types."
172 return c->qtTrId("cutelyst-valpwq-err-minclasses");
173 case PWQ_ERROR_MAX_CONSECUTIVE:
174 //% "The password contains too many same characters consecutively."
175 return c->qtTrId("cutelyst-valpwq-err-maxconsecutive");
176 case PWQ_ERROR_MAX_CLASS_REPEAT:
177 //% "The password contains too many characters of the same type consecutively."
178 return c->qtTrId("cutelyst-valpwq-err-maxclassrepeat");
179 case PWQ_ERROR_MAX_SEQUENCE:
180 //% "The password contains too long a monotonous string."
181 return c->qtTrId("cutelyst-valpwq-err-maxsequence");
182 case PWQ_ERROR_EMPTY_PASSWORD:
183 //% "No password supplied."
184 return c->qtTrId("cutelyst-valpwq-err-emptypw");
185 case PWQ_ERROR_RNG:
186 //% "Password quality check failed because we cannot obtain random "
187 //% "numbers from the RNG device."
188 return c->qtTrId("cutelyst-valpwq-err-rng");
189 case PWQ_ERROR_CRACKLIB_CHECK:
190 //% "The password fails the dictionary check."
191 return c->qtTrId("cutelyst-valpwq-err-cracklibcheck");
192 case PWQ_ERROR_UNKNOWN_SETTING:
193 //% "Password quality check failed because of an unknown setting."
194 return c->qtTrId("cutelyst-valpwq-err-unknownsetting");
195 case PWQ_ERROR_INTEGER:
196 //% "Password quality check failed because of a bad integer value in the settings."
197 return c->qtTrId("cutelyst-valpwq-err-integer");
198 case PWQ_ERROR_NON_INT_SETTING:
199 //% "Password quality check failed because of a settings entry is not "
200 //% "of integer type."
201 return c->qtTrId("cutelyst-valpwq-err-nonintsetting");
202 case PWQ_ERROR_NON_STR_SETTING:
203 //% "Password quality check failed because of a settings entry is not of string type."
204 return c->qtTrId("cutelyst-valpwq-err-nonstrsetting");
205 case PWQ_ERROR_CFGFILE_OPEN:
206 //% "Password quality check failed because opening the configuration file failed."
207 return c->qtTrId("cutelyst-valpwq-err-cfgfileopen");
208 case PWQ_ERROR_CFGFILE_MALFORMED:
209 //% "Password quality check failed because the configuration file is malformed."
210 return c->qtTrId("cutelyst-valpwq-err-cfgfilemalformed");
211 case PWQ_ERROR_FATAL_FAILURE:
212 //% "Password quality check failed because of a fatal failure."
213 return c->qtTrId("cutelyst-valpwq-err-fatalfailure");
214 default:
215 {
216 if (returnValue < 0) {
217 //% "Password quality check failed because of an unknown error."
218 return c->qtTrId("cutelyst-valpwq-err-unknown");
219 } else {
220 if (returnValue < threshold) {
221 //% "The password quality score of %1 is below the threshold of %2."
222 return c->qtTrId("cutelyst-valpwq-err-belowthreshold")
223 .arg(QString::number(returnValue), QString::number(threshold));
224 } else {
225 return {};
226 }
227 }
228 }
229 }
230 } else {
231 switch (returnValue) {
232 case PWQ_ERROR_MEM_ALLOC:
233 //% "Password quality check for the “%1“ field failed because of a "
234 //% "memory allocation error."
235 return c->qtTrId("cutelyst-valpwq-err-memalloc-label").arg(label);
236 case PWQ_ERROR_SAME_PASSWORD:
237 //% "The password in the “%1” field is the same as the old one."
238 return c->qtTrId("cutelyst-valpwq-err-samepass-label").arg(label);
239 case PWQ_ERROR_PALINDROME:
240 //% "The password in the “%1” field is a palindrome."
241 return c->qtTrId("cutelyst-valpwq-err-palindrome-label").arg(label);
242 case PWQ_ERROR_CASE_CHANGES_ONLY:
243 //% "The password in the “%1” field differs with case changes only from the old one."
244 return c->qtTrId("cutelyst-valpwq-err-casechangesonly-label").arg(label);
245 case PWQ_ERROR_TOO_SIMILAR:
246 //% "The password in the “%1” field is too similar to the old one."
247 return c->qtTrId("cutelyst-valpwq-err-toosimilar-label").arg(label);
248 case PWQ_ERROR_USER_CHECK:
249 //% "The password in the “%1” field contains the user name in some form."
250 return c->qtTrId("cutelyst-valpwq-err-usercheck-label").arg(label);
251 case PWQ_ERROR_GECOS_CHECK:
252 //% "The password in the “%1” field contains words from the real name "
253 //% "of the user name in some form."
254 return c->qtTrId("cutelyst-valpwq-err-gecoscheck-label").arg(label);
255 case PWQ_ERROR_BAD_WORDS:
256 //% "The password in the “%1” field contains forbidden words in some form."
257 return c->qtTrId("cutelyst-valpwq-err-badwords-label").arg(label);
258 case PWQ_ERROR_MIN_DIGITS:
259 //% "The password in the “%1” field does not contain enough digits."
260 return c->qtTrId("cutelyst-valpwq-err-mindigits-label").arg(label);
261 case PWQ_ERROR_MIN_UPPERS:
262 //% "The password in the “%1” field does not contain enough uppercase letters."
263 return c->qtTrId("cutelyst-valpwq-err-minuppers-label").arg(label);
264 case PWQ_ERROR_MIN_LOWERS:
265 //% "The password in the “%1” field does not contain enough lowercase letters."
266 return c->qtTrId("cutelyst-valpwq-err-minlowers-label").arg(label);
267 case PWQ_ERROR_MIN_OTHERS:
268 //% "The password in the “%1” field does not contain enough non-alphanumeric "
269 //% "characters."
270 return c->qtTrId("cutelyst-valpwq-err-minothers-label").arg(label);
271 case PWQ_ERROR_MIN_LENGTH:
272 //% "The password in the “%1” field is too short."
273 return c->qtTrId("cutelyst-valpwq-err-minlength-label").arg(label);
274 case PWQ_ERROR_ROTATED:
275 //% "The password in the “%1” field is just the rotated old one."
276 return c->qtTrId("cutelyst-valpwq-err-rotated-label").arg(label);
277 case PWQ_ERROR_MIN_CLASSES:
278 //% "The password in the “%1” field does not contain enough character types."
279 return c->qtTrId("cutelyst-valpwq-err-minclasses-label").arg(label);
280 case PWQ_ERROR_MAX_CONSECUTIVE:
281 //% "The password in the “%1” field contains too many same characters "
282 //% "consecutively."
283 return c->qtTrId("cutelyst-valpwq-err-maxconsecutive-label").arg(label);
284 case PWQ_ERROR_MAX_CLASS_REPEAT:
285 //% "The password in the “%1” field contains too many characters of "
286 //% "the same type consecutively."
287 return c->qtTrId("cutelyst-valpwq-err-maxclassrepeat-label").arg(label);
288 case PWQ_ERROR_MAX_SEQUENCE:
289 //% "The password in the “%1” field contains contains too long a "
290 //% "monotonous string."
291 return c->qtTrId("cutelyst-valpwq-err-maxsequence-label").arg(label);
292 case PWQ_ERROR_EMPTY_PASSWORD:
293 //% "No password supplied in the “%1” field."
294 return c->qtTrId("cutelyst-valpwq-err-emptypw-label").arg(label);
295 case PWQ_ERROR_RNG:
296 //% "Password quality check for the “%1“ field failed because we "
297 //% "cannot obtain random numbers from the RNG device."
298 return c->qtTrId("cutelyst-valpwq-err-rng-label").arg(label);
299 case PWQ_ERROR_CRACKLIB_CHECK:
300 //% "The password in the “%1” field fails the dictionary check."
301 return c->qtTrId("cutelyst-valpwq-err-cracklibcheck-label").arg(label);
302 case PWQ_ERROR_UNKNOWN_SETTING:
303 //% "Password quality check for the “%1“ field failed because of an "
304 //% "unknown setting."
305 return c->qtTrId("cutelyst-valpwq-err-unknownsetting-label").arg(label);
306 case PWQ_ERROR_INTEGER:
307 //% "Password quality check for the “%1“ field failed because of a "
308 //% "bad integer value in the settings."
309 return c->qtTrId("cutelyst-valpwq-err-integer-label").arg(label);
310 case PWQ_ERROR_NON_INT_SETTING:
311 //% "Password quality check for the “%1“ field failed because of a "
312 //% "settings entry is not of integer type."
313 return c->qtTrId("cutelyst-valpwq-err-nonintsetting-label").arg(label);
314 case PWQ_ERROR_NON_STR_SETTING:
315 //% "Password quality check for the “%1“ field failed because of a "
316 //% "settings entry is not of string type."
317 return c->qtTrId("cutelyst-valpwq-err-nonstrsetting-label").arg(label);
318 case PWQ_ERROR_CFGFILE_OPEN:
319 //% "Password quality check for the “%1“ field failed because opening "
320 //% "the configuration file failed."
321 return c->qtTrId("cutelyst-valpwq-err-cfgfileopen-label").arg(label);
322 case PWQ_ERROR_CFGFILE_MALFORMED:
323 //% "Password quality check for the “%1“ field failed because the "
324 //% "configuration file is malformed."
325 return c->qtTrId("cutelyst-valpwq-err-cfgfilemalformed-label").arg(label);
326 case PWQ_ERROR_FATAL_FAILURE:
327 //% "Password quality check for the “%1“ field failed because of a fatal failure."
328 return c->qtTrId("cutelyst-valpwq-err-fatalfailure-label").arg(label);
329 default:
330 {
331 if (returnValue < 0) {
332 //% "Password quality check for the “%1” field failed because of "
333 //% "an unknown error."
334 return c->qtTrId("cutelyst-valpwq-err-unknown-label").arg(label);
335 } else {
336 if (returnValue < threshold) {
337 //% "The quality score of %1 for the password in the “%2” field "
338 //% "is below the threshold of %3."
339 return c->qtTrId("cutelyst-valpwq-err-belowthreshold-label")
340 .arg(QString::number(returnValue), QString::number(threshold));
341 } else {
342 return {};
343 }
344 }
345 }
346 }
347 }
348}
349
351{
352 ValidatorReturnType result;
353
354 const QString v = value(params);
355
356 if (!v.isEmpty()) {
357 Q_D(const ValidatorPwQuality);
358 QVariant opts;
359 if (d->options.isValid()) {
360 if (d->options.typeId() == QMetaType::QVariantMap) {
361 opts = d->options;
362 } else if (d->options.typeId() == QMetaType::QString) {
363 const QString optString = d->options.toString();
364 if (c->stash().contains(optString)) {
365 opts = c->stash(optString);
366 } else {
367 opts = d->options;
368 }
369 }
370 }
371 QString un;
372 if (!d->userName.isEmpty()) {
373 un = params.value(d->userName);
374 if (un.isEmpty()) {
375 un = c->stash(d->userName).toString();
376 }
377 }
378 QString opw;
379 if (!d->oldPassword.isEmpty()) {
380 opw = params.value(d->oldPassword);
381 if (opw.isEmpty()) {
382 opw = c->stash(d->oldPassword).toString();
383 }
384 }
385 int rv = validate(v, opts, opw, un);
386 if (rv < d->threshold) {
387 result.errorMessage = validationError(c, rv);
388 if (C_VALIDATOR().isDebugEnabled()) {
389 if (rv < 0) {
390 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
391 qCDebug(C_VALIDATOR).noquote()
392 << debugString(c)
393 << pwquality_strerror(buf.data(), buf.size(), rv, nullptr);
394 } else {
395 qCDebug(C_VALIDATOR).noquote() << debugString(c) << "The quality score" << rv
396 << "is below the threshold of" << d->threshold;
397 }
398 }
399 } else {
400 qCDebug(C_VALIDATOR).noquote()
401 << "ValidatorPwQuality: \"" << v << "\" got a quality score of" << rv;
402 result.value = v;
403 }
404 }
405
406 return result;
407}
408
410{
411 Q_D(const ValidatorPwQuality);
412 return ValidatorPwQuality::errorString(c, errorData.toInt(), label(c), d->threshold);
413}
The Cutelyst Context.
Definition context.h:42
void stash(const QVariantHash &unite)
Definition context.cpp:562
QString qtTrId(const char *id, int n=-1) const
Definition context.h:656
Validates an input field with libpwquality to check password quality.
static QString errorString(Context *c, int returnValue, const QString &label=QString(), int threshold=0)
QString genericValidationError(Context *c, const QVariant &errorData) const override
ValidatorPwQuality(const QString &field, int threshold=ValidatorPwQuality::defaultThreshold, const QVariant &options=QVariant(), const QString &userName=QString(), const QString &oldPassword=QString(), const ValidatorMessages &messages=ValidatorMessages())
Base class for all validator rules.
QString validationError(Context *c, const QVariant &errorData={}) const
QString label(Context *c) const
QString value(const ParamsMultiMap &params) const
QString debugString(Context *c) const
static int validate(const QString &value, const QVariant &options=QVariant(), const QString &oldPassword=QString(), const QString &user=QString())
Returns the password quality score for value.
The Cutelyst namespace holds all public Cutelyst API.
const char * constData() const const
bool isEmpty() const const
QList::pointer data()
qsizetype size() const const
T value(const Key &key, const T &defaultValue) const const
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QByteArray toUtf8() const const
bool isValid() const const
int toInt(bool *ok) const const
QMap< QString, QVariant > toMap() const const
QString toString() const const
int typeId() const const
Stores custom error messages and the input field label.
Contains the result of a single input parameter validation.