示例#1
0
    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
示例#2
0
    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)
示例#3
0
    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 []
示例#4
0
    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))

            response.set_cookie(app.session_cookie_name,
                                session_id,
                                expires=expires)
示例#5
0
 def _gen_ids():
     while True:
         with db.transaction() as t:
             top_id = first(
                 first(
                     t.query(
                         sql_query({
                             "select": "next_id",
                             "from": VERSION_TABLE
                         })).data))
             max_id = top_id + 1000
             t.execute(SQL_UPDATE + quote_column(VERSION_TABLE) + SQL_SET +
                       sql_eq(next_id=max_id))
         while top_id < max_id:
             yield top_id
             top_id += 1
示例#6
0
    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)))
示例#7
0
    def device_status(self, path=None):
        """
        AUTOMATION CAN CALL THIS ENDPOINT TO FIND OUT THE LOGIN STATUS
        RESPOND WITH {"ok":true} WHEN USER HAS LOGGED IN, AND user IS
        ASSOCIATED WITH SESSION
        """
        now = Date.now().unix
        if not session.session_id:
            return Response('{"try_again":false, "status":"no session id"}',
                            status=401)
        request_body = request.get_data().strip()
        signed = json2value(request_body.decode("utf8"))
        command = rsa_crypto.verify(signed, session.public_key)

        time_sent = parse(command.timestamp)
        if not (now - LEEWAY <= time_sent < now + LEEWAY):
            return Response(
                '{"try_again":false, "status":"timestamp is not recent"}',
                status=401)
        if session.expires < now:
            return Response(
                '{"try_again":false, "status":"session is too old"}',
                status=401)
        if session.user:
            session.public_key = None
            return Response('{"try_again":false, "status":"verified"}',
                            status=200)

        state_info = self.device.db.query(
            sql_query({
                "select": "session_id",
                "from": self.device.table,
                "where": {
                    "eq": {
                        "state": session.state
                    }
                },
            }))
        if not state_info.data:
            return Response(
                '{"try_again":false, "status":"State has been lost"}',
                status=401)

        return Response('{"try_again":true, "status":"still waiting"}',
                        status=200)
    def _load_from_database(self):
        # FIND ALL TABLES
        result = self.db.query(sql_query({
            "from": "sqlite_master",
            "where": {"eq": {"type": "table"}},
            "orderby": "name"
        }))
        tables = wrap([{k: d for k, d in zip(result.header, row)} for row in result.data])
        last_nested_path = ["."]
        for table in tables:
            if table.name.startswith("__"):
                continue
            base_table, nested_path = tail_field(table.name)

            # FIND COMMON NESTED PATH SUFFIX
            if nested_path == ".":
                last_nested_path = []
            else:
                for i, p in enumerate(last_nested_path):
                    if startswith_field(nested_path, p):
                        last_nested_path = last_nested_path[i:]
                        break
                else:
                    last_nested_path = []

            full_nested_path = [nested_path] + last_nested_path
            self._snowflakes[literal_field(base_table)] += [full_nested_path]

            # LOAD THE COLUMNS
            details = self.db.about(table.name)

            for cid, name, dtype, notnull, dfft_value, pk in details:
                if name.startswith("__"):
                    continue
                cname, ctype = untyped_column(name)
                self.add(Column(
                    name=cname,
                    jx_type=coalesce(sql_type_to_json_type.get(ctype), IS_NULL),
                    nested_path=full_nested_path,
                    es_type=dtype,
                    es_column=name,
                    es_index=table.name,
                    last_updated=Date.now()
                ))
            last_nested_path = full_nested_path
示例#9
0
    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
示例#10
0
    def device_callback(self, path=None):
        # HANDLE BROWESR RETURN FROM AUTH0 LOGIN
        error = request.args.get("error")
        if error:
            Log.error("You did it wrong")

        code = request.args.get("code")
        state = request.args.get("state")
        referer = request.headers.get("Referer")
        result = self.device.db.query(
            sql_query({
                "from": "device",
                "select": "session_id",
                "where": {
                    "eq": {
                        "state": state
                    }
                },
            }))
        if not result.data:
            Log.error("expecting valid state")
        device_session_id = result.data[0][0]

        # GO BACK TO AUTH0 TO GET TOKENS
        token_request = {
            "client_id": self.device.auth0.client_id,
            "redirect_uri": self.device.auth0.redirect_uri,
            "code_verifier": session.code_verifier,
            "code": code,
            "grant_type": "authorization_code",
        }
        DEBUG and Log.note("Send token request to Auth0:\n {{request}}",
                           request=token_request)
        auth_response = requests.request(
            "POST",
            str(URL("https://" + self.device.auth0.domain,
                    path="oauth/token")),
            headers={
                "Accept": "application/json",
                "Content-Type": "application/json",
                # "Referer": str(URL(self.device.auth0.redirect_uri, query={"code": code, "state": state})),
            },
            data=value2json(token_request),
        )

        try:
            auth_result = wrap(auth_response.json())
        except Exception as e:
            Log.error("not json {{value}}",
                      value=auth_response.content,
                      cause=e)

        # VERIFY TOKENS, ADD USER TO DEVICE'S SESSION
        user_details = self.verify_opaque_token(auth_result.access_token)
        self.session_manager.update_session(
            device_session_id,
            {"user": self.permissions.get_or_create_user(user_details)},
        )

        # REMOVE DEVICE SETUP STATE
        with self.device.db.transaction() as t:
            t.execute(SQL_DELETE + SQL_FROM + quote_column(self.device.table) +
                      SQL_WHERE + sql_eq(state=state))
        Log.note("login complete")
        return Response("Login complete. You may close this page", status=200)