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