summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorTomas Mraz <tmraz@redhat.com>2011-11-07 17:42:00 +0100
committerTomas Mraz <tmraz@redhat.com>2011-11-07 17:42:00 +0100
commita91d98e5cc2955763f76ec29dadc62235331da63 (patch)
tree8cb3fdd515530bdb6ea8e1720cf7fe0efbf768d9 /python
parent2aff91b770e617c54d97fd96119f573d9a9c0777 (diff)
downloadlibpwquality-a91d98e5cc2955763f76ec29dadc62235331da63.tar.gz
Add Python interface for libpwquality.
Diffstat (limited to 'python')
-rw-r--r--python/Makefile.am16
-rw-r--r--python/pwquality.c396
-rwxr-xr-xpython/setup.py74
3 files changed, 486 insertions, 0 deletions
diff --git a/python/Makefile.am b/python/Makefile.am
new file mode 100644
index 0000000..acdf7f5
--- /dev/null
+++ b/python/Makefile.am
@@ -0,0 +1,16 @@
+#
+# Copyright (c) 2011 Red Hat, Inc.
+# Copyright (c) 2011 Tomas Mraz <tm@t8m.info>
+#
+
+pythonincdir = $(includedir)/python@PYTHONREV@
+pythonsitedir = $(libdir)/python@PYTHONREV@/site-packages
+
+CLEANFILES = *~ constants.c pwquality.so
+
+EXTRA_DIST = pwquality.c setup.py
+
+pythonsite_SCRIPTS = pwquality.so
+
+pwquality.so: ../src/pwquality.h pwquality.c setup.py
+ python setup.py build --build-lib=.
diff --git a/python/pwquality.c b/python/pwquality.c
new file mode 100644
index 0000000..e60152f
--- /dev/null
+++ b/python/pwquality.c
@@ -0,0 +1,396 @@
+/*
+ * libpwquality Python bindings
+ *
+ * Copyright (c) Red Hat, Inc, 2011
+ * Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+ *
+ * See the end of the file for the License Information
+ */
+
+#include <Python.h>
+#include "pwquality.h"
+
+static PyObject *PWQError;
+
+typedef struct {
+ PyObject_HEAD
+ pwquality_settings_t *pwq;
+} PWQSettings;
+
+static PyObject *
+pwqsettings_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static void
+pwqsettings_dealloc(PWQSettings *self);
+static PyObject *
+pwqsettings_getint(PWQSettings *self, void *setting);
+static int
+pwqsettings_setint(PWQSettings *self, PyObject *value, void *setting);
+static PyObject *
+pwqsettings_getstr(PWQSettings *self, void *setting);
+static int
+pwqsettings_setstr(PWQSettings *self, PyObject *value, void *setting);
+static PyObject *
+read_config(PWQSettings *self, PyObject *args);
+static PyObject *
+set_option(PWQSettings *self, PyObject *args);
+static PyObject *
+generate(PWQSettings *self, PyObject *args);
+static PyObject *
+check(PWQSettings *self, PyObject *args);
+
+static PyMethodDef pwqsettings_methods[] = {
+ { "read_config", (PyCFunction)read_config, METH_VARARGS,
+ "Read the settings from configuration file"
+ },
+ { "set_option", (PyCFunction)set_option, METH_VARARGS,
+ "Set option from name=value pair"
+ },
+ { "generate", (PyCFunction)generate, METH_VARARGS,
+ "Generate password with requested entropy"
+ },
+ { "check", (PyCFunction)check, METH_VARARGS,
+ "Check whether the password conforms to the requirements and return password strength score"
+ },
+ { NULL } /* Sentinel */
+};
+
+static PyGetSetDef pwqsettings_getseters[] = {
+ { "difok",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Minimum difference from the old password",
+ (void *)PWQ_SETTING_DIFF_OK
+ },
+ { "minlen",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Minimum length of the new password",
+ (void *)PWQ_SETTING_MIN_LENGTH
+ },
+ { "dcredit",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Credit for or minimum of digits",
+ (void *)PWQ_SETTING_DIG_CREDIT
+ },
+ { "ucredit",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Credit for or minimum of uppercase characters",
+ (void *)PWQ_SETTING_UP_CREDIT
+ },
+ { "lcredit",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Credit for or minimum of lowercase characters",
+ (void *)PWQ_SETTING_LOW_CREDIT
+ },
+ { "ocredit",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Credit for or minimum of other characters",
+ (void *)PWQ_SETTING_OTH_CREDIT
+ },
+ { "minclass",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Minimum number of character classes",
+ (void *)PWQ_SETTING_MIN_CLASS
+ },
+ { "maxrepeat",
+ (getter)pwqsettings_getint, (setter)pwqsettings_setint,
+ "Maximum repeated consecutive characters",
+ (void *)PWQ_SETTING_MAX_REPEAT
+ },
+ { "dictpath",
+ (getter)pwqsettings_getstr, (setter)pwqsettings_setstr,
+ "Path to the cracklib dictionary",
+ (void *)PWQ_SETTING_DICT_PATH
+ },
+ { NULL } /* Sentinel */
+};
+
+
+static PyTypeObject pwqsettings_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "pwquality.PWQSettings", /* tp_name */
+ sizeof(PWQSettings), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)pwqsettings_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "PWQSettings objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ pwqsettings_methods, /* tp_methods */
+ 0, /* tp_members */
+ pwqsettings_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ pwqsettings_new, /* tp_new */
+};
+
+static PyMethodDef pwquality_methods[] = {
+ { NULL } /* Sentinel */
+};
+
+static PyObject *
+pwqerror(int rc, void *auxerror)
+{
+ char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+ PyObject *py_errvalue;
+ const char *msg;
+
+ msg = pwquality_strerror(buf, sizeof(buf), rc, auxerror);
+
+ if (rc == PWQ_ERROR_MEM_ALLOC)
+ return PyErr_NoMemory();
+
+ py_errvalue = Py_BuildValue("is", rc, msg);
+ if (py_errvalue == NULL)
+ return NULL;
+
+ if (rc == PWQ_ERROR_UNKNOWN_SETTING || rc == PWQ_ERROR_NON_INT_SETTING
+ || rc == PWQ_ERROR_NON_STR_SETTING) {
+ PyErr_SetObject(PyExc_AttributeError, py_errvalue);
+ } else {
+ PyErr_SetObject(PWQError, py_errvalue);
+ }
+ return NULL;
+}
+
+static PyObject *
+pwqsettings_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PWQSettings *self;
+
+ self = (PWQSettings *)type->tp_alloc(type, 0);
+ if (self) {
+ self->pwq = pwquality_default_settings();
+ if (self->pwq == NULL) {
+ Py_DECREF(self);
+ return PyErr_NoMemory();
+ }
+ }
+ return (PyObject *)self;
+}
+
+static void
+pwqsettings_dealloc(PWQSettings *self)
+{
+ pwquality_free_settings(self->pwq);
+ self->ob_type->tp_free((PyObject *)self);
+}
+
+static PyObject *
+pwqsettings_getint(PWQSettings *self, void *setting)
+{
+ int value;
+ int rc;
+
+ if ((rc = pwquality_get_int_value(self->pwq, (int)(ssize_t)setting, &value)) < 0) {
+ return pwqerror(rc, NULL);
+ }
+ return PyInt_FromLong((long)value);
+}
+
+static int
+pwqsettings_setint(PWQSettings *self, PyObject *value, void *setting)
+{
+ long l;
+ int rc;
+
+ l = PyInt_AsLong(value);
+ if (PyErr_Occurred() == NULL) {
+ if ((rc = pwquality_set_int_value(self->pwq,
+ (int)(ssize_t)setting, (int)l)) < 0) {
+ pwqerror(rc, NULL);
+ return -1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static PyObject *
+pwqsettings_getstr(PWQSettings *self, void *setting)
+{
+ const char *value;
+ int rc;
+
+ if ((rc = pwquality_get_str_value(self->pwq, (int)(ssize_t)setting, &value)) < 0) {
+ return pwqerror(rc, NULL);
+ }
+ if (value == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ return PyString_FromString(value);
+}
+
+static int
+pwqsettings_setstr(PWQSettings *self, PyObject *value, void *setting)
+{
+ const char *s;
+ int rc;
+
+ if (value == (PyObject *)Py_None)
+ s = NULL;
+ else
+ s = PyString_AsString(value);
+
+ if (PyErr_Occurred() == NULL) {
+ if ((rc = pwquality_set_str_value(self->pwq,
+ (int)(ssize_t)setting, s)) < 0) {
+ pwqerror(rc, NULL);
+ return -1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static PyObject *
+read_config(PWQSettings *self, PyObject *args)
+{
+ char *cfgfile = NULL;
+ void *auxerror;
+ int rc;
+
+ if (!PyArg_ParseTuple(args, "|s", &cfgfile))
+ return NULL;
+ if ((rc = pwquality_read_config(self->pwq, cfgfile, &auxerror)) < 0) {
+ return pwqerror(rc, auxerror);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+set_option(PWQSettings *self, PyObject *args)
+{
+ char *option;
+ int rc;
+
+ if (!PyArg_ParseTuple(args, "s", &option))
+ return NULL;
+ if ((rc = pwquality_set_option(self->pwq, option)) < 0) {
+ return pwqerror(rc, NULL);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+generate(PWQSettings *self, PyObject *args)
+{
+ int entropy_bits;
+ char *password;
+ PyObject *passobj;
+ int rc;
+
+ if (!PyArg_ParseTuple(args, "i", &entropy_bits))
+ return NULL;
+ if ((rc = pwquality_generate(self->pwq, entropy_bits, &password)) < 0) {
+ return pwqerror(rc, NULL);
+ }
+
+ passobj = PyString_FromString(password);
+ free(password);
+ return passobj;
+}
+
+static PyObject *
+check(PWQSettings *self, PyObject *args)
+{
+ char *password;
+ char *oldpassword = NULL;
+ char *username = NULL;
+ void *auxerror;
+ int rc;
+
+ if (!PyArg_ParseTuple(args, "s|ss", &password, &oldpassword, &username))
+ return NULL;
+ if ((rc = pwquality_check(self->pwq, password, oldpassword,
+ username, &auxerror)) < 0) {
+ return pwqerror(rc, auxerror);
+ }
+
+ return PyInt_FromLong((long)rc);
+}
+
+PyMODINIT_FUNC
+initpwquality(void)
+{
+ PyObject *module;
+
+ if (PyType_Ready(&pwqsettings_type) < 0)
+ return;
+
+ module = Py_InitModule3("pwquality", pwquality_methods,
+ "Libpwquality wrapper module");
+ if (module == NULL)
+ return;
+
+ PWQError = PyErr_NewException("pwquality.PWQError", NULL, NULL);
+ if (PWQError == NULL) {
+ Py_DECREF(module);
+ return;
+ }
+ Py_INCREF(PWQError);
+ PyModule_AddObject(module, "PWQError", PWQError);
+
+ Py_INCREF(&pwqsettings_type);
+ PyModule_AddObject(module, "PWQSettings", (PyObject *)&pwqsettings_type);
+
+#include "constants.c"
+}
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/python/setup.py b/python/setup.py
new file mode 100755
index 0000000..b9a7900
--- /dev/null
+++ b/python/setup.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# See the end of the file for Copyright and License Information
+#
+
+from distutils.core import setup, Extension
+from distutils.command.build_ext import build_ext as _build_ext
+
+class build_ext(_build_ext):
+ def genconstants(self, headerfile, outputfile):
+ hf = open(headerfile, 'r')
+ of = open(outputfile, 'w')
+ of.write('/* This file is generated during build time from pwquality.h */\n\n')
+ for line in hf:
+ if line.startswith('#define PWQ_'):
+ s = line.split()
+ of.write('PyModule_AddIntConstant(module, "%s", %s);\n' % (s[1], s[2]))
+
+ def run(self):
+ self.genconstants('../src/pwquality.h', 'constants.c')
+ _build_ext.run(self)
+
+pwqmodule = Extension('pwquality',
+ sources = ['pwquality.c'],
+ include_dirs = ['../src'],
+ library_dirs = ['../src/.libs'],
+ libraries = ['pwquality'])
+
+setup(
+ name = 'pwquality',
+ version = '0.9.1',
+ description = 'Python bindings for the libpwquality library for password quality checking',
+ author = 'Tomáš Mráz',
+ author_email = 'tm@t8m.info',
+ url = 'http://fedorahosted.org/libpwquality',
+ license = 'BSD or GPL+',
+ ext_modules = [pwqmodule],
+ cmdclass = {'build_ext': build_ext}
+)
+
+# Copyright (c) Red Hat, Inc, 2011
+# Copyright (c) Tomas Mraz <tm@t8m.info>, 2011
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, and the entire permission notice in its entirety,
+# including the disclaimer of warranties.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# ALTERNATIVELY, this product may be distributed under the terms of
+# the GNU Public License, in which case the provisions of the GPL are
+# required INSTEAD OF the above restrictions. (This clause is
+# necessary due to a potential bad interaction between the GPL and
+# the restrictions contained in a BSD-style copyright.)
+#
+# THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.