diff --git a/migrations/versions/b41f0d35641a_.py b/migrations/versions/3a95e16cf17c_.py similarity index 99% rename from migrations/versions/b41f0d35641a_.py rename to migrations/versions/3a95e16cf17c_.py index 8c843c84..73e78b66 100644 --- a/migrations/versions/b41f0d35641a_.py +++ b/migrations/versions/3a95e16cf17c_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: b41f0d35641a +Revision ID: 3a95e16cf17c Revises: -Create Date: 2022-10-18 16:16:23.575571 +Create Date: 2022-10-19 12:42:38.086243 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'b41f0d35641a' +revision = '3a95e16cf17c' down_revision = None branch_labels = None depends_on = None @@ -137,7 +137,7 @@ def upgrade(): op.create_table('secret', sa.Column('id', sa.Integer(), nullable=False), sa.Column('key', sa.String(length=50), nullable=False), - sa.Column('value', sa.String(length=255), nullable=False), + sa.Column('value', sa.Text(), nullable=False), sa.Column('creator_user_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['creator_user_id'], ['user.id'], ), sa.PrimaryKeyConstraint('id'), diff --git a/poetry.lock b/poetry.lock index 91c92d58..4ddf730e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -639,7 +639,7 @@ werkzeug = "*" type = "git" url = "https://github.com/sartography/flask-bpmn" reference = "main" -resolved_reference = "42cebab51fe469ab9dcb45672917d34c1f30987a" +resolved_reference = "6e1411dd134955a829bb9f3d59b4af121907cd35" [[package]] name = "Flask-Cors" @@ -1847,8 +1847,8 @@ test = ["pytest"] [[package]] name = "SpiffWorkflow" -version = "1.2.0" -description = "A workflow framework and BPMN/DMN Processor" +version = "1.2.1" +description = "" category = "main" optional = false python-versions = "*" @@ -1858,7 +1858,6 @@ develop = false celery = "*" configparser = "*" dateparser = "*" -importlib-metadata = "<5.0" lxml = "*" pytz = "*" @@ -1866,7 +1865,7 @@ pytz = "*" type = "git" url = "https://github.com/sartography/SpiffWorkflow" reference = "main" -resolved_reference = "d9fcd45a384f8376a669cf58677564289d2c661c" +resolved_reference = "a094adad8767f82e9c5fa806a46597e066252a72" [[package]] name = "SQLAlchemy" diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 69cc7277..ac73743d 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -951,6 +951,47 @@ paths: schema: $ref: "#/components/schemas/ServiceTask" + /authentication_callback/{service}/{auth_method}: + parameters: + - name: service + in: path + required: true + description: The name of the service + schema: + type: string + - name: auth_method + in: path + required: true + description: The method + schema: + type: string + - name: response + in: query + required: true + description: The response + schema: + type: string + - name: token + in: query + required: true + description: The response + schema: + type: string + get: + # disable security so we can get the token from query params instead + security: [] + tags: + - Authentications + operationId: spiffworkflow_backend.routes.process_api_blueprint.authentication_callback + summary: Callback to backend + responses: + "200": + description: All authentications + content: + application/json: + schema: + $ref: "#/components/schemas/ServiceTask" + /tasks/{process_instance_id}/{task_id}: parameters: - name: task_id diff --git a/src/spiffworkflow_backend/models/secret_model.py b/src/spiffworkflow_backend/models/secret_model.py index 444a531d..0d650ab9 100644 --- a/src/spiffworkflow_backend/models/secret_model.py +++ b/src/spiffworkflow_backend/models/secret_model.py @@ -16,7 +16,7 @@ class SecretModel(SpiffworkflowBaseDBModel): __tablename__ = "secret" id: int = db.Column(db.Integer, primary_key=True) key: str = db.Column(db.String(50), unique=True, nullable=False) - value: str = db.Column(db.String(255), nullable=False) + value: str = db.Column(db.Text(), nullable=False) creator_user_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 3358e3dc..3b5a9ea2 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -13,11 +13,13 @@ from typing import Union import connexion # type: ignore import flask.wrappers import jinja2 +import werkzeug from flask import Blueprint from flask import current_app from flask import g from flask import jsonify from flask import make_response +from flask import redirect from flask import request from flask.wrappers import Response from flask_bpmn.api.api_error import ApiError @@ -52,6 +54,7 @@ from spiffworkflow_backend.models.secret_model import SecretModel from spiffworkflow_backend.models.secret_model import SecretModelSchema from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.routes.user import verify_token from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.git_service import GitService @@ -780,11 +783,28 @@ def authentication_list() -> flask.wrappers.Response: response_json = { "results": available_authentications, "connector_proxy_base_url": current_app.config["CONNECTOR_PROXY_URL"], + "redirect_url": f"{current_app.config['SPIFFWORKFLOW_BACKEND_URL']}/v1.0/authentication_callback", } return Response(json.dumps(response_json), status=200, mimetype="application/json") +def authentication_callback( + service: str, + auth_method: str, +) -> werkzeug.wrappers.Response: + """Authentication_callback.""" + verify_token(request.args.get("token")) + response = request.args["response"] + print(f"response: {response}") + SecretService().update_secret( + f"{service}/{auth_method}", response, g.user.id, create_if_not_exists=True + ) + return redirect( + f"{current_app.config['SPIFFWORKFLOW_FRONTEND_URL']}/admin/authentications" + ) + + def process_instance_report_show( process_group_id: str, process_model_id: str, diff --git a/src/spiffworkflow_backend/services/secret_service.py b/src/spiffworkflow_backend/services/secret_service.py index f6b35e0a..fb71be4d 100644 --- a/src/spiffworkflow_backend/services/secret_service.py +++ b/src/spiffworkflow_backend/services/secret_service.py @@ -69,7 +69,8 @@ class SecretService: def update_secret( key: str, value: str, - creator_user_id: Optional[int] = None, + creator_user_id: int, + create_if_not_exists: Optional[bool] = False, ) -> None: """Does this pass pre commit?""" secret_model = SecretModel.query.filter(SecretModel.key == key).first() @@ -80,16 +81,18 @@ class SecretService: try: db.session.commit() except Exception as e: - raise ApiError( - error_code="update_secret_error", - message=f"There was an error updating the secret with key: {key}, and value: {value}", - ) from e + db.session.rollback() + raise e else: raise ApiError( error_code="update_secret_error", message=f"User: {creator_user_id} cannot update the secret with key : {key}", status_code=401, ) + elif create_if_not_exists: + SecretService.add_secret( + key=key, value=value, creator_user_id=creator_user_id + ) else: raise ApiError( error_code="update_secret_error", diff --git a/src/spiffworkflow_backend/services/service_task_service.py b/src/spiffworkflow_backend/services/service_task_service.py index 33e318e5..173d8789 100644 --- a/src/spiffworkflow_backend/services/service_task_service.py +++ b/src/spiffworkflow_backend/services/service_task_service.py @@ -90,5 +90,4 @@ class ServiceTaskService: parsed_response = json.loads(response.text) return parsed_response except Exception as exception: - current_app.logger.error(exception) raise ConnectorProxyError(exception.__class__.__name__) from exception diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index 3f71f1f1..aff5e5e4 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1560,6 +1560,16 @@ class TestProcessApi(BaseTest): assert response.json is not None assert len(response.json["results"]) == 2 + # TODO: test the auth callback endpoint + # def test_can_store_authentication_secret( + # self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None + # ) -> None: + # """Test_can_store_authentication_secret.""" + # response = client.get( + # "/v1.0/authentication_callback", + # headers=self.logged_in_headers(user), + # ) + # def test_get_process_model(self): # # load_test_spec('random_fact')