jedcunningham commented on a change in pull request #21167:
URL: https://github.com/apache/airflow/pull/21167#discussion_r797156794



##########
File path: airflow/_vendor/flask_session/sessions.py
##########
@@ -0,0 +1,146 @@
+# ~~~~~~~~~~~~~~~~~~~~~~
+#
+# Server-side Sessions and SessionInterfaces.
+#
+# Originally copied from flask_session.sessions
+# Flask-Session
+# Copyright (C) 2014 by Shipeng Feng and other contributors
+# BSD, see LICENSE for more details.
+
+from datetime import datetime
+from uuid import uuid4
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle

Review comment:
       ```suggestion
   import pickle
   ```
   
   Let's keep it simple.

##########
File path: docs/apache-airflow/migrations-ref.rst
##########
@@ -23,7 +23,9 @@ Here's the list of all the Database Migrations that are 
executed via when you ru
 
+--------------------------------+------------------+-----------------+---------------------------------------------------------------------------------------+
 | Revision ID                    | Revises ID       | Airflow Version | 
Description                                                                     
      |
 
+--------------------------------+------------------+-----------------+---------------------------------------------------------------------------------------+
-| ``e655c0453f75`` (head)        | ``587bdf053233`` | ``2.3.0``       | Add 
``map_index`` column to TaskInstance to identify task-mapping, and a 
``task_map`` |
+| ``c381b21cb7e4`` (head)        | ``e655c0453f75`` | ``2.3.0``       | Create 
a ``sessions`` table to store web session data                                 |

Review comment:
       This will need to be moved into the right spot for 2.2.4 (e.g. a 
different "revises id"), but I'm most likely going to pull another revision 
back to 2.2.4 tomorrow so hold off for now. At least this comment will remind 
us we need to do it.

##########
File path: airflow/_vendor/flask_session/db_models.py
##########
@@ -0,0 +1,35 @@
+# ~~~~~~~~~~~~~~~~~~~~~~
+#
+# Server-side Sessions and SessionInterfaces.
+#
+# Originally copied from flask_session.sessions
+# Flask-Session
+# Copyright (C) 2014 by Shipeng Feng and other contributors
+# BSD, see LICENSE for more details.
+
+from sqlalchemy import (
+    Column,
+    DateTime,
+    Integer,
+    LargeBinary,
+    String,
+)
+from airflow.models.base import Base
+from airflow.utils.log.logging_mixin import LoggingMixin
+
+
+class Session(Base, LoggingMixin):
+    __tablename__ = 'sessions'
+
+    id = Column(Integer, primary_key=True)
+    session_id = Column(String(255), unique=True)
+    data = Column(LargeBinary)

Review comment:
       If we can do json, then we probably need to adjust this column type?

##########
File path: airflow/www/extensions/init_session.py
##########
@@ -15,33 +15,30 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from flask import request, session as flask_session
-from flask.sessions import SecureCookieSessionInterface
+from flask import session as builtin_flask_session
 
+from airflow.configuration import conf
+from airflow.www.session import AirflowDatabaseSessionInterface, 
AirflowSecureCookieSessionInterface
 
-class AirflowSessionInterface(SecureCookieSessionInterface):
-    """
-    Airflow cookie session interface.
-    Modifications of sessions should be done here because
-    the change here is global.
-    """
 
-    def save_session(self, *args, **kwargs):
-        """Prevent creating session from REST API requests."""
-        if request.blueprint == '/api/v1':
-            return None
-        return super().save_session(*args, **kwargs)
-
-
-def init_permanent_session(app):
-    """Make session permanent to allows us to store data"""
-
-    def make_session_permanent():
-        flask_session.permanent = True
-
-    app.before_request(make_session_permanent)
-
-
-def init_airflow_session_interface(app):
+def init_airflow_session_interface(app, db):
     """Set airflow session interface"""
-    app.session_interface = AirflowSessionInterface()
+    config = app.config.copy()
+    selected_backend = conf.get('webserver', 'SESSION_BACKEND', 
fallback='database')

Review comment:
       We don't need a fallback - defaults are loaded automatically.
   
   (You'd use fallbacks if it's possible for the config to not be in the 
default, e.g. if a provider could be installed on an earlier version of core 
airflow)
   
   ```suggestion
       selected_backend = conf.get('webserver', 'SESSION_BACKEND')
   ```

##########
File path: airflow/www/extensions/init_session.py
##########
@@ -15,33 +15,30 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from flask import request, session as flask_session
-from flask.sessions import SecureCookieSessionInterface
+from flask import session as builtin_flask_session
 
+from airflow.configuration import conf
+from airflow.www.session import AirflowDatabaseSessionInterface, 
AirflowSecureCookieSessionInterface
 
-class AirflowSessionInterface(SecureCookieSessionInterface):
-    """
-    Airflow cookie session interface.
-    Modifications of sessions should be done here because
-    the change here is global.
-    """
 
-    def save_session(self, *args, **kwargs):
-        """Prevent creating session from REST API requests."""
-        if request.blueprint == '/api/v1':
-            return None
-        return super().save_session(*args, **kwargs)
-
-
-def init_permanent_session(app):
-    """Make session permanent to allows us to store data"""
-
-    def make_session_permanent():
-        flask_session.permanent = True
-
-    app.before_request(make_session_permanent)
-
-
-def init_airflow_session_interface(app):
+def init_airflow_session_interface(app, db):
     """Set airflow session interface"""
