Skip to content
This repository was archived by the owner on Mar 8, 2023. It is now read-only.

Commit 24968ab

Browse files
author
Quentin Lux
committed
test nested sql configurations for mysql integration
1 parent 34db70d commit 24968ab

File tree

2 files changed

+83
-78
lines changed

2 files changed

+83
-78
lines changed

privacyidea_pam.py

Lines changed: 82 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import traceback
4949
import datetime
5050
import yaml
51+
import re
5152

5253

5354
def _get_config(argv):
@@ -70,11 +71,27 @@ def _get_config(argv):
7071
config[argument[0]] = True
7172
elif len(argument) == 2:
7273
config[argument[0]] = argument[1]
73-
# Users filter
74+
# User filter
7475
if config.get("users") is not None:
7576
config["users"] = config.get("users").split(',')
7677
else:
7778
config["users"] = []
79+
# SQL Connection type/default
80+
if config.get("mysql") is not None:
81+
mysql_settings = re.match("mysql://([^:]+):([^@]+)@([^:/]+):([0-9]+)/(.+)", config.get("mysql"))
82+
config["sql"] = {
83+
'lite': False,
84+
'user': mysql_settings.group(1),
85+
'password': mysql_settings.group(2),
86+
'host': mysql_settings.group(3),
87+
'port': mysql_settings.group(4),
88+
'database': mysql_settings.group(5)
89+
}
90+
else:
91+
config["sql"] = {
92+
'lite': True,
93+
'file': config.get("sqlfile", "/etc/privacyidea/pam.sqlite")
94+
}
7895
return config
7996

8097

@@ -94,7 +111,7 @@ def __init__(self, pamh, config):
94111
self.realm = config.get("realm")
95112
self.debug = config.get("debug")
96113
self.api_token = config.get("api_token")
97-
self.sqlfile = config.get("sqlfile", "/etc/privacyidea/pam.sqlite")
114+
self.sql = config.get("sql")
98115

99116
def make_request(self, data, endpoint="/validate/check",
100117
api_token=None, post=True):
@@ -226,14 +243,14 @@ def enroll_user(self, user):
226243
def offline_refill(self, serial, password):
227244

228245
# get refilltoken
229-
conn = sqlite3.connect(self.sqlfile)
230-
c = conn.cursor()
246+
startdb(self.sql)
231247
refilltoken = None
232248
# get all possible serial/tokens for a user
233249
for row in c.execute("SELECT refilltoken FROM refilltokens WHERE serial=?",
234250
(serial, )):
235251
refilltoken = row[0]
236252
syslog.syslog("Doing refill with token {0!s}".format(refilltoken))
253+
closedb()
237254

