Exemplo n.º 1
0
class SqliteSessionInterface(FlaskSessionInterface):
    """STORE SESSION DATA IN SQLITE

    :param db: Sqlite database
    :param table: The table name you want to use.
    :param use_signer: Whether to sign the session id cookie or not.
    """
    @override
    def __init__(self, flask_app, db, cookie, table="sessions"):
        global SINGLTON
        if SINGLTON:
            Log.error("Can only handle one session manager at a time")
        SINGLTON = self
        if is_data(db):
            self.db = Sqlite(db)
        else:
            self.db = db
        self.table = table
        self.cookie = cookie
        self.cookie.max_lifetime = parse(self.cookie.max_lifetime)
        self.cookie.inactive_lifetime = parse(self.cookie.inactive_lifetime)

        if not self.db.about(self.table):
            self.setup()
        Thread.run("session monitor", self.monitor)

    def create_session(self, session):
        session.session_id = generate_sid()
        session.permanent = True
        session.expires = (Date.now() + self.cookie.max_lifetime).unix

    def monitor(self, please_stop):
        while not please_stop:
            # Delete expired session
            try:
                with self.db.transaction() as t:
                    t.execute("DELETE FROM " + quote_column(self.table) +
                              SQL_WHERE + sql_lt(expires=Date.now().unix))
            except Exception as e:
                Log.warning("problem with session expires", cause=e)
            (please_stop | Till(seconds=60)).wait()

    def setup(self):
        with self.db.transaction() as t:
            t.execute(
                sql_create(
                    self.table,
                    {
                        "session_id": "TEXT PRIMARY KEY",
                        "data": "TEXT",
                        "last_used": "NUMBER",
                        "expires": "NUMBER",
                    },
                ))

    def cookie_data(self, session):
        return {
            "session_id": session.session_id,
            "expires": session.expires,
            "inactive_lifetime": self.cookie.inactive_lifetime.seconds,
        }

    def update_session(self, session_id, props):
        """
        UPDATE GIVEN SESSION WITH PROPERTIES
        :param session_id:
        :param props:
        :return:
        """
        now = Date.now().unix
        session = self.get_session(session_id)
        for k, v in props.items():
            session[k] = v
        session.last_used = now

        record = {
            "session_id": session_id,
            "data": value2json(session),
            "expires": session.expires,
            "last_used": session.last_used,
        }

        with self.db.transaction() as t:
            t.execute(SQL_UPDATE + quote_column(self.table) + SQL_SET +
                      sql_list(sql_eq(**{k: v}) for k, v in record.items()) +
                      SQL_WHERE + sql_eq(session_id=session_id))

    def get_session(self, session_id):
        now = Date.now().unix
        result = self.db.query(
            sql_query({
                "from": self.table,
                "where": {
                    "eq": {
                        "session_id": session_id
                    }
                }
            }))
        saved_record = first(Data(zip(result.header, r)) for r in result.data)
        if not saved_record or saved_record.expires <= now:
            return Data()
        session = json2value(saved_record.data)

        DEBUG and Log.note("record from db {{session}}", session=saved_record)
        return session

    @register_thread
    def open_session(self, app, request):
        session_id = request.headers.get("Authorization")
        DEBUG and Log.note("got session_id {{session|quote}}",
                           session=session_id)
        if not session_id:
            return Data()
        return self.get_session(session_id)

    @register_thread
    def save_session(self, app, session, response):
        if not session or not session.keys():
            return
        if not session.session_id:
            session.session_id = generate_sid()
            session.permanent = True
        DEBUG and Log.note("save session {{session}}", session=session)

        now = Date.now().unix
        session_id = session.session_id
        result = self.db.query(
            sql_query({
                "from": self.table,
                "where": {
                    "eq": {
                        "session_id": session_id
                    }
                }
            }))
        saved_record = first(Data(zip(result.header, r)) for r in result.data)
        expires = min(session.expires,
                      now + self.cookie.inactive_lifetime.seconds)
        if saved_record:
            DEBUG and Log.note("found session {{session}}",
                               session=saved_record)

            saved_record.data = value2json(session)
            saved_record.expires = expires
            saved_record.last_used = now
            with self.db.transaction() as t:
                t.execute("UPDATE " + quote_column(self.table) + SQL_SET +
                          sql_list(
                              sql_eq(**{k: v})
                              for k, v in saved_record.items()) + SQL_WHERE +
                          sql_eq(session_id=session_id))
        else:
            new_record = {
                "session_id": session_id,
                "data": value2json(session),
                "expires": expires,
                "last_used": now,
            }
            DEBUG and Log.note("new record for db {{session}}",
                               session=new_record)
            with self.db.transaction() as t:
                t.execute(sql_insert(self.table, new_record))
