# components to store tickets in a database # we use sqlite3 import time from sqlalchemy import ( create_engine, Column, Integer, Float, String, Boolean, MetaData) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.pool import StaticPool Base = declarative_base() class ServiceTicket(Base): __tablename__ = 'service_tickets' id = Column(Integer, primary_key=True) ticket = Column(String(255), nullable=False) ts = Column(Float, nullable=False) service = Column(String(2048), nullable=True) user = Column(String(512), nullable=True) sso = Column(Boolean, nullable=False, default=True) def __init__(self, ticket, user, service=None, sso=True, timestamp=None): if timestamp is None: timestamp = time.time() self.ticket = ticket self.ts = timestamp self.service = service self.user = user self.sso = sso def __repr__(self): return ( "ServiceTicket(ticket='%s', user='%s', service='%s', " "sso=%s, timestamp=%s)" % ( self.ticket, self.user, self.service, self.sso, self.ts)) class LoginTicket(Base): __tablename__ = 'login_tickets' ticket = Column(String(255), primary_key=True) ts = Column(Float, nullable=False) def __init__(self, ticket, timestamp=None): if timestamp is None: timestamp = time.time() self.ticket = ticket self.ts = timestamp def __repr__(self): return "LoginTicket('%s', %s)" % (self.ticket, self.ts) class TicketGrantingCookie(Base): __tablename__ = 'ticket_granting_cookies' value = Column(String(255), primary_key=True) ts = Column(Float, nullable=False) def __init__(self, value, timestamp=None): if timestamp is None: timestamp = time.time() self.value = value self.ts = timestamp def __repr__(self): return "TicketGrantingCookie('%s', %s)" % (self.value, self.ts) Session = scoped_session(sessionmaker()) class DB(object): """Abstract database to make data persistent. Creates a database identified by `connection_string` if it does not exist and initializes sessions. """ @property def session(self): return Session def __init__(self, connection_string): args = {} if connection_string in ('sqlite:///', 'sqlite:///:memory:'): # make sure all threads access the same memory args = dict(connect_args={'check_same_thread': False}, poolclass=StaticPool) self.engine = create_engine(connection_string, **args) self.metadata = MetaData() Base.metadata.create_all(self.engine) Session.configure(bind=self.engine) def add(self, item): """Insert `item` into database. `item` must be an instance of the database content types defined in this module, normally some `Ticket` instance. """ Session.add(item) Session.commit() def delete(self, item): """Delete `item` from database. `item must be an instance of the database content types defined in this module, normally some `Ticket` instance. """ Session.delete(item) Session.commit() def query(self, *args, **kw): return Session.query(*args, **kw) class DBSessionContext(object): """A context manager providing database sessions. Creates a new (threadlocal) database session when entering and tears down the session after leaving the context. Tearing down includes committing transactions and closing the connection. Meant to be used as a wrapper for web request handlers, such, that a session is created when a request comes in and released when the response is ready to be delivered. This context manager does *not* catch any exceptions. """ def __enter__(self): return Session() def __exit__(self, *args, **kw): if args or kw: Session.rollback() Session.remove() return False