# 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/>.
import logging
from sqlalchemy import Sequence, Unicode, Integer, ForeignKey, UniqueConstraint, delete, \
UnicodeText
from sqlalchemy.orm import Mapped, mapped_column, relationship
from privacyidea.models import db
from privacyidea.models.utils import MethodsMixin
from privacyidea.lib.utils import convert_column_to_unicode
log = logging.getLogger(__name__)
[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: Mapped[int] = mapped_column(Integer, Sequence("smsgateway_seq"), primary_key=True)
identifier: Mapped[str] = mapped_column(Unicode(255), nullable=False, unique=True)
description: Mapped[str | None] = mapped_column(Unicode(1024), default="")
providermodule: Mapped[str] = mapped_column(Unicode(1024), nullable=False)
options: Mapped[list["SMSGatewayOption"]] = relationship(
'SMSGatewayOption',
lazy='dynamic',
back_populates='smsgw'
)
def __init__(self, identifier, providermodule, description=None):
self.identifier = identifier
self.providermodule = providermodule
self.description = description
[docs]
def delete(self):
"""
When deleting an SMS Gateway we also delete all the options.
"""
ret = self.id
delete_stmt = delete(SMSGatewayOption).where(SMSGatewayOption.gateway_id == ret)
db.session.execute(delete_stmt)
# delete the SMSGateway itself
db.session.delete(self)
db.session.commit()
return ret
@property
def option_dict(self):
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):
res = {}
for option in self.options:
if option.Type == "header":
res[option.Key] = option.Value
return res
def as_dict(self):
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: Mapped[int] = mapped_column(Integer, Sequence("smsgwoption_seq"), primary_key=True)
Key: Mapped[str] = mapped_column(Unicode(255), nullable=False)
Value: Mapped[str | None] = mapped_column(UnicodeText(), default='')
Type: Mapped[str | None] = mapped_column(Unicode(100), default='option')
gateway_id: Mapped[int | None] = mapped_column(Integer, ForeignKey('smsgateway.id'), index=True)
smsgw = relationship("SMSGateway", back_populates="options")
__table_args__ = (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()