From 67667828c2d12f1349fc2976d0f1e31bed11e88c Mon Sep 17 00:00:00 2001 From: burnettk Date: Sat, 4 Feb 2023 00:03:32 -0500 Subject: [PATCH] if there are tenant specific fields in the config, transfer them from openid token to db --- .../migrations/versions/ca9b79dde5cc_.py | 32 +++++++++++++ .../spiffworkflow_backend/config/__init__.py | 14 ++++++ .../spiffworkflow_backend/config/default.py | 7 ++- .../src/spiffworkflow_backend/models/user.py | 3 ++ .../services/authorization_service.py | 46 +++++++++++-------- 5 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 spiffworkflow-backend/migrations/versions/ca9b79dde5cc_.py diff --git a/spiffworkflow-backend/migrations/versions/ca9b79dde5cc_.py b/spiffworkflow-backend/migrations/versions/ca9b79dde5cc_.py new file mode 100644 index 000000000..8a7134f41 --- /dev/null +++ b/spiffworkflow-backend/migrations/versions/ca9b79dde5cc_.py @@ -0,0 +1,32 @@ +"""empty message + +Revision ID: ca9b79dde5cc +Revises: 2ec4222f0012 +Create Date: 2023-02-03 21:06:56.396816 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'ca9b79dde5cc' +down_revision = '2ec4222f0012' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('user', sa.Column('tenant_specific_field_1', sa.String(length=255), nullable=True)) + op.add_column('user', sa.Column('tenant_specific_field_2', sa.String(length=255), nullable=True)) + op.add_column('user', sa.Column('tenant_specific_field_3', sa.String(length=255), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('user', 'tenant_specific_field_3') + op.drop_column('user', 'tenant_specific_field_2') + op.drop_column('user', 'tenant_specific_field_1') + # ### end Alembic commands ### diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py index 64c7e2c1a..24fc452de 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/__init__.py @@ -51,6 +51,19 @@ def load_config_file(app: Flask, env_config_module: str) -> None: ) from exception +def _set_up_tenant_specific_fields_as_list_of_strings(app: Flask) -> None: + tenant_specific_fields = app.config.get("TENANT_SPECIFIC_FIELDS") + + if tenant_specific_fields is None or tenant_specific_fields == "": + app.config["TENANT_SPECIFIC_FIELDS"] = [] + else: + app.config["TENANT_SPECIFIC_FIELDS"] = tenant_specific_fields.split(",") + if len(app.config["TENANT_SPECIFIC_FIELDS"]) > 3: + raise ConfigurationError( + "TENANT_SPECIFIC_FIELDS can have a maximum of 3 fields" + ) + + def setup_config(app: Flask) -> None: """Setup_config.""" # ensure the instance folder exists @@ -108,3 +121,4 @@ def setup_config(app: Flask) -> None: thread_local_data = threading.local() app.config["THREAD_LOCAL_DATA"] = thread_local_data + _set_up_tenant_specific_fields_as_list_of_strings(app) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py index 4f0a82966..52126b1b5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py @@ -72,7 +72,7 @@ GIT_SSH_PRIVATE_KEY = environ.get("GIT_SSH_PRIVATE_KEY") GIT_USERNAME = environ.get("GIT_USERNAME") GIT_USER_EMAIL = environ.get("GIT_USER_EMAIL") -# Datbase Configuration +# Database Configuration SPIFF_DATABASE_TYPE = environ.get( "SPIFF_DATABASE_TYPE", default="mysql" ) # can also be sqlite, postgres @@ -88,3 +88,8 @@ SYSTEM_NOTIFICATION_PROCESS_MODEL_MESSAGE_ID = environ.get( ALLOW_CONFISCATING_LOCK_AFTER_SECONDS = int( environ.get("ALLOW_CONFISCATING_LOCK_AFTER_SECONDS", default="600") ) + +# Tenant specific fields is a comma separated list of field names that we will convert to list of strings +# and store in the user table's tenant_specific_field_n columns. You can have up to three items in this +# comma-separated list. +TENANT_SPECIFIC_FIELDS = environ.get("TENANT_SPECIFIC_FIELDS") diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py index 7f8c88da9..3ecb8b284 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py @@ -34,6 +34,9 @@ class UserModel(SpiffworkflowBaseDBModel): service_id = db.Column(db.String(255), nullable=False, unique=False) display_name = db.Column(db.String(255)) email = db.Column(db.String(255)) + tenant_specific_field_1 = db.Column(db.String(255)) + tenant_specific_field_2 = db.Column(db.String(255)) + tenant_specific_field_3 = db.Column(db.String(255)) updated_at_in_seconds: int = db.Column(db.Integer) created_at_in_seconds: int = db.Column(db.Integer) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py index a72effd46..9134a4bad 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py @@ -484,38 +484,44 @@ class AuthorizationService: .filter(UserModel.service_id == user_info["sub"]) .first() ) - email = display_name = username = "" + user_attributes = {} + if "email" in user_info: - username = user_info["email"] - email = user_info["email"] + user_attributes["username"] = user_info["email"] + user_attributes["email"] = user_info["email"] else: # we fall back to the sub, which may be very ugly. - username = user_info["sub"] + "@" + user_info["iss"] + fallback_username = user_info["sub"] + "@" + user_info["iss"] + user_attributes["username"] = fallback_username if "preferred_username" in user_info: - display_name = user_info["preferred_username"] + user_attributes["display_name"] = user_info[ + "preferred_username" + ] elif "nickname" in user_info: - display_name = user_info["nickname"] + user_attributes["display_name"] = user_info["nickname"] elif "name" in user_info: - display_name = user_info["name"] + user_attributes["display_name"] = user_info["name"] + + user_attributes["service"] = user_info["iss"] + user_attributes["service_id"] = user_info["sub"] + + for field_index, tenant_specific_field in enumerate( + current_app.config["TENANT_SPECIFIC_FIELDS"] + ): + if tenant_specific_field in user_info: + field_number = field_index + 1 + user_attributes[ + f"tenant_specific_field_{field_number}" + ] = user_info[tenant_specific_field] if user_model is None: current_app.logger.debug("create_user in login_return") is_new_user = True - user_model = UserService().create_user( - username=username, - service=user_info["iss"], - service_id=user_info["sub"], - email=email, - display_name=display_name, - ) - + user_model = UserService().create_user(**user_attributes) else: # Update with the latest information - user_model.username = username - user_model.email = email - user_model.display_name = display_name - user_model.service = user_info["iss"] - user_model.service_id = user_info["sub"] + for key, value in user_attributes.items(): + setattr(user_model, key, value) # this may eventually get too slow. # when it does, be careful about backgrounding, because