Exemplo n.º 2
0
class Permissions:
    @override
    def __init__(self, db, kwargs):
        if is_data(db):
            self.db = Sqlite(db)
        elif isinstance(db, Sqlite):
            self.db = db
        else:
            Log.error("Bad db parameter")

        if not self.db.about(PERMISSION_TABLE):
            self.setup()
        self.next_id = id_generator(self.db)

    def setup(self):
        with self.db.transaction() as t:
            t.execute(sql_create(VERSION_TABLE, {"version": "TEXT"}))
            t.execute(sql_insert(VERSION_TABLE, {"version": "1.0"}))

            t.execute(
                sql_create(
                    GROUP_TABLE,
                    {
                        "_id": "LONG PRIMARY KEY",
                        "name": "TEXT",
                        "group": "TEXT",
                        "email": "TEXT",
                        "issuer": "TEXT",
                        "email_verified": "INTEGER",
                        "description": "TEXT",
                        "owner": "LONG",
                    },
                ))

            t.execute(
                sql_insert(
                    GROUP_TABLE,
                    [
                        {
                            "_id": 1,
                            "name": "root",
                            "email": "*****@*****.**",
                            "description": "access for security system",
                        },
                        {
                            "_id": 11,
                            "group": "public",
                            "description": "everyone with confirmed email",
                            "owner": 1,
                        },
                        {
                            "_id": 12,
                            "group": "mozillians",
                            "description":
                            "people that mozilla authentication has recongized as mozillian",
                            "owner": 1,
                        },
                        {
                            "_id": 13,
                            "group": "moz-employee",
                            "description":
                            "people that mozilla authentication has recongized as employee",
                            "owner": 1,
                        },
                    ],
                ))

            t.execute(
                sql_create(
                    RESOURCE_TABLE,
                    {
                        "_id": "LONG PRIMARY KEY",
                        "table": "TEXT",
                        "operation": "TEXT",
                        "owner": "LONG",
                    },
                ))
            t.execute(
                sql_insert(
                    RESOURCE_TABLE,
                    [
                        CREATE_TABLE,
                        {
                            "_id": 101,
                            "table": ".",
                            "operation": "update",
                            "owner": 1
                        },
                        {
                            "_id": 102,
                            "table": ".",
                            "operation": "from",
                            "owner": 1
                        },
                    ],
                ))

            t.execute(
                sql_create(
                    PERMISSION_TABLE,
                    {
                        "user": "******",
                        "resource": "LONG",
                        "owner": "LONG"
                    },
                ))
            t.execute(
                sql_insert(
                    PERMISSION_TABLE,
                    [
                        {
                            "user": 12,
                            "resource": 11,
                            "owner": 1
                        },
                        {
                            "user": 13,
                            "resource": 11,
                            "owner": 1
                        },
                        {
                            "user": 13,
                            "resource": 12,
                            "owner": 1
                        },
                        {
                            "user": 1,
                            "resource": 100,
                            "owner": 1
                        },
                        {
                            "user": 1,
                            "resource": 101,
                            "owner": 1
                        },
                        {
                            "user": 1,
                            "resource": 102,
                            "owner": 1
                        },
                    ],
                ))

    def create_table_resource(self, table_name, owner):
        """
        CREATE A TABLE, CREATE RESOURCES FOR OPERATIONS, ENSURE CREATOR HAS CONTROL OVER TABLE

        :param table_name:  Create resources for given table
        :param owner: assign this user as owner
        :return:
        """
        new_resources = wrap([{
            "table": table_name,
            "operation": op,
            "owner": 1
        } for op in TABLE_OPERATIONS])
        self._insert(RESOURCE_TABLE, new_resources)

        with self.db.transaction() as t:
            t.execute(
                sql_insert(PERMISSION_TABLE, [{
                    "user": owner._id,
                    "resource": r._id,
                    "owner": ROOT_USER._id
                } for r in new_resources]))

    def get_or_create_user(self, details):
        details = wrap(details)
        issuer = details.sub or details.issuer
        email = details.email
        email_verified = details.email_verified
        if not email:
            Log.error("Expecting id_token to have claims.email propert")

        result = self.db.query(
            sql_query({
                "select": ["_id", "email", "issuer"],
                "from": GROUP_TABLE,
                "where": {
                    "eq": {
                        "email": email,
                        "issuer": issuer
                    }
                },
            }))

        if result.data:
            user = Data(zip(result.header, first(result.data)))
            user.email_verified = email_verified
            return user

        new_user = wrap({
            "email": email,
            "issuer": issuer,
            "email_verified": email_verified,
            "owner": ROOT_USER._id
        })
        self._insert(GROUP_TABLE, new_user)
        return new_user

    def get_resource(self, table, operation):
        result = self.db.query(
            sql_query({
                "select": "_id",
                "from": RESOURCE_TABLE,
                "where": {
                    "eq": {
                        "table": table,
                        "operation": operation
                    }
                },
            }))
        if not result.data:
            Log.error("Expecting to find a resource")

        return Data(zip(result.header, first(result.data)))

    def add_permission(self, user, resource, owner):
        """
        :param user:
        :param resource:
        :param owner:
        :return:
        """
        user = wrap(user)
        resource = wrap(resource)
        owner = wrap(owner)

        # DOES owner HAVE ACCESS TO resource?
        if not self.verify_allowance(owner, resource):
            Log.error("not allowed to assign resource")

        # DOES THIS PERMISSION EXIST ALREADY
        allowance = self.verify_allowance(user, resource)
        if allowance:
            if any(r.owner == owner for r in allowance):
                Log.error("already allowed via {{allowance}}",
                          allowance=allowance)
            # ALREADY ALLOWED, BUT MULTIPLE PATHS MAY BE OK
        with self.db.transaction() as t:
            t.execute(
                sql_insert(PERMISSION_TABLE, {
                    "user": user._id,
                    "resource": resource._id,
                    "owner": owner._id
                }))

    def verify_allowance(self, user, resource):
        """
        VERIFY IF user CAN ACCESS resource
        :param user:
        :param resource:
        :return: ALLOWANCE CHAIN
        """
        user = wrap(user)
        resource = wrap(resource)
        resources = self.db.query(
            sql_query({
                "select": ["resource", "owner"],
                "from": PERMISSION_TABLE,
                "where": {
                    "eq": {
                        "user": user._id
                    }
                },
            }))

        for r in resources.data:
            record = Data(zip(resources.header, r))
            if record.resource == resource._id:
                if record.owner == ROOT_USER._id:
                    return FlatList(vals=[{
                        "resource": resource,
                        "user": user,
                        "owner": ROOT_USER
                    }])
                else:
                    cascade = self.verify_allowance(
                        wrap({"_id": record.owner}), resource)
                    if cascade:
                        cascade.append({
                            "resource": resource,
                            "user": user,
                            "owner": record.owner
                        })
                    return cascade
            else:
                group = record.resource
                cascade = self.verify_allowance(wrap({"_id": group}), resource)
                if cascade:
                    cascade.append({
                        "group": group,
                        "user": user,
                        "owner": record.owner
                    })
                    return cascade

        return []

    def find_resource(self, table, operation):
        result = self.db.query(
            sql_query({
                "from": RESOURCE_TABLE,
                "where": {
                    "eq": {
                        "table": table,
                        "operation": operation
                    }
                }
            }))

        return first(Data(zip(result.header, r)) for r in result.data)

    def _insert(self, table, records):
        records = listwrap(records)
        keys = {"_id"}
        for r in records:
            keys.update(r.keys())
            if r._id == None:
                r._id = self.next_id()

        with self.db.transaction() as t:
            t.execute(sql_insert(table, records))