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