У меня есть веб-приложение на основе Pylons, которое через Sqlalchemy (v0.5) подключается к базе данных Postgres. В целях безопасности вместо того, чтобы следовать типичному шаблону простых веб-приложений (как видно практически во всех руководствах), я не использую общего пользователя Postgres (например, «webapp»), но требую, чтобы пользователи вводили свои собственные ИД пользователя и пароль Postgres , и я использую это, чтобы установить соединение. Это означает, что мы получаем все преимущества безопасности Postgres.
Еще сложнее, есть две отдельные базы данных для подключения. Хотя они в настоящее время находятся в одном кластере Postgres, они должны иметь возможность перемещаться на отдельные хосты позднее.
Мы используем декларативный пакет sqlalchemy , хотя я не вижу, что это имеет какое-то отношение к делу.
Большинство примеров sqlalchemy демонстрируют тривиальные подходы, такие как однократная настройка метаданных при запуске приложения с использованием общего имени пользователя и пароля базы данных, которые используются через веб-приложение. Обычно это делается с помощью Metadata.bind = create_engine (), иногда даже на уровне модуля в файлах модели базы данных.
У меня вопрос: как мы можем отложить установление соединений до тех пор, пока пользователь не войдет в систему, а затем (конечно) повторно использовать эти соединения или повторно установить их с использованием тех же учетных данных для каждого последующего запроса.
У нас это работает - мы думаем - но я не только не уверен в его безопасности, я также думаю, что это выглядит невероятно тяжелым для ситуации.
Внутри __call__
метода BaseController мы получаем идентификатор пользователя и пароль из веб-сессии, вызываем sqlalchemy create_engine () один раз для каждой базы данных, затем вызываем подпрограмму, которая несколько раз вызывает Session.bind_mapper (), один раз для каждой таблицы что на можно ссылаться на каждое из этих соединений, даже если любой данный запрос обычно ссылается только на одну или две таблицы. Это выглядит примерно так:
# in lib/base.py on the BaseController class
def __call__(self, environ, start_response):
# note: web session contains {'username': XXX, 'password': YYY}
url1 = 'postgres://%(username)s:%(password)s@server1/finance' % session
url2 = 'postgres://%(username)s:%(password)s@server2/staff' % session
finance = create_engine(url1)
staff = create_engine(url2)
db_configure(staff, finance) # see below
... etc
# in another file
Session = scoped_session(sessionmaker())
def db_configure(staff, finance):
s = Session()
from db.finance import Employee, Customer, Invoice
for c in [
Employee,
Customer,
Invoice,
]:
s.bind_mapper(c, finance)
from db.staff import Project, Hour
for c in [
Project,
Hour,
]:
s.bind_mapper(c, staff)
s.close() # prevents leaking connections between sessions?
Так что вызовы create_engine () происходят при каждом запросе ... Я вижу, что это необходимо, и пул соединений, вероятно, кеширует их и делает все разумно.
Но вызов Session.bind_mapper () один раз для каждой таблицы, для каждого запроса? Кажется, должен быть лучший путь.
Очевидно, поскольку стремление к надежной безопасности лежит в основе всего этого, мы не хотим шансов, что соединение, установленное для пользователя с высоким уровнем безопасности, будет случайно использовано в более позднем запросе пользователем с низким уровнем защиты.