Source code for privacyidea.models.smsgateway

# SPDX-FileCopyrightText: (C) 2025 NetKnights GmbH <https://netknights.it>
# SPDX-FileCopyrightText: (C) 2025 Paul Lettich <paul.lettich@netknights.it>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# This code is free software; you can redistribute it and/or
# modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
# as published by the Free Software Foundation; either
# version 3 of the License, or any later version.
#
# This code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU Affero General Public
# License along with this program.  If not, see <http://www.gnu.org/licenses/>.

from sqlalchemy import Sequence

from privacyidea.models import db
from privacyidea.models.utils import MethodsMixin
from privacyidea.lib.utils import convert_column_to_unicode


[docs] class SMSGateway(MethodsMixin, db.Model): """ This table stores the SMS Gateway definitions. See https://github.com/privacyidea/privacyidea/wiki/concept:-Delivery-Gateway It saves the * unique name * a description * the SMS provider module All options and parameters are saved in other tables. """ __tablename__ = 'smsgateway' id = db.Column(db.Integer, Sequence("smsgateway_seq"), primary_key=True) identifier = db.Column(db.Unicode(255), nullable=False, unique=True) description = db.Column(db.Unicode(1024), default="") providermodule = db.Column(db.Unicode(1024), nullable=False) options = db.relationship('SMSGatewayOption', lazy='dynamic', backref='smsgw') def __init__(self, identifier, providermodule, description=None, options=None, headers=None): options = options or {} headers = headers or {} sql = SMSGateway.query.filter_by(identifier=identifier).first() if sql: self.id = sql.id self.identifier = identifier self.providermodule = providermodule self.description = description self.save() # delete non-existing options in case of update opts = {"option": options, "header": headers} if sql: sql_opts = {"option": sql.option_dict, "header": sql.header_dict} for typ, vals in opts.items(): for key in sql_opts[typ].keys(): # iterate through all existing options/headers if key not in vals: # if the option is not contained anymore SMSGatewayOption.query.filter_by(gateway_id=self.id, Key=key, Type=typ).delete() # add the options and headers to the SMS Gateway for typ, vals in opts.items(): for k, v in vals.items(): SMSGatewayOption(gateway_id=self.id, Key=k, Value=v, Type=typ).save() def save(self): if self.id is None: # create a new one db.session.add(self) db.session.commit() else: # update SMSGateway.query.filter_by(id=self.id).update({ "identifier": self.identifier, "providermodule": self.providermodule, "description": self.description }) db.session.commit() return self.id
[docs] def delete(self): """ When deleting an SMS Gateway we also delete all the options. :return: """ ret = self.id # delete all SMSGatewayOptions db.session.query(SMSGatewayOption) \ .filter(SMSGatewayOption.gateway_id == ret) \ .delete() # delete the SMSGateway itself db.session.delete(self) db.session.commit() return ret
@property def option_dict(self): """ Return all connected options as a dictionary :return: dict """ res = {} for option in self.options: if option.Type == "option" or not option.Type: res[option.Key] = option.Value return res @property def header_dict(self): """ Return all connected headers as a dictionary :return: dict """ res = {} for option in self.options: if option.Type == "header": res[option.Key] = option.Value return res
[docs] def as_dict(self): """ Return the object as a dictionary :return: complete dict :rytpe: dict """ d = {"id": self.id, "name": self.identifier, "providermodule": self.providermodule, "description": self.description, "options": self.option_dict, "headers": self.header_dict} return d
[docs] class SMSGatewayOption(MethodsMixin, db.Model): """ This table stores the options, parameters and headers for an SMS Gateway definition. """ __tablename__ = 'smsgatewayoption' id = db.Column(db.Integer, Sequence("smsgwoption_seq"), primary_key=True) Key = db.Column(db.Unicode(255), nullable=False) Value = db.Column(db.UnicodeText(), default='') Type = db.Column(db.Unicode(100), default='option') gateway_id = db.Column(db.Integer(), db.ForeignKey('smsgateway.id'), index=True) __table_args__ = (db.UniqueConstraint('gateway_id', 'Key', 'Type', name='sgix_1'),) def __init__(self, gateway_id, Key, Value, Type=None): """ Create a new gateway_option for the gateway_id """ self.gateway_id = gateway_id self.Key = Key self.Value = convert_column_to_unicode(Value) self.Type = Type self.save() def save(self): # See, if there is this option or header for this gateway # The first match takes precedence go = SMSGatewayOption.query.filter_by(gateway_id=self.gateway_id, Key=self.Key, Type=self.Type).first() if go is None: # create a new one db.session.add(self) db.session.commit() ret = self.id else: # update SMSGatewayOption.query.filter_by(gateway_id=self.gateway_id, Key=self.Key, Type=self.Type ).update({'Value': self.Value, 'Type': self.Type}) ret = go.id db.session.commit() return ret