This commit is contained in:
Dan 2023-01-19 13:26:13 -05:00
commit ecbe194831
38 changed files with 682 additions and 312 deletions

View File

@ -8,8 +8,11 @@ rst-roles = class,const,func,meth,mod,ref
rst-directives = deprecated
per-file-ignores =
# prefer naming tests descriptively rather than forcing comments
tests/*:S101,D103
# asserts are ok in tests
tests/*:S101
# prefer naming functions descriptively rather than forcing comments
*:D103
bin/keycloak_test_server.py:B950,D
conftest.py:S105

View File

@ -22,8 +22,8 @@ set -o errtrace -o errexit -o nounset -o pipefail
# KEYCLOAK_BASE_URL=http://localhost:7002
KEYCLOAK_BASE_URL=https://keycloak.dev.spiffworkflow.org
# BACKEND_BASE_URL=http://localhost:7000
BACKEND_BASE_URL=https://api.dev.spiffworkflow.org
BACKEND_BASE_URL=http://localhost:7000
# BACKEND_BASE_URL=https://api.dev.spiffworkflow.org
REALM_NAME=spiffworkflow
USERNAME=${1-fin}
PASSWORD=${2-fin}

View File

@ -1,12 +0,0 @@
{
"web": {
"issuer": "http://localhost:8080/realms/finance",
"auth_uri": "http://localhost:8080/realms/finance/protocol/openid-connect/auth",
"client_id": "myclient",
"client_secret": "OAh6rkjXIiPJDtPOz4459i3VtdlxGcce",
"redirect_uris": ["http://localhost:5005/*"],
"userinfo_uri": "http://localhost:8080/realms/finance/protocol/openid-connect/userinfo",
"token_uri": "http://localhost:8080/realms/finance/protocol/openid-connect/token",
"token_introspection_uri": "http://localhost:8080/realms/finance/protocol/openid-connect/token/introspect"
}
}

View File

@ -1,105 +0,0 @@
# type: ignore
"""keycloak_test_server."""
# ./bin/start_keycloak # starts keycloak on 8080
# pip install flask_oidc
# pip install itsdangerous==2.0.1
# python ./bin/keycloak_test_server.py # starts flask on 5005
import json
import logging
import requests
from flask import Flask
from flask import g
from flask_oidc import OpenIDConnect
logging.basicConfig(level=logging.DEBUG)
app = Flask(__name__)
app.config.update(
{
"SECRET_KEY": "SomethingNotEntirelySecret",
"TESTING": True,
"DEBUG": True,
"OIDC_CLIENT_SECRETS": "bin/keycloak_test_secrets.json",
"OIDC_ID_TOKEN_COOKIE_SECURE": False,
"OIDC_REQUIRE_VERIFIED_EMAIL": False,
"OIDC_USER_INFO_ENABLED": True,
"OIDC_OPENID_REALM": "flask-demo",
"OIDC_SCOPES": ["openid", "email", "profile"],
"OIDC_INTROSPECTION_AUTH_METHOD": "client_secret_post",
}
)
oidc = OpenIDConnect(app)
@app.route("/")
def hello_world():
"""Hello_world."""
if oidc.user_loggedin:
return (
'Hello, %s, <a href="/private">See private</a> '
'<a href="/logout">Log out</a>'
% oidc.user_getfield("preferred_username")
)
else:
return 'Welcome anonymous, <a href="/private">Log in</a>'
@app.route("/private")
@oidc.require_login
def hello_me():
"""Example for protected endpoint that extracts private information from the OpenID Connect id_token.
Uses the accompanied access_token to access a backend service.
"""
info = oidc.user_getinfo(["preferred_username", "email", "sub"])
username = info.get("preferred_username")
email = info.get("email")
user_id = info.get("sub")
if user_id in oidc.credentials_store:
try:
from oauth2client.client import OAuth2Credentials
access_token = OAuth2Credentials.from_json(
oidc.credentials_store[user_id]
).access_token
print("access_token=<%s>" % access_token)
headers = {"Authorization": "Bearer %s" % (access_token)}
# YOLO
greeting = requests.get(
"http://localhost:8080/greeting", headers=headers
).text
except BaseException:
print("Could not access greeting-service")
greeting = "Hello %s" % username
return """{} your email is {} and your user_id is {}!
<ul>
<li><a href="/">Home</a></li>
<li><a href="//localhost:8080/auth/realms/finance/account?referrer=flask-app&referrer_uri=http://localhost:5005/private&">Account</a></li>
</ul>""".format(
greeting,
email,
user_id,
)
@app.route("/api", methods=["POST"])
@oidc.accept_token(require_token=True, scopes_required=["openid"])
def hello_api():
"""OAuth 2.0 protected API endpoint accessible via AccessToken."""
return json.dumps({"hello": "Welcome %s" % g.oidc_token_info["sub"]})
@app.route("/logout")
def logout():
"""Performs local logout by removing the session cookie."""
oidc.logout()
return 'Hi, you have been logged out! <a href="/">Return</a>'
if __name__ == "__main__":
app.run(port=5005)

View File

@ -29,7 +29,13 @@ else
export FLASK_DEBUG=1
if [[ "${SPIFFWORKFLOW_BACKEND_RUN_DATA_SETUP:-}" != "false" ]]; then
SPIFFWORKFLOW_BACKEND_FAIL_ON_INVALID_PROCESS_MODELS=false poetry run python bin/save_all_bpmn.py
RUN_BACKGROUND_SCHEDULER=false SPIFFWORKFLOW_BACKEND_FAIL_ON_INVALID_PROCESS_MODELS=false poetry run python bin/save_all_bpmn.py
fi
FLASK_APP=src/spiffworkflow_backend poetry run flask run -p 7000
if [[ -z "${RUN_BACKGROUND_SCHEDULER:-}" ]]; then
RUN_BACKGROUND_SCHEDULER=true
fi
# this line blocks
RUN_BACKGROUND_SCHEDULER="${RUN_BACKGROUND_SCHEDULER}" FLASK_APP=src/spiffworkflow_backend poetry run flask run -p 7000
fi

View File

@ -1,10 +0,0 @@
#!/usr/bin/env bash
function error_handler() {
>&2 echo "Exited with BAD EXIT CODE '${2}' in ${0} script at line: ${1}."
exit "$2"
}
trap 'error_handler ${LINENO} $?' ERR
set -o errtrace -o errexit -o nounset -o pipefail
curl -v -F key1=value1 -F upload=@localfilename URL

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
function error_handler() {
>&2 echo "Exited with BAD EXIT CODE '${2}' in ${0} script at line: ${1}."
exit "$2"
}
trap 'error_handler ${LINENO} $?' ERR
set -o errtrace -o errexit -o nounset -o pipefail
if [[ "${1:-}" == "c" ]]; then
curl --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{}'
elif grep -qE '^[0-9]$' <<<"${1:-}" ; then
curl --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d "{ \"task_identifier\": \"${1}\"}"
else
./bin/recreate_db clean
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Product Name": "G", "Quantity": "2"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Sleeve Type": "Short"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Continue shopping?": "N"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Shipping Method": "Overnight"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Shipping Address": "Somewhere"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Place Order": "Y"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Card Number": "MY_CARD"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "2", "answer": {"Was the customer charged?": "Y"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Was the product available?": "Y"}}' | jq .
curl --silent --fail localhost:5000/run_process -H "Content-type: application/json" -X POST -d '{ "task_identifier": "1", "answer": {"Was the order shipped?": "Y"}}' | jq .
fi

View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
function error_handler() {
>&2 echo "Exited with BAD EXIT CODE '${2}' in ${0} script at line: ${1}."
exit "$2"
}
trap 'error_handler ${LINENO} $?' ERR
set -o errtrace -o errexit -o nounset -o pipefail
user_file_with_one_email_per_line="${1:-}"
if [[ -z "${1:-}" ]]; then
>&2 echo "usage: $(basename "$0") [user_file_with_one_email_per_line]"
exit 1
fi
KEYCLOAK_BASE_URL=http://localhost:7002
REALM_NAME=master
ADMIN_USERNAME="admin"
ADMIN_PASSWORD="admin"
SECURE=false
KEYCLOAK_URL=$KEYCLOAK_BASE_URL/realms/$REALM_NAME/protocol/openid-connect/token
if [[ $SECURE = 'y' ]]; then
INSECURE=
else
INSECURE=--insecure
fi
# https://www.appsdeveloperblog.com/keycloak-rest-api-create-a-new-user/
result=$(curl --fail -s -X POST "$KEYCLOAK_URL" "$INSECURE" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "username=${ADMIN_USERNAME}" \
--data-urlencode "password=${ADMIN_PASSWORD}" \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=admin-cli'
)
backend_token=$(jq -r '.access_token' <<< "$result")
while read -r user_email; do
if [[ -n "$user_email" ]]; then
username=$(awk -F '@' '{print $1}' <<<"$user_email")
credentials='{"type":"password","value":"'"${username}"'","temporary":false}'
curl --fail --location --request POST 'http://localhost:7002/admin/realms/spiffworkflow/users' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $backend_token" \
--data-raw '{"email":"'"${user_email}"'", "enabled":"true", "username":"'"${username}"'", "credentials":['"${credentials}"']}'
fi
done <"$user_file_with_one_email_per_line"

View File

@ -7,6 +7,8 @@ function error_handler() {
trap 'error_handler ${LINENO} $?' ERR
set -o errtrace -o errexit -o nounset -o pipefail
script_dir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
realms="$*"
if [[ -z "$realms" ]]; then
realms="spiffworkflow-realm"
@ -19,7 +21,7 @@ docker exec keycloak /opt/keycloak/bin/kc.sh export --dir "${docker_container_pa
docker cp "keycloak:${docker_container_path}" "$local_tmp_dir"
for realm in $realms ; do
cp "${local_tmp_dir}/hey/${realm}.json" bin/
cp "${local_tmp_dir}/hey/${realm}.json" "${script_dir}/../realm_exports/"
done
rm -rf "$local_tmp_dir"

View File

@ -45,7 +45,7 @@ docker run \
-Dkeycloak.profile.feature.admin_fine_grained_authz=enabled
script_dir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
cp "${script_dir}/spiffworkflow-realm.json" /tmp/spiffworkflow-realm.json
cp "${script_dir}/../realm_exports/spiffworkflow-realm.json" /tmp/spiffworkflow-realm.json
spiff_subdomain="unused-for-local-dev"
perl -pi -e "s/{{SPIFF_SUBDOMAIN}}/${spiff_subdomain}/g" /tmp/spiffworkflow-realm.json
docker cp /tmp/spiffworkflow-realm.json keycloak:/tmp

View File

@ -634,6 +634,46 @@
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "29ba295e-9a70-41f1-bf0d-f02b468397c5",
"createdTimestamp" : 1674148694595,
"username" : "finance.lead",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "finance.lead@status.im",
"credentials" : [ {
"id" : "8f746fde-0a10-41b4-a973-0b967de73839",
"type" : "password",
"createdDate" : 1674148694661,
"secretData" : "{\"value\":\"vhe8ONTdkYaXLcSr73/4Ey//7U7rxh/0hiGc9S0wp8FV3EUsf+3bQSreDQCTp3DePJInpVCV34d4T0Ij+6Po0A==\",\"salt\":\"s6hEEdUPlULWfqGpxlG+TQ==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "f6d2488a-446c-493b-bbe8-210ede6f3e42",
"createdTimestamp" : 1674148694899,
"username" : "finance.sme",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "finance.sme@status.im",
"credentials" : [ {
"id" : "faee8eaa-0bf4-4050-8d17-8b6b52f0b7ee",
"type" : "password",
"createdDate" : 1674148694945,
"secretData" : "{\"value\":\"tk78HqSoRT0PAJ45zt2/q6gXRYxvDDIYtLzsVdYM3sHk+tRkgYeXoyKDSyRwHm9AjbM8jFI5yUXPsWck8vemOg==\",\"salt\":\"aR9qgYMx1VUfOrppTDzMmQ==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "9b46f3be-a81d-4b76-92e6-2ac8462f5ec8",
"createdTimestamp" : 1665688255982,
@ -674,6 +714,26 @@
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "f55135de-7341-459d-8a42-a59f52d05bed",
"createdTimestamp" : 1674148694958,
"username" : "infra.sme",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "infra.sme@status.im",
"credentials" : [ {
"id" : "e1f4368c-ed7c-481c-9426-fc0b8f2bf520",
"type" : "password",
"createdDate" : 1674148695008,
"secretData" : "{\"value\":\"7RHwvrhGAA3EddNNjPaVah+EOg5be0eugiwLLQLGlhFGSdGfg6kiUmPr5wBqBabivXHiSZgv/BiaL5KQ/VmR+A==\",\"salt\":\"HW3yCxErwpKASPvHX8o9Uw==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "1561518b-c327-491e-9db3-23c2b5394104",
"createdTimestamp" : 1669303773974,
@ -843,6 +903,46 @@
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "530e99cb-b400-4baf-8ca6-22e64a30ef84",
"createdTimestamp" : 1674148694688,
"username" : "legal.lead",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "legal.lead@status.im",
"credentials" : [ {
"id" : "81f3aeca-8316-4a1b-8eb9-2570c062d0df",
"type" : "password",
"createdDate" : 1674148694733,
"secretData" : "{\"value\":\"puCrVcCNrO6P0VF8w0ZSx97RHi/c6NCuSeTidk/tEfSpZyY9x0oz/bkdFJO359HuvhN5HMBQ+CKPNbW1VjOSoA==\",\"salt\":\"ZczpeV+0QJGZG96EfLWYRQ==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "2a3176a0-8dd5-4223-a3e1-3cac4134e474",
"createdTimestamp" : 1674148695030,
"username" : "legal.sme",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "legal.sme@status.im",
"credentials" : [ {
"id" : "52fd8bd4-8fc4-4b71-8325-424220ef83af",
"type" : "password",
"createdDate" : 1674148695076,
"secretData" : "{\"value\":\"Rce1M5ph1ITsCguiHlv7YMcDTyofRnSPnOraQskkmeojV+tlUeBBsHV1fTiqJ4f13vE1qtnwC/60vQV8BprsHw==\",\"salt\":\"zFyJq5G2F/pZeLmgKaGoxQ==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "6f5bfa09-7494-4a2f-b871-cf327048cac7",
"createdTimestamp" : 1665517010600,
@ -905,6 +1005,26 @@
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "c3ea06ee-c497-48e6-8816-43c8ef68bd8b",
"createdTimestamp" : 1674148694747,
"username" : "program.lead",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "program.lead@status.im",
"credentials" : [ {
"id" : "393e3cd9-c403-41dd-8562-7edba6acedd3",
"type" : "password",
"createdDate" : 1674148694793,
"secretData" : "{\"value\":\"AD/rFDJcnQNVSZLVnLl6FzdiMSkRFiKiF2L6jyPtnAOAuQ6IivNvDIqiZf98rPuSq1zs8wjeDzFzyXvTYp7Pjg==\",\"salt\":\"T4XlF58M6LNTX8ksxYq8jQ==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "f3852a7d-8adf-494f-b39d-96ad4c899ee5",
"createdTimestamp" : 1665516926300,
@ -925,6 +1045,26 @@
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "74374cda-1516-48e5-9ef2-1fd7bcee84d3",
"createdTimestamp" : 1674148695088,
"username" : "security.sme",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "security.sme@status.im",
"credentials" : [ {
"id" : "43427e80-292e-453f-9968-511a1064729e",
"type" : "password",
"createdDate" : 1674148695133,
"secretData" : "{\"value\":\"HB68S1rm/fef2nY2qpakAyZ0a+OFM0G/Xp+kHNdTQSWZA6fYq8EUzhfTFkUQ5xuTriOesXao0srtFmcCs2Pi8Q==\",\"salt\":\"e8J1O8M7mrDq/jTJXzwYyQ==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "487d3a85-89dd-4839-957a-c3f6d70551f6",
"createdTimestamp" : 1657115173081,
@ -961,6 +1101,26 @@
},
"notBefore" : 0,
"groups" : [ ]
}, {
"id" : "3d45bb85-0a2d-4b15-8a19-d26a5619d359",
"createdTimestamp" : 1674148694810,
"username" : "services.lead",
"enabled" : true,
"totp" : false,
"emailVerified" : false,
"email" : "services.lead@status.im",
"credentials" : [ {
"id" : "45607c53-3768-4f76-bda3-4d31b39ffccd",
"type" : "password",
"createdDate" : 1674148694884,
"secretData" : "{\"value\":\"E3GPcOLU56efhBQE7MMZa0OM0FAtgK5kDA9sy65uCwSyaoZGp4ZVUDsIfIkWe+TEEQA5QP5FVJbJhwvdkx3m9w==\",\"salt\":\"dySpiEZxeyb11oQZR2WYVQ==\",\"additionalParameters\":{}}",
"credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "default-roles-spiffworkflow" ],
"notBefore" : 0,
"groups" : [ ]
} ],
"scopeMappings" : [ {
"clientScope" : "offline_access",
@ -2174,7 +2334,7 @@
"subType" : "authenticated",
"subComponents" : { },
"config" : {
"allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper" ]
"allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper" ]
}
}, {
"id" : "d68e938d-dde6-47d9-bdc8-8e8523eb08cd",
@ -2192,7 +2352,7 @@
"subType" : "anonymous",
"subComponents" : { },
"config" : {
"allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-attribute-mapper" ]
"allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-attribute-mapper" ]
}
}, {
"id" : "3854361d-3fe5-47fb-9417-a99592e3dc5c",
@ -2282,7 +2442,7 @@
"internationalizationEnabled" : false,
"supportedLocales" : [ ],
"authenticationFlows" : [ {
"id" : "76ae522e-7ab3-48dc-af76-9cb8069368a2",
"id" : "fd44ea2b-052b-470a-9afd-216390c40d54",
"alias" : "Account verification options",
"description" : "Method with which to verity the existing account",
"providerId" : "basic-flow",
@ -2304,7 +2464,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "ddf80243-ec40-4c21-ae94-2967d841f84c",
"id" : "88a96abb-a839-4405-97bf-fa53f5290482",
"alias" : "Authentication Options",
"description" : "Authentication options.",
"providerId" : "basic-flow",
@ -2333,7 +2493,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "4f075680-46b7-49eb-b94c-d7425f105cb9",
"id" : "cbe05604-280f-4304-bda5-ed5245537f4d",
"alias" : "Browser - Conditional OTP",
"description" : "Flow to determine if the OTP is required for the authentication",
"providerId" : "basic-flow",
@ -2355,7 +2515,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "a0467c77-c3dc-4df6-acd2-c05ca13601ed",
"id" : "5275913f-e597-4a89-b416-4f9412b9082b",
"alias" : "Direct Grant - Conditional OTP",
"description" : "Flow to determine if the OTP is required for the authentication",
"providerId" : "basic-flow",
@ -2377,7 +2537,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "07536fec-8d41-4c73-845f-ca85002022e0",
"id" : "a0afd432-ed89-41c6-be8d-f31834e80ba1",
"alias" : "First broker login - Conditional OTP",
"description" : "Flow to determine if the OTP is required for the authentication",
"providerId" : "basic-flow",
@ -2399,7 +2559,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "f123f912-71fb-4596-97f9-c0628a59413d",
"id" : "fab45b23-3353-4482-b690-07f3ab177776",
"alias" : "Handle Existing Account",
"description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
"providerId" : "basic-flow",
@ -2421,7 +2581,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "03c26cc5-366b-462d-9297-b4016f8d7c57",
"id" : "f5eb0757-f2cd-4d4b-9608-d1b9ae4fd941",
"alias" : "Reset - Conditional OTP",
"description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
"providerId" : "basic-flow",
@ -2443,7 +2603,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "1b4f474e-aa64-45cc-90f1-63504585d89c",
"id" : "521586b9-ade0-4f8c-aff6-3d6c357aa6e4",
"alias" : "User creation or linking",
"description" : "Flow for the existing/non-existing user alternatives",
"providerId" : "basic-flow",
@ -2466,7 +2626,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "38024dd6-daff-45de-8782-06b07b7bfa56",
"id" : "b21bb98a-9241-4484-966b-6f8294ba2186",
"alias" : "Verify Existing Account by Re-authentication",
"description" : "Reauthentication of existing account",
"providerId" : "basic-flow",
@ -2488,7 +2648,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "b7e30fca-e4ac-4886-a2e7-642fe2a27ee7",
"id" : "7ec2a1f6-37e7-444e-9376-dee7d442ec2f",
"alias" : "browser",
"description" : "browser based authentication",
"providerId" : "basic-flow",
@ -2524,7 +2684,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "92e3571d-ac3e-4e79-a391-5315954e866f",
"id" : "1bc2b251-bf69-40b1-ace2-e3be5037b910",
"alias" : "clients",
"description" : "Base authentication for clients",
"providerId" : "client-flow",
@ -2560,7 +2720,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "5093dd2d-fe5d-4f41-a54d-03cd648d9b7f",
"id" : "12a854bd-4d8a-49eb-8be5-cfc9d25cba54",
"alias" : "direct grant",
"description" : "OpenID Connect Resource Owner Grant",
"providerId" : "basic-flow",
@ -2589,7 +2749,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "95d2f1ff-6907-47ce-a93c-db462fe04844",
"id" : "99ebf3a7-674e-4603-a0cf-8fe4c6dd4cfc",
"alias" : "docker auth",
"description" : "Used by Docker clients to authenticate against the IDP",
"providerId" : "basic-flow",
@ -2604,7 +2764,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "27405ee8-5730-419c-944c-a7c67edd91ce",
"id" : "a241b9b8-9c21-4a47-877a-5a6535678c90",
"alias" : "first broker login",
"description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
"providerId" : "basic-flow",
@ -2627,7 +2787,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "fce6d926-3a99-40ee-b79e-cae84493dbd8",
"id" : "c9df7ad1-9b59-46ec-a85e-714fd682569c",
"alias" : "forms",
"description" : "Username, password, otp and other auth forms.",
"providerId" : "basic-flow",
@ -2649,7 +2809,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "75d93596-b7fb-4a2c-a780-e6a038e66fe9",
"id" : "14f21f85-2bcb-4ed6-aaab-1ee237da153f",
"alias" : "http challenge",
"description" : "An authentication flow based on challenge-response HTTP Authentication Schemes",
"providerId" : "basic-flow",
@ -2671,7 +2831,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "04cdc1ac-c58d-4f8c-bc10-7d5e2bb99485",
"id" : "bc7e40c0-9172-496b-8db1-3ebc20065887",
"alias" : "registration",
"description" : "registration flow",
"providerId" : "basic-flow",
@ -2687,7 +2847,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "99593c1e-f2a5-4198-ad41-634694259110",
"id" : "ef97f42b-7f32-442c-ab4a-8cb6c873cf1f",
"alias" : "registration form",
"description" : "registration form",
"providerId" : "form-flow",
@ -2723,7 +2883,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "7d53f026-b05e-4a9c-aba6-23b17826a4d4",
"id" : "1ee2b484-3836-466f-9f5b-bbf47abc5ad7",
"alias" : "reset credentials",
"description" : "Reset credentials for a user if they forgot their password or something",
"providerId" : "basic-flow",
@ -2759,7 +2919,7 @@
"userSetupAllowed" : false
} ]
}, {
"id" : "7ca17e64-f916-4d6c-91f0-815ec66f50e8",
"id" : "4918f32e-6780-4ddd-a1a2-c3ae9d8fa598",
"alias" : "saml ecp",
"description" : "SAML ECP Profile Authentication Flow",
"providerId" : "basic-flow",
@ -2775,13 +2935,13 @@
} ]
} ],
"authenticatorConfig" : [ {
"id" : "9b71d817-b999-479d-97f8-07e39dd9e9fa",
"id" : "5479944f-6198-48df-8a18-4bc0caba5963",
"alias" : "create unique user config",
"config" : {
"require.password.update.after.registration" : "false"
}
}, {
"id" : "f9f13ba1-6a17-436b-a80b-6ccc042f9fc2",
"id" : "fd9f571f-0d6e-4ece-a3e5-fffccc1e4fad",
"alias" : "review profile config",
"config" : {
"update.profile.on.first.login" : "missing"
@ -2876,4 +3036,4 @@
"clientPolicies" : {
"policies" : [ ]
}
}
}

View File

@ -0,0 +1,9 @@
finance.lead@status.im
legal.lead@status.im
program.lead@status.im
services.lead@status.im
finance.sme@status.im
infra.sme@status.im
legal.sme@status.im
security.sme@status.im

161
poetry.lock generated
View File

@ -248,7 +248,7 @@ zstd = ["zstandard"]
[[package]]
name = "certifi"
version = "2022.9.24"
version = "2022.12.7"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
@ -610,6 +610,37 @@ python-versions = "*"
bcrypt = ">=3.1.1"
Flask = "*"
[[package]]
name = "flask-bpmn"
version = "0.0.0"
description = "Flask Bpmn"
category = "main"
optional = false
python-versions = "^3.7"
develop = false
[package.dependencies]
click = "^8.0.1"
flask = "*"
flask-admin = "*"
flask-bcrypt = "*"
flask-cors = "*"
flask-mail = "*"
flask-marshmallow = "*"
flask-migrate = "*"
flask-restful = "*"
greenlet = "^2.0.1"
sentry-sdk = "*"
sphinx-autoapi = "^2.0.0"
spiffworkflow = "*"
werkzeug = "*"
[package.source]
type = "git"
url = "https://github.com/sartography/flask-bpmn"
reference = "main"
resolved_reference = "c18306300f4312b8d36e0197fd6b62399180d0b1"
[[package]]
name = "Flask-Cors"
version = "3.0.10"
@ -1043,19 +1074,19 @@ python-versions = "*"
[[package]]
name = "mysql-connector-python"
version = "8.0.31"
version = "8.0.32"
description = "MySQL driver written in Python"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
protobuf = ">=3.11.0,<=3.20.1"
protobuf = ">=3.11.0,<=3.20.3"
[package.extras]
compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.15.2)"]
compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.19.0)"]
dns-srv = ["dnspython (>=1.16.0,<=2.1.0)"]
gssapi = ["gssapi (>=1.6.9,<=1.8.1)"]
gssapi = ["gssapi (>=1.6.9,<=1.8.2)"]
[[package]]
name = "nodeenv"
@ -1179,7 +1210,7 @@ wcwidth = "*"
[[package]]
name = "protobuf"
version = "3.20.1"
version = "3.20.3"
description = "Protocol Buffers"
category = "main"
optional = false
@ -1745,7 +1776,7 @@ description = "A workflow framework and BPMN/DMN Processor"
category = "main"
optional = false
python-versions = "*"
develop = true
develop = false
[package.dependencies]
celery = "*"
@ -1753,8 +1784,10 @@ configparser = "*"
lxml = "*"
[package.source]
type = "directory"
url = "../../SpiffWorkflow"
type = "git"
url = "https://github.com/sartography/SpiffWorkflow"
reference = "be26100bcbef8026e26312c665dae42faf476485"
resolved_reference = "be26100bcbef8026e26312c665dae42faf476485"
[[package]]
name = "SQLAlchemy"
@ -2125,7 +2158,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "1.1"
python-versions = ">=3.9,<3.12"
content-hash = "f646836512a18f2a0e9ba4979926e004abd17790da5178e06e87092fdf23c35f"
content-hash = "d804b8cbb34882f92cf19e5e59231aa7eac84764298fe7eae72bd03112e09496"
[metadata.files]
alabaster = [
@ -2227,8 +2260,8 @@ celery = [
{file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"},
]
certifi = [
{file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
{file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
{file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
{file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
]
cfgv = [
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
@ -2388,6 +2421,7 @@ Flask-Bcrypt = [
{file = "Flask-Bcrypt-1.0.1.tar.gz", hash = "sha256:f07b66b811417ea64eb188ae6455b0b708a793d966e1a80ceec4a23bc42a4369"},
{file = "Flask_Bcrypt-1.0.1-py3-none-any.whl", hash = "sha256:062fd991dc9118d05ac0583675507b9fe4670e44416c97e0e6819d03d01f808a"},
]
flask-bpmn = []
Flask-Cors = [
{file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"},
{file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"},
@ -2465,7 +2499,6 @@ greenlet = [
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"},
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"},
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"},
{file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0459d94f73265744fee4c2d5ec44c6f34aa8a31017e6e9de770f7bcf29710be9"},
{file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"},
{file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"},
{file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"},
@ -2474,7 +2507,6 @@ greenlet = [
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"},
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"},
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"},
{file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d38ffd0e81ba8ef347d2be0772e899c289b59ff150ebbbbe05dc61b1246eb4e0"},
{file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"},
{file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"},
{file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"},
@ -2483,7 +2515,6 @@ greenlet = [
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"},
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"},
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"},
{file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:662e8f7cad915ba75d8017b3e601afc01ef20deeeabf281bd00369de196d7726"},
{file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"},
{file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"},
{file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"},
@ -2740,32 +2771,31 @@ mypy-extensions = [
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
mysql-connector-python = [
{file = "mysql-connector-python-8.0.31.tar.gz", hash = "sha256:0fbe8f5441ad781b4f65c54a10ac77c6a329591456607e042786528599519636"},
{file = "mysql_connector_python-8.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3e271d8de00d5e9f9bd4b212c8e23d2986dead0f20379010f3b274a3e24cbfcb"},
{file = "mysql_connector_python-8.0.31-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f3ee04a601f9cb90ace9618bbe2fa8e5bb59be3eb0c2bd8a5405fe69e05e446b"},
{file = "mysql_connector_python-8.0.31-cp310-cp310-manylinux1_i686.whl", hash = "sha256:f89b7a731885b8a04248e4d8d124705ca836f0ddd3b7cf0c789e21f4b32810ed"},
{file = "mysql_connector_python-8.0.31-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:48eb34f4e69a2fba56f310de6682862a15d46cd2bd51ee6eebc3a244e4ee0aa6"},
{file = "mysql_connector_python-8.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:a570a72e0015b36b9c0775ae27c1d4946225f02f62129d16a14e9d77a38c0717"},
{file = "mysql_connector_python-8.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7ac859a52486ac319e37f61469bbb9023faef38018223efa74e953f1fe23d36"},
{file = "mysql_connector_python-8.0.31-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:79d6a6e8ce955df5ca0786cb8ed8fbd999745c9b50def89993a2a0f4732de721"},
{file = "mysql_connector_python-8.0.31-cp311-cp311-manylinux1_i686.whl", hash = "sha256:e60426af313dcd526028d018d70757a82c5cc0673776b2a614e2180b5970feed"},
{file = "mysql_connector_python-8.0.31-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:d0ca1ba3e5fb2f2cddcf271c320cd5c368f8d392c034ddab7a1c8dfd19510351"},
{file = "mysql_connector_python-8.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:a1d8c1509c740649f352400d50360185e5473371507bb6498ceda0c6e877920c"},
{file = "mysql_connector_python-8.0.31-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:447847396d1b51edd9cfe05a8c5ba82836d8ea4866f25f36a836cab322fdc4f0"},
{file = "mysql_connector_python-8.0.31-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5e01a2f50378c13407a32e40dd4d225cfee5996d9d11968f76720ec28aa45421"},
{file = "mysql_connector_python-8.0.31-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ac85883ec3b3a9a0e36cacc89b8f5e666206842c432a5f69b09a7687ddf51d4a"},
{file = "mysql_connector_python-8.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:28cb3667be64ebfbd3d477bbd2c71e50d48bd5ed7ba2072dd460ae886d27e88e"},
{file = "mysql_connector_python-8.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:30f4542d4d20357c79604e6bf1a801e71dfc45c759c22b502ca5aa8122c3e859"},
{file = "mysql_connector_python-8.0.31-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:e9e5ad544adfc82ffbda2c74685c8c953bce2e212c56f117020079f05e2c68b2"},
{file = "mysql_connector_python-8.0.31-cp38-cp38-manylinux1_i686.whl", hash = "sha256:744c976569e81eecce5e8c7e8f80df2a1c3f64414829addc69c64aef8f56d091"},
{file = "mysql_connector_python-8.0.31-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17d6ea22dacca7fa78a73a81f2b186d4c5c6e70b7be314e352526654e9ba4713"},
{file = "mysql_connector_python-8.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:ae1b3d03802474a161cce8a97024484d18bef43b86d20114908cbc263817cade"},
{file = "mysql_connector_python-8.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:746df133c677fbe4687da33aad5a711abdd9bd2277bbc350e20f903f07c81ef5"},
{file = "mysql_connector_python-8.0.31-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:4d75e6c3a7f18004e8279cbd9f5edc70089d6aaf3cb64374e21098d9bf0b93c4"},
{file = "mysql_connector_python-8.0.31-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8ad0d08f3f7c9e48d6d102c7de718e5e44f630f916ff2f4b4ff8a3756b5d10ac"},
{file = "mysql_connector_python-8.0.31-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:02526f16eacc3961ff681c5c8455d2306a9b45124f2f012ca75a1eac9ceb5165"},
{file = "mysql_connector_python-8.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:b2bbf443f6346e46c26a3e91dd96a428a1038f2d3c5e466541078479c64a1833"},
{file = "mysql_connector_python-8.0.31-py2.py3-none-any.whl", hash = "sha256:9be9c4dcae987a2a3f07b2ad984984c24f90887dbfab3c8a971e631ad4ca5ccf"},
{file = "mysql-connector-python-8.0.32.tar.gz", hash = "sha256:c2d20b29fd096a0633f9360c275bd2434d4bcf597281991c4b7f1c820cd07b84"},
{file = "mysql_connector_python-8.0.32-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4df11c683924ef34c177a54887dc4844ae735b01c8a29ce6ab92d6d3db7a2757"},
{file = "mysql_connector_python-8.0.32-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:4b2d00c9e2cb9e3d11c57ec411226f43aa627607085fbed661cfea1c4dc57f61"},
{file = "mysql_connector_python-8.0.32-cp310-cp310-manylinux1_i686.whl", hash = "sha256:992b7a464daa398e86df8c75f7d8cd6044f884ff9087e782120fc8beff96c638"},
{file = "mysql_connector_python-8.0.32-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:232095f0c36266510009b0f1214d2823a649efb8bc511dbab9ce8847f66ab08a"},
{file = "mysql_connector_python-8.0.32-cp310-cp310-win_amd64.whl", hash = "sha256:fd233c83daaf048c1f9827be984c2721576ae0adf50e139429a06ccd094987d9"},
{file = "mysql_connector_python-8.0.32-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:ab13dd6ede0e0e99ba97c73946462c3420625ab6e63fe13b6fc350e30eb3298d"},
{file = "mysql_connector_python-8.0.32-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:e722b6ffa5b0d7188eebac792b18bc871643db505bf60d0e6bd2859f31e5ed79"},
{file = "mysql_connector_python-8.0.32-cp311-cp311-manylinux1_i686.whl", hash = "sha256:283fe6f647e9d684feb1b7c48fa6a46b1e72c59ecdd6ea2b62392cd80c1a6701"},
{file = "mysql_connector_python-8.0.32-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:1c0a11f3ffbf850f2ca7b39e6c82021e8de910ddaeffd856e53dca028d21c923"},
{file = "mysql_connector_python-8.0.32-cp311-cp311-win_amd64.whl", hash = "sha256:6cdba2779bcd16af0ceff0a6e50d33e6664a83f8d17d70524beb6f677a6d1fae"},
{file = "mysql_connector_python-8.0.32-cp37-cp37m-macosx_12_0_x86_64.whl", hash = "sha256:93b1eb3e07d19a23ccf2605d818aacee0d842b1820bbeef8d0022d8d3d014ab9"},
{file = "mysql_connector_python-8.0.32-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:d6b54656ca131a4f0f17b9d0adddc60f84fd982d64e06360026d5b06e5dbf865"},
{file = "mysql_connector_python-8.0.32-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8c5bfedc979d7858402f39c20d66a6cf03ca4c960732a98318126c278535ddb2"},
{file = "mysql_connector_python-8.0.32-cp37-cp37m-win_amd64.whl", hash = "sha256:bdd716b1e162fe4b3887f6617e9ddcfa659ba96a9ddb22feeae208a72f43d22f"},
{file = "mysql_connector_python-8.0.32-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:bd52a462759aa324a60054c4b44dc8b32007187a328f72be6b58f193d5e32a91"},
{file = "mysql_connector_python-8.0.32-cp38-cp38-manylinux1_i686.whl", hash = "sha256:be82357cc7e7e1377e2f4f8c18aa89c8aab6c0117155cf9fcf18e3cd0eb6ac8e"},
{file = "mysql_connector_python-8.0.32-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1f399f3c2599d2591854cd0e0a24c7c399dff21ac5accb6e52e06924de29f3f4"},
{file = "mysql_connector_python-8.0.32-cp38-cp38-win_amd64.whl", hash = "sha256:c8bba02501525e1fbbba094a6d8d391d1534e8be41be6396c3e1b9f7d9d13b1c"},
{file = "mysql_connector_python-8.0.32-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:145aeb75eefb7425e0a7fb36a4f95ebfe79e06be7c69a4045d34cde95c666dc4"},
{file = "mysql_connector_python-8.0.32-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:c990f4c0702d1739076261c4dece1042e1eb18bf34e0d8516d19ec5166a205ce"},
{file = "mysql_connector_python-8.0.32-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7f7a69db9e0c36764a6c65377f6174aee46e484520e48659e7aa674415b8e192"},
{file = "mysql_connector_python-8.0.32-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:677b5c6dcaec7e2a4bf95b991a869f4d371114f69a0d9a5bb236e988c8f4c376"},
{file = "mysql_connector_python-8.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:8c334c41cd1c5bcfa3550340253ef7d9d3b962211f33327c20f69706a0bcce06"},
{file = "mysql_connector_python-8.0.32-py2.py3-none-any.whl", hash = "sha256:e0299236297b63bf6cbb61d81a9d400bc01cad4743d1abe5296ef349de15ee53"},
]
nodeenv = [
{file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"},
@ -2782,7 +2812,10 @@ orjson = [
{file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"},
{file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"},
{file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"},
{file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"},
{file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"},
{file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"},
{file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"},
{file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"},
{file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"},
{file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"},
@ -2852,30 +2885,28 @@ prompt-toolkit = [
{file = "prompt_toolkit-3.0.31.tar.gz", hash = "sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"},
]
protobuf = [
{file = "protobuf-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996"},
{file = "protobuf-3.20.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"},
{file = "protobuf-3.20.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde"},
{file = "protobuf-3.20.1-cp310-cp310-win32.whl", hash = "sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c"},
{file = "protobuf-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7"},
{file = "protobuf-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153"},
{file = "protobuf-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f"},
{file = "protobuf-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20"},
{file = "protobuf-3.20.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531"},
{file = "protobuf-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e"},
{file = "protobuf-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c"},
{file = "protobuf-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067"},
{file = "protobuf-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf"},
{file = "protobuf-3.20.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab"},
{file = "protobuf-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c"},
{file = "protobuf-3.20.1-cp38-cp38-win32.whl", hash = "sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7"},
{file = "protobuf-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739"},
{file = "protobuf-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7"},
{file = "protobuf-3.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f"},
{file = "protobuf-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9"},
{file = "protobuf-3.20.1-cp39-cp39-win32.whl", hash = "sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8"},
{file = "protobuf-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91"},
{file = "protobuf-3.20.1-py2.py3-none-any.whl", hash = "sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388"},
{file = "protobuf-3.20.1.tar.gz", hash = "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9"},
{file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"},
{file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"},
{file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"},
{file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"},
{file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"},
{file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"},
{file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"},
{file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"},
{file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"},
{file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"},
{file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"},
{file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"},
{file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"},
{file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"},
{file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"},
{file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"},
{file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"},
{file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"},
{file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"},
{file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"},
{file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"},
{file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"},
]
psycopg2 = [
{file = "psycopg2-2.9.4-cp310-cp310-win32.whl", hash = "sha256:8de6a9fc5f42fa52f559e65120dcd7502394692490c98fed1221acf0819d7797"},

View File

@ -27,10 +27,13 @@ flask-marshmallow = "*"
flask-migrate = "*"
flask-restful = "*"
werkzeug = "*"
#SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"}
SpiffWorkflow = {develop = true, path = "/home/dan/code/workflow/SpiffWorkflow" }
# temporarily switch off main to fix CI because poetry export doesn't capture the revision if it's not here (it ignores the lock)
SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"}
# SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" }
sentry-sdk = "^1.10"
sphinx-autoapi = "^2.0"
flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"}
# flask-bpmn = {develop = true, path = "../flask-bpmn"}
mysql-connector-python = "*"
pytest-flask = "^1.2.0"
pytest-flask-sqlalchemy = "^1.1.0"

View File

@ -125,7 +125,11 @@ def create_app() -> flask.app.Flask:
app.json = MyJSONEncoder(app)
if app.config["RUN_BACKGROUND_SCHEDULER"]:
# do not start the scheduler twice in flask debug mode
if (
app.config["RUN_BACKGROUND_SCHEDULER"]
and os.environ.get("WERKZEUG_RUN_MAIN") != "true"
):
start_scheduler(app)
configure_sentry(app)

View File

@ -10,7 +10,7 @@ SPIFFWORKFLOW_BACKEND_LOG_LEVEL = environ.get(
)
RUN_BACKGROUND_SCHEDULER = (
environ.get("RUN_BACKGROUND_SCHEDULER", default="true") == "true"
environ.get("RUN_BACKGROUND_SCHEDULER", default="false") == "true"
)
GIT_CLONE_URL_FOR_PUBLISHING = environ.get(
"GIT_CLONE_URL", default="https://github.com/sartography/sample-process-models.git"

View File

@ -20,11 +20,29 @@ from spiffworkflow_backend.routes.process_api_blueprint import (
_un_modify_modified_process_model_id,
)
from spiffworkflow_backend.services.process_model_service import ProcessModelService
from spiffworkflow_backend.services.process_model_service import (
ProcessModelWithInstancesNotDeletableError,
)
def process_group_create(body: dict) -> flask.wrappers.Response:
"""Add_process_group."""
process_group = ProcessGroup(**body)
if ProcessModelService.is_process_model_identifier(process_group.id):
raise ApiError(
error_code="process_model_with_id_already_exists",
message=f"Process Model with given id already exists: {process_group.id}",
status_code=400,
)
if ProcessModelService.is_process_group_identifier(process_group.id):
raise ApiError(
error_code="process_group_with_id_already_exists",
message=f"Process Group with given id already exists: {process_group.id}",
status_code=400,
)
ProcessModelService.add_process_group(process_group)
_commit_and_push_to_git(
f"User: {g.user.username} added process group {process_group.id}"
@ -35,7 +53,16 @@ def process_group_create(body: dict) -> flask.wrappers.Response:
def process_group_delete(modified_process_group_id: str) -> flask.wrappers.Response:
"""Process_group_delete."""
process_group_id = _un_modify_modified_process_model_id(modified_process_group_id)
ProcessModelService().process_group_delete(process_group_id)
try:
ProcessModelService().process_group_delete(process_group_id)
except ProcessModelWithInstancesNotDeletableError as exception:
raise ApiError(
error_code="existing_instances",
message=str(exception),
status_code=400,
) from exception
_commit_and_push_to_git(
f"User: {g.user.username} deleted process group {process_group_id}"
)
@ -54,6 +81,13 @@ def process_group_update(
}
process_group_id = _un_modify_modified_process_model_id(modified_process_group_id)
if not ProcessModelService.is_process_group_identifier(process_group_id):
raise ApiError(
error_code="process_group_does_not_exist",
message=f"Process Group with given id does not exist: {process_group_id}",
status_code=400,
)
process_group = ProcessGroup(id=process_group_id, **body_filtered)
ProcessModelService.update_process_group(process_group)
_commit_and_push_to_git(

View File

@ -182,9 +182,17 @@ def process_instance_log_list(
)
if not detailed:
log_query = log_query.filter(
or_(
# this was the previous implementation, where we only show completed tasks and skipped tasks.
# maybe we want to iterate on this in the future (in a third tab under process instance logs?)
# or_(
# SpiffLoggingModel.message.in_(["State change to COMPLETED"]), # type: ignore
# SpiffLoggingModel.message.like("Skipped task %"), # type: ignore
# )
and_(
SpiffLoggingModel.message.in_(["State change to COMPLETED"]), # type: ignore
SpiffLoggingModel.message.like("Skipped task %"), # type: ignore
SpiffLoggingModel.bpmn_task_type.in_( # type: ignore
["Default Throwing Event", "End Event", "Default Start Event"]
),
)
)

View File

@ -39,6 +39,9 @@ from spiffworkflow_backend.services.process_instance_report_service import (
ProcessInstanceReportService,
)
from spiffworkflow_backend.services.process_model_service import ProcessModelService
from spiffworkflow_backend.services.process_model_service import (
ProcessModelWithInstancesNotDeletableError,
)
from spiffworkflow_backend.services.spec_file_service import (
ProcessModelFileInvalidError,
)
@ -75,6 +78,24 @@ def process_model_create(
status_code=400,
)
if ProcessModelService.is_process_model_identifier(process_model_info.id):
raise ApiError(
error_code="process_model_with_id_already_exists",
message=(
f"Process Model with given id already exists: {process_model_info.id}"
),
status_code=400,
)
if ProcessModelService.is_process_group_identifier(process_model_info.id):
raise ApiError(
error_code="process_group_with_id_already_exists",
message=(
f"Process Group with given id already exists: {process_model_info.id}"
),
status_code=400,
)
ProcessModelService.add_process_model(process_model_info)
_commit_and_push_to_git(
f"User: {g.user.username} created process model {process_model_info.id}"
@ -91,7 +112,15 @@ def process_model_delete(
) -> flask.wrappers.Response:
"""Process_model_delete."""
process_model_identifier = modified_process_model_identifier.replace(":", "/")
ProcessModelService().process_model_delete(process_model_identifier)
try:
ProcessModelService().process_model_delete(process_model_identifier)
except ProcessModelWithInstancesNotDeletableError as exception:
raise ApiError(
error_code="existing_instances",
message=str(exception),
status_code=400,
) from exception
_commit_and_push_to_git(
f"User: {g.user.username} deleted process model {process_model_identifier}"
)

View File

@ -49,7 +49,7 @@ def script_unit_test_create(
# TODO: move this to an xml service or something
file_contents = SpecFileService.get_data(process_model, file.name)
bpmn_etree_element = etree.fromstring(file_contents)
bpmn_etree_element = SpecFileService.get_etree_from_xml_bytes(file_contents)
nsmap = bpmn_etree_element.nsmap
spiff_element_maker = ElementMaker(

View File

@ -303,7 +303,6 @@ def login_api() -> Response:
def login_api_return(code: str, state: str, session_state: str) -> str:
"""Login_api_return."""
state_dict = ast.literal_eval(base64.b64decode(state).decode("utf-8"))
state_dict["redirect_url"]

View File

@ -692,9 +692,8 @@ class ProcessInstanceProcessor:
):
continue
subprocesses_by_child_task_ids[
task_id
] = subprocesses_by_child_task_ids[subprocess_id]
subprocesses_by_child_task_ids[task_id] =\
subprocesses_by_child_task_ids[subprocess_id]
self.get_highest_level_calling_subprocesses_by_child_task_ids(
subprocesses_by_child_task_ids, task_typename_by_task_id
)
@ -1022,10 +1021,10 @@ class ProcessInstanceProcessor:
data = SpecFileService.get_data(process_model_info, file.name)
try:
if file.type == FileType.bpmn.value:
bpmn: etree.Element = etree.fromstring(data)
bpmn: etree.Element = SpecFileService.get_etree_from_xml_bytes(data)
parser.add_bpmn_xml(bpmn, filename=file.name)
elif file.type == FileType.dmn.value:
dmn: etree.Element = etree.fromstring(data)
dmn: etree.Element = SpecFileService.get_etree_from_xml_bytes(data)
parser.add_dmn_xml(dmn, filename=file.name)
except XMLSyntaxError as xse:
raise ApiError(
@ -1075,9 +1074,13 @@ class ProcessInstanceProcessor:
if bpmn_process_instance.is_completed():
return ProcessInstanceStatus.complete
user_tasks = bpmn_process_instance.get_ready_user_tasks()
waiting_tasks = bpmn_process_instance.get_tasks(TaskState.WAITING)
if len(waiting_tasks) > 0:
return ProcessInstanceStatus.waiting
# if the process instance has status "waiting" it will get picked up
# by background processing. when that happens it can potentially overwrite
# human tasks which is bad because we cache them with the previous id's.
# waiting_tasks = bpmn_process_instance.get_tasks(TaskState.WAITING)
# if len(waiting_tasks) > 0:
# return ProcessInstanceStatus.waiting
if len(user_tasks) > 0:
return ProcessInstanceStatus.user_input_required
else:

View File

@ -26,6 +26,10 @@ from spiffworkflow_backend.services.user_service import UserService
T = TypeVar("T")
class ProcessModelWithInstancesNotDeletableError(Exception):
"""ProcessModelWithInstancesNotDeletableError."""
class ProcessModelService(FileSystemService):
"""ProcessModelService."""
@ -44,7 +48,7 @@ class ProcessModelService(FileSystemService):
return path.replace(os.sep, "/")
@classmethod
def is_group(cls, path: str) -> bool:
def is_process_group(cls, path: str) -> bool:
"""Is_group."""
group_json_path = os.path.join(path, cls.PROCESS_GROUP_JSON_FILE)
if os.path.exists(group_json_path):
@ -52,8 +56,8 @@ class ProcessModelService(FileSystemService):
return False
@classmethod
def is_group_identifier(cls, process_group_identifier: str) -> bool:
"""Is_group_identifier."""
def is_process_group_identifier(cls, process_group_identifier: str) -> bool:
"""Is_process_group_identifier."""
if os.path.exists(FileSystemService.root_path()):
process_group_path = os.path.abspath(
os.path.join(
@ -63,21 +67,21 @@ class ProcessModelService(FileSystemService):
),
)
)
return cls.is_group(process_group_path)
return cls.is_process_group(process_group_path)
return False
@classmethod
def is_model(cls, path: str) -> bool:
"""Is_model."""
def is_process_model(cls, path: str) -> bool:
"""Is_process_model."""
model_json_path = os.path.join(path, cls.PROCESS_MODEL_JSON_FILE)
if os.path.exists(model_json_path):
return True
return False
@classmethod
def is_model_identifier(cls, process_model_identifier: str) -> bool:
"""Is_model_identifier."""
def is_process_model_identifier(cls, process_model_identifier: str) -> bool:
"""Is_process_model_identifier."""
if os.path.exists(FileSystemService.root_path()):
process_model_path = os.path.abspath(
os.path.join(
@ -87,7 +91,7 @@ class ProcessModelService(FileSystemService):
),
)
)
return cls.is_model(process_model_path)
return cls.is_process_model(process_model_path)
return False
@ -129,7 +133,9 @@ class ProcessModelService(FileSystemService):
def save_process_model(cls, process_model: ProcessModelInfo) -> None:
"""Save_process_model."""
process_model_path = os.path.abspath(
os.path.join(FileSystemService.root_path(), process_model.id)
os.path.join(
FileSystemService.root_path(), process_model.id_for_file_path()
)
)
os.makedirs(process_model_path, exist_ok=True)
json_path = os.path.abspath(
@ -150,12 +156,9 @@ class ProcessModelService(FileSystemService):
ProcessInstanceModel.process_model_identifier == process_model_id
).all()
if len(instances) > 0:
raise ApiError(
error_code="existing_instances",
message=(
f"We cannot delete the model `{process_model_id}`, there are"
" existing instances that depend on it."
),
raise ProcessModelWithInstancesNotDeletableError(
f"We cannot delete the model `{process_model_id}`, there are"
" existing instances that depend on it."
)
process_model = self.get_process_model(process_model_id)
path = self.workflow_path(process_model)
@ -196,7 +199,7 @@ class ProcessModelService(FileSystemService):
model_path = os.path.abspath(
os.path.join(FileSystemService.root_path(), process_model_id)
)
if cls.is_model(model_path):
if cls.is_process_model(model_path):
return cls.get_process_model_from_relative_path(process_model_id)
raise ProcessEntityNotFoundError("process_model_not_found")
@ -300,7 +303,7 @@ class ProcessModelService(FileSystemService):
FileSystemService.id_string_to_relative_path(process_group_id),
)
)
if cls.is_group(process_group_path):
if cls.is_process_group(process_group_path):
return cls.find_or_create_process_group(
process_group_path,
find_direct_nested_items=find_direct_nested_items,
@ -348,7 +351,7 @@ class ProcessModelService(FileSystemService):
for _root, dirs, _files in os.walk(group_path):
for dir in dirs:
model_dir = os.path.join(group_path, dir)
if ProcessModelService.is_model(model_dir):
if ProcessModelService.is_process_model(model_dir):
process_model = self.get_process_model(model_dir)
all_nested_models.append(process_model)
return all_nested_models
@ -366,13 +369,10 @@ class ProcessModelService(FileSystemService):
if len(instances) > 0:
problem_models.append(process_model)
if len(problem_models) > 0:
raise ApiError(
error_code="existing_instances",
message=(
f"We cannot delete the group `{process_group_id}`, there are"
" models with existing instances inside the group."
f" {problem_models}"
),
raise ProcessModelWithInstancesNotDeletableError(
f"We cannot delete the group `{process_group_id}`, there are"
" models with existing instances inside the group."
f" {problem_models}"
)
shutil.rmtree(path)
self.cleanup_process_group_display_order()
@ -403,7 +403,7 @@ class ProcessModelService(FileSystemService):
process_groups = []
for item in directory_items:
# if item.is_dir() and not item.name[0] == ".":
if item.is_dir() and cls.is_group(item): # type: ignore
if item.is_dir() and cls.is_process_group(item): # type: ignore
scanned_process_group = cls.find_or_create_process_group(item.path)
process_groups.append(scanned_process_group)
return process_groups
@ -450,12 +450,12 @@ class ProcessModelService(FileSystemService):
for nested_item in nested_items:
if nested_item.is_dir():
# TODO: check whether this is a group or model
if cls.is_group(nested_item.path):
if cls.is_process_group(nested_item.path):
# This is a nested group
process_group.process_groups.append(
cls.find_or_create_process_group(nested_item.path)
)
elif ProcessModelService.is_model(nested_item.path):
elif ProcessModelService.is_process_model(nested_item.path):
process_group.process_models.append(
cls.__scan_process_model(
nested_item.path,

View File

@ -5,6 +5,7 @@ from datetime import datetime
from typing import List
from typing import Optional
from flask_bpmn.models.db import db
from lxml import etree # type: ignore
from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnValidator # type: ignore
@ -93,6 +94,12 @@ class SpecFileService(FileSystemService):
process_model_info, file.name, file_contents
)
@classmethod
def get_etree_from_xml_bytes(cls, binary_data: bytes) -> etree.Element:
"""Get_etree_from_xml_bytes."""
etree_xml_parser = etree.XMLParser(resolve_entities=False)
return etree.fromstring(binary_data, parser=etree_xml_parser)
@classmethod
def get_references_for_file_contents(
cls, process_model_info: ProcessModelInfo, file_name: str, binary_data: bytes
@ -118,13 +125,13 @@ class SpecFileService(FileSystemService):
correlations = {}
start_messages = []
if file_type.value == FileType.bpmn.value:
parser.add_bpmn_xml(etree.fromstring(binary_data))
parser.add_bpmn_xml(cls.get_etree_from_xml_bytes(binary_data))
parser_type = "process"
sub_parsers = list(parser.process_parsers.values())
messages = parser.messages
correlations = parser.correlations
elif file_type.value == FileType.dmn.value:
parser.add_dmn_xml(etree.fromstring(binary_data))
parser.add_dmn_xml(cls.get_etree_from_xml_bytes(binary_data))
sub_parsers = list(parser.dmn_parsers.values())
parser_type = "decision"
else:
@ -172,9 +179,13 @@ class SpecFileService(FileSystemService):
validator = BpmnValidator()
parser = MyCustomParser(validator=validator)
try:
parser.add_bpmn_xml(etree.fromstring(binary_data), filename=file_name)
parser.add_bpmn_xml(
cls.get_etree_from_xml_bytes(binary_data), filename=file_name
)
except Exception as exception:
raise ProcessModelFileInvalidError(str(exception))
raise ProcessModelFileInvalidError(
f"Received error trying to parse bpmn xml: {str(exception)}"
) from exception
@classmethod
def update_file(

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="ManualTaskToCallFromCallActivityToTestWaitingLogs" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_1nxz6rd</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_1nxz6rd" sourceRef="StartEvent_1" targetRef="the_manual_task" />
<bpmn:endEvent id="Event_19yodox">
<bpmn:incoming>Flow_1jtitb1</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1jtitb1" sourceRef="the_manual_task" targetRef="Event_19yodox" />
<bpmn:manualTask id="the_manual_task" name="The Manual Task">
<bpmn:extensionElements>
<spiffworkflow:instructionsForEndUser>NOOOOOOOOOOOOOOOOOO!!!!!!!!!!</spiffworkflow:instructionsForEndUser>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1nxz6rd</bpmn:incoming>
<bpmn:outgoing>Flow_1jtitb1</bpmn:outgoing>
</bpmn:manualTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="ManualTaskToCallFromCallActivityToTestWaitingLogs">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_19yodox_di" bpmnElement="Event_19yodox">
<dc:Bounds x="432" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0tl8vo6_di" bpmnElement="the_manual_task">
<dc:Bounds x="270" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1nxz6rd_di" bpmnElement="Flow_1nxz6rd">
<di:waypoint x="215" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1jtitb1_di" bpmnElement="Flow_1jtitb1">
<di:waypoint x="370" y="177" />
<di:waypoint x="432" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="Process_zywnms5" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0fdzi5f</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0fdzi5f" sourceRef="StartEvent_1" targetRef="call_activity_to_human_task" />
<bpmn:endEvent id="Event_1dzrts2">
<bpmn:incoming>Flow_0ii0wgu</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0ii0wgu" sourceRef="call_activity_to_human_task" targetRef="Event_1dzrts2" />
<bpmn:callActivity id="call_activity_to_human_task" name="Call Activity To Human Task" calledElement="ManualTaskToCallFromCallActivityToTestWaitingLogs">
<bpmn:incoming>Flow_0fdzi5f</bpmn:incoming>
<bpmn:outgoing>Flow_0ii0wgu</bpmn:outgoing>
</bpmn:callActivity>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_zywnms5">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1dzrts2_di" bpmnElement="Event_1dzrts2">
<dc:Bounds x="432" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0qb9821_di" bpmnElement="call_activity_to_human_task">
<dc:Bounds x="270" y="137" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0fdzi5f_di" bpmnElement="Flow_0fdzi5f">
<di:waypoint x="215" y="177" />
<di:waypoint x="270" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0ii0wgu_di" bpmnElement="Flow_0ii0wgu">
<di:waypoint x="370" y="177" />
<di:waypoint x="432" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -0,0 +1 @@
THIS_STRING_SHOULD_NOT_EXIST_ITS_SECRET

View File

@ -0,0 +1,6 @@
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file://{{FULL_PATH_TO_FILE}}"> ]>
<userInfo>
<firstName>John</firstName>
<lastName>&ent;</lastName>
</userInfo>

View File

@ -139,7 +139,7 @@ class BaseTest:
process_group_path = os.path.abspath(
os.path.join(FileSystemService.root_path(), process_group_id)
)
if ProcessModelService.is_group(process_group_path):
if ProcessModelService.is_process_group(process_group_path):
if exception_notification_addresses is None:
exception_notification_addresses = []
@ -173,11 +173,11 @@ class BaseTest:
" model"
)
def get_test_data_file_contents(
def get_test_data_file_full_path(
self, file_name: str, process_model_test_data_dir: str
) -> bytes:
) -> str:
"""Get_test_data_file_contents."""
file_full_path = os.path.join(
return os.path.join(
current_app.instance_path,
"..",
"..",
@ -186,6 +186,14 @@ class BaseTest:
process_model_test_data_dir,
file_name,
)
def get_test_data_file_contents(
self, file_name: str, process_model_test_data_dir: str
) -> bytes:
"""Get_test_data_file_contents."""
file_full_path = self.get_test_data_file_full_path(
file_name, process_model_test_data_dir
)
with open(file_full_path, "rb") as file:
return file.read()
@ -251,9 +259,9 @@ class BaseTest:
There must be an existing process model to instantiate.
"""
if not ProcessModelService.is_model_identifier(test_process_model_id):
if not ProcessModelService.is_process_model_identifier(test_process_model_id):
dirname = os.path.dirname(test_process_model_id)
if not ProcessModelService.is_group_identifier(dirname):
if not ProcessModelService.is_process_group_identifier(dirname):
process_group = ProcessGroup(id=dirname, display_name=dirname)
ProcessModelService.add_process_group(process_group)
basename = os.path.basename(test_process_model_id)

View File

@ -57,7 +57,7 @@ class TestLoggingService(BaseTest):
assert response.status_code == 200
log_response = client.get(
f"/v1.0/logs/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}",
f"/v1.0/logs/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}?detailed=true",
headers=headers,
)
assert log_response.status_code == 200

View File

@ -18,15 +18,15 @@ def test_start_dates_are_one_hour_apart(app: Flask) -> None:
)
group_identifier = os.path.dirname(process_model_identifier)
parent_group_identifier = os.path.dirname(group_identifier)
if not ProcessModelService.is_group(parent_group_identifier):
if not ProcessModelService.is_process_group(parent_group_identifier):
process_group = ProcessGroup(
id=parent_group_identifier, display_name=parent_group_identifier
)
ProcessModelService.add_process_group(process_group)
if not ProcessModelService.is_group(group_identifier):
if not ProcessModelService.is_process_group(group_identifier):
process_group = ProcessGroup(id=group_identifier, display_name=group_identifier)
ProcessModelService.add_process_group(process_group)
if not ProcessModelService.is_model(process_model_identifier):
if not ProcessModelService.is_process_model(process_model_identifier):
process_model = ProcessModelInfo(
id=process_model_identifier,
display_name=process_model_identifier,

View File

@ -0,0 +1,46 @@
"""Test_process_instance_processor."""
import os
from flask.app import Flask
from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.process_instance_processor import (
ProcessInstanceProcessor,
)
class TestProcessInstanceService(BaseTest):
"""TestProcessInstanceService."""
def test_does_not_log_set_data_when_calling_engine_steps_on_waiting_call_activity(
self,
app: Flask,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
) -> None:
"""Test_does_not_log_set_data_when_calling_engine_steps_on_waiting_call_activity."""
tmp_file = '/tmp/testfile.txt'
if os.path.isfile(tmp_file):
os.remove(tmp_file)
process_model = load_test_spec(
process_model_id="test_group/call-activity-to-human-task",
process_model_source_directory="call-activity-to-human-task",
)
process_instance = self.create_process_instance_from_process_model(
process_model=process_model, user=with_super_admin_user
)
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps(save=True)
process_instance_logs = SpiffLoggingModel.query.filter_by(process_instance_id=process_instance.id).all()
initial_length = len(process_instance_logs)
# logs should NOT increase after running this a second time since it's just waiting on a human task
print("HEY NOW")
with open(tmp_file, 'w') as f:
f.write("HEY")
processor.do_engine_steps(save=True)
process_instance_logs = SpiffLoggingModel.query.filter_by(process_instance_id=process_instance.id).all()
assert len(process_instance_logs) == initial_length

View File

@ -1,9 +1,12 @@
"""Test_message_service."""
import os
import sys
import pytest
from flask import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from lxml import etree # type: ignore
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
@ -237,3 +240,29 @@ class TestSpecFileService(BaseTest):
full_file_path = SpecFileService.full_file_path(process_model, "bad_xml.bpmn")
assert not os.path.isfile(full_file_path)
@pytest.mark.skipif(
sys.platform == "win32",
reason="tmp file path is not valid xml for windows and it doesn't matter",
)
def test_does_not_evaluate_entities(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
"""Test_does_not_evaluate_entities."""
string_replacement = b"THIS_STRING_SHOULD_NOT_EXIST_ITS_SECRET"
tmp_file = os.path.normpath(
self.get_test_data_file_full_path("file_to_inject", "xml_with_entity")
)
file_contents = self.get_test_data_file_contents(
"invoice.bpmn", "xml_with_entity"
)
file_contents = (
file_contents.decode("utf-8")
.replace("{{FULL_PATH_TO_FILE}}", tmp_file)
.encode()
)
etree_element = SpecFileService.get_etree_from_xml_bytes(file_contents)
assert string_replacement not in etree.tostring(etree_element)