238255
if refilltoken:
239256
data = {"serial": serial,
@@ -253,7 +270,7 @@ def offline_refill(self, serial, password):
253270

254271
if result.get("status"):
255272
if result.get("value"):
256-
save_auth_item(self.sqlfile, self.user, serial, tokentype,
273+
save_auth_item(self.sql, self.user, serial, tokentype,
257274
auth_item)
258275
return True
259276
else:
@@ -264,13 +281,13 @@ def offline_refill(self, serial, password):
264281

265282
def authenticate(self, password):
266283
rval = self.pamh.PAM_SYSTEM_ERR
267-
# First we try to authenticate against the sqlitedb
268-
r, serial = check_offline_otp(self.user, password, self.sqlfile, window=10)
284+
# First we try to authenticate against the sqldb
285+
r, serial = check_offline_otp(self.sql, self.user, password, window=10)
269286
syslog.syslog(syslog.LOG_DEBUG, "offline check returned: {0!s}, {1!s}".format(r, serial))
270287
if r:
271288
syslog.syslog(syslog.LOG_DEBUG,
272289
"%s: successfully authenticated against offline "
273-
"database %s" % (__name__, self.sqlfile))
290+
"database" % (__name__))
274291

275292
# Try to refill
276293
try:
@@ -305,7 +322,7 @@ def authenticate(self, password):
305322
if result.get("status"):
306323
if result.get("value"):
307324
rval = self.pamh.PAM_SUCCESS
308-
save_auth_item(self.sqlfile, self.user, serial, tokentype,
325+
save_auth_item(self.sql, self.user, serial, tokentype,
309326
auth_item)
310327
else:
311328
transaction_id = detail.get("transaction_id")
@@ -335,7 +352,7 @@ def authenticate(self, password):
335352
self.pamh.conversation(pam_message)
336353

337354
# Save history
338-
save_history_item(self.sqlfile, self.user, self.rhost, serial,
355+
save_history_item(self.sql, self.user, self.rhost, serial,
339356
(True if rval == self.pamh.PAM_SUCCESS else False))
340357
return rval
341358

@@ -472,7 +489,7 @@ def pam_sm_authenticate(pamh, flags, argv):
472489
syslog.syslog(syslog.LOG_DEBUG,
473490
"Grace period in minutes: %s " % (str(grace_time)))
474491
# First we check if grace is authorized
475-
if check_last_history(Auth.sqlfile, Auth.user,
492+
if check_last_history(Auth.sql, Auth.user,
476493
Auth.rhost, grace_time, window=10):
477494
rval = pamh.PAM_SUCCESS
478495

@@ -536,22 +553,21 @@ def pam_sm_chauthtok(pamh, flags, argv):
536553
return pamh.PAM_SUCCESS
537554

538555

539-
def check_offline_otp(user, otp, sqlfile, window=10, refill=True):
556+
def check_offline_otp(sql_params, user, otp, window=10, refill=True):
540557
"""
541558
compare the given otp values with the next hashes of the user.
542559
543560
DB entries older than the matching counter will be deleted from the
544561
database.
545562
563+
:param sql_params: MySQL/SQLite connection parameters
564+
:type sql_params: dict
546565
:param user: The local user in the sql file
547566
:param otp: The otp value
548-
:param sqlfile: The sqlite file
549567
:return: Tuple of (True or False, serial)
550568
"""
551569
res = False
552-
conn = sqlite3.connect(sqlfile)
553-
c = conn.cursor()
554-
_create_table(c)
570+
startdb(sql_params)
555571
# get all possible serial/tokens for a user
556572
serials = []
557573
matching_serial = None
@@ -575,22 +591,22 @@ def check_offline_otp(user, otp, sqlfile, window=10, refill=True):
575591
if res:
576592
c.execute("DELETE from authitems WHERE counter <= ? and serial = ?",
577593
(matching_counter, matching_serial))
578-
conn.commit()
579-
conn.close()
594+
595+
closedb()
580596
return res, matching_serial
581597

582598

583-
def save_auth_item(sqlfile, user, serial, tokentype, authitem):
599+
def save_auth_item(sql_params, user, serial, tokentype, authitem):
584600
"""
585-
Save the given authitem to the sqlite file to be used later for offline
601+
Save the given authitem to the sqldb file to be used later for offline
586602
authentication.
587603
588604
There is only one table in it with the columns:
589605
590606
username, counter, otp
591607
592-
:param sqlfile: An SQLite file. If it does not exist, it will be generated.
593-
:type sqlfile: basestring
608+
:param sql_params: MySQL/SQLite connection parameters
609+
:type sql_params: dict
594610
:param user: The PAM user
595611
:param serial: The serial number of the token
596612
:param tokentype: The type of the token
@@ -599,10 +615,7 @@ def save_auth_item(sqlfile, user, serial, tokentype, authitem):
599615
600616
:return:
601617
"""
602-
conn = sqlite3.connect(sqlfile)
603-
c = conn.cursor()
604-
# Create the table if necessary
605-
_create_table(c)
618+
startdb(sql_params)
606619

607620
syslog.syslog(syslog.LOG_DEBUG, "%s: offline save authitem: %s" % (
608621
__name__, authitem))
@@ -624,33 +637,25 @@ def save_auth_item(sqlfile, user, serial, tokentype, authitem):
624637
c.execute("INSERT INTO refilltokens (serial, refilltoken) VALUES (?,?)",
625638
(serial, refilltoken))
626639

627-
# Save (commit) the changes
628-
conn.commit()
640+
closedb()
629641

630-
# We can also close the connection if we are done with it.
631-
# Just be sure any changes have been committed or they will be lost.
632-
conn.close()
633-
634-
def check_last_history(sqlfile, user, rhost, grace_time, window=10):
642+
def check_last_history(sql_params, user, rhost, grace_time, window=10):
635643
"""
636644
Get the last event for this user.
637645
638646
If success reset the error counter.
639647
If error increment the error counter.
640648
641-
:param sqlfile: An SQLite file. If it does not exist, it will be generated.
642-
:type sqlfile: basestring
649+
:param sql_params: MySQL/SQLite connection parameters
650+
:type sql_params: dict
643651
:param user: The PAM user
644652
:param rhost: The PAM user rhost value
645653
:param serial: The serial number of the token
646654
:param success: Boolean
647655
648656
:return:
649657
"""
650-
conn = sqlite3.connect(sqlfile, detect_types=sqlite3.PARSE_DECLTYPES)
651-
c = conn.cursor()
652-
# Create the table if necessary
653-
_create_table(c)
658+
startdb(sql_params)
654659

655660
res = False
656661
events = []
@@ -683,31 +688,27 @@ def check_last_history(sqlfile, user, rhost, grace_time, window=10):
683688
syslog.syslog(syslog.LOG_DEBUG, "%s: No history for: %s" % (
684689
__name__, user))
685690

686-
687-
conn.close()
691+
closedb()
688692
return res
689693

690694

691-
def save_history_item(sqlfile, user, rhost, serial, success):
695+
def save_history_item(sql_params, user, rhost, serial, success):
692696
"""
693697
Save the given success/error event.
694698
695699
If success reset the error counter.
696700
If error increment the error counter.
697701
698-
:param sqlfile: An SQLite file. If it does not exist, it will be generated.
699-
:type sqlfile: basestring
702+
:param sql_params: MySQL/SQLite connection parameters
703+
:type sql_params: dict
700704
:param user: The PAM user
701705
:param rhost: The PAM user rhost value
702706
:param serial: The serial number of the token
703707
:param success: Boolean
704708
705709
:return:
706710
"""
707-
conn = sqlite3.connect(sqlfile, detect_types=sqlite3.PARSE_DECLTYPES)
708-
c = conn.cursor()
709-
# Create the table if necessary
710-
_create_table(c)
711+
startdb(sql_params)
711712

712713
syslog.syslog(syslog.LOG_DEBUG, "%s: offline save event: %s" % (
713714
__name__, ("success" if success else "error")))
@@ -729,39 +730,44 @@ def save_history_item(sqlfile, user, rhost, serial, success):
729730
"error_counter, last_error) VALUES (?,?,?,?,?)",
730731
(user, rhost, serial, 1, datetime.datetime.now()))
731732

733+
closedb()
732734

733-
# Save (commit) the changes
734-
conn.commit()
735735

736-
# We can also close the connection if we are done with it.
737-
# Just be sure any changes have been committed or they will be lost.
738-
conn.close()
736+
# Start connection and create cursor
737+
def startdb(sql_params):
738+
global conn, c
739+
# Create connection
740+
if sql_params["lite"]:
741+
conn = sqlite3.connect(sql_params["file"], detect_types=sqlite3.PARSE_DECLTYPES)
742+
# Create a cursor object
743+
c = conn.cursor()
744+
else:
745+
print("Mysql")
746+
# mysql.connector.connect(**connection_config_dict)
747+
748+
# Create table if does not exist
749+
_create_table()
739750

751+
# Commit and close db
752+
def closedb():
753+
# Commit changes
754+
conn.commit()
755+
# Close connections
756+
conn.close()
740757

741-
def _create_table(c):
758+
def _create_table():
742759
"""
743760
Create table if necessary
744761
:param c: The connection cursor
745762
"""
746-
try:
747-
c.execute("CREATE TABLE IF NOT EXISTS authitems "
748-
"(counter int, user text, serial text, tokenowner text,"
749-
"otp text, tokentype text)")
750-
except sqlite3.OperationalError:
751-
pass
752-
753-
try:
754-
# create refilltokens table
755-
c.execute("CREATE TABLE IF NOT EXISTS refilltokens (serial text, refilltoken text)")
756-
except sqlite3.OperationalError:
757-
pass
758-
759-
try:
760-
# create history table
761-
c.execute("CREATE TABLE IF NOT EXISTS history "
762-
"(user text, rhost text, serial text, error_counter int, "
763-
"last_success timestamp, last_error timestamp)")
764-
c.execute("CREATE UNIQUE INDEX idx_user "
765-
"ON history (user, rhost);")
766-
except sqlite3.OperationalError:
767-
pass
763+
c.execute("CREATE TABLE IF NOT EXISTS authitems "
764+
"(counter int, user text, serial text, tokenowner text,"
765+
"otp text, tokentype text)")
766+
# create refilltokens table
767+
c.execute("CREATE TABLE IF NOT EXISTS refilltokens (serial text, refilltoken text)")
768+
# create history table
769+
c.execute("CREATE TABLE IF NOT EXISTS history "
770+
"(user text, rhost text, serial text, error_counter int, "
771+
"last_success timestamp, last_error timestamp)")
772+
c.execute("CREATE UNIQUE INDEX idx_user "
773+
"ON history (user, rhost);")

setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
from setuptools import setup
44

5-
#VERSION="2.1dev4"
6-
VERSION = "2.11dev0"
5+
VERSION = "2.12dev0"
76

87
install_requires = [
98
'requests>=2.23',

0 commit comments

Comments
 (0)