-    app.session_interface = AirflowSessionInterface()
+    config = app.config.copy()
+    selected_backend = conf.get('webserver', 'SESSION_BACKEND', 
fallback='database')
+
+    if selected_backend == 'securecookie':
+        app.session_interface = AirflowSecureCookieSessionInterface()
+        if config.get('SESSION_PERMANENT', True):
+
+            def make_session_permanent():
+                builtin_flask_session.permanent = True
+
+            app.before_request(make_session_permanent)
+    elif selected_backend == 'database' or selected_backend is None:
+        app.session_interface = AirflowDatabaseSessionInterface(
+            db,
+            config.get('SESSION_SQLALCHEMY_TABLE', 'sessions'),
+            config.get('SESSION_KEY_PREFIX', 'session:'),
+            config.get('SESSION_USE_SIGNER', True),
+            config.get('SESSION_PERMANENT', True),
+        )

Review comment:
       We should probably raise if we get something else?

##########
File path: airflow/config_templates/config.yml
##########
@@ -1016,6 +1016,13 @@
       type: string
       example: ~
       default: ""
+    - name: web_server_session_backend
+      description: |
+        The type of backend used to store web session data, can be 'database' 
or 'securecookie'
+      version_added: ~

Review comment:
       ```suggestion
         version_added: 2.2.4
   ```

##########
File path: airflow/www/extensions/init_session.py
##########
@@ -15,33 +15,30 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from flask import request, session as flask_session
-from flask.sessions import SecureCookieSessionInterface
+from flask import session as builtin_flask_session
 
+from airflow.configuration import conf
+from airflow.www.session import AirflowDatabaseSessionInterface, 
AirflowSecureCookieSessionInterface
 
-class AirflowSessionInterface(SecureCookieSessionInterface):
-    """
-    Airflow cookie session interface.
-    Modifications of sessions should be done here because
-    the change here is global.
-    """
 
-    def save_session(self, *args, **kwargs):
-        """Prevent creating session from REST API requests."""
-        if request.blueprint == '/api/v1':
-            return None
-        return super().save_session(*args, **kwargs)
-
-
-def init_permanent_session(app):
-    """Make session permanent to allows us to store data"""
-
-    def make_session_permanent():
-        flask_session.permanent = True
-
-    app.before_request(make_session_permanent)
-
-
-def init_airflow_session_interface(app):
+def init_airflow_session_interface(app, db):
     """Set airflow session interface"""
-    app.session_interface = AirflowSessionInterface()
+    config = app.config.copy()
+    selected_backend = conf.get('webserver', 'SESSION_BACKEND', 
fallback='database')
+
+    if selected_backend == 'securecookie':
+        app.session_interface = AirflowSecureCookieSessionInterface()
+        if config.get('SESSION_PERMANENT', True):
+
+            def make_session_permanent():
+                builtin_flask_session.permanent = True
+
+            app.before_request(make_session_permanent)
+    elif selected_backend == 'database' or selected_backend is None:

Review comment:
       ```suggestion
       elif selected_backend == 'database':
   ```

##########
File path: airflow/_vendor/flask_session/sessions.py
##########
@@ -0,0 +1,146 @@
+# ~~~~~~~~~~~~~~~~~~~~~~
+#
+# Server-side Sessions and SessionInterfaces.
+#
+# Originally copied from flask_session.sessions
+# Flask-Session
+# Copyright (C) 2014 by Shipeng Feng and other contributors
+# BSD, see LICENSE for more details.
+
+from datetime import datetime
+from uuid import uuid4
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+from flask.sessions import SessionInterface as FlaskSessionInterface
+from flask.sessions import SessionMixin as FlaskSessionMixin
+from werkzeug.datastructures import CallbackDict
+from itsdangerous import Signer, BadSignature, want_bytes
+
+from .db_models import Session as SessionModel
+
+
+class SQLAlchemySession(CallbackDict, FlaskSessionMixin):
+    """Baseclass for server-side based sessions."""
+
+    def __init__(self, initial=None, sid=None, permanent=None):
+        def on_update(self):
+            self.modified = True
+        super().__init__(initial, on_update)
+        self.sid = sid
+        if permanent:
+            self.permanent = permanent
+        self.modified = False
+
+
+class SessionInterface(FlaskSessionInterface):
+    def _generate_sid(self):
+        return str(uuid4())
+
+    def _get_signer(self, app):
+        if not app.secret_key:
+            return None
+        # The salt is combined with app.secret_key, and is used to "distinguish
+        # signatures in different contexts"
+        return Signer(app.secret_key, salt='airflow',
+                      key_derivation='hmac')
+
+
+class SQLAlchemySessionInterface(SessionInterface):
+    """Uses the Flask-SQLAlchemy from a flask app as a session backend.
+
+    .. versionadded:: 0.2
+
+    :param db: A Flask-SQLAlchemy instance.
+    :param table: The table name you want to use.
+    :param key_prefix: A prefix that is added to all store keys.
+    :param use_signer: Whether to sign the session id cookie or not.
+    :param permanent: Whether to use permanent session or not.
+    """
+
+    serializer = pickle

Review comment:
       In fact, can we serialize with json instead?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to