diff --git a/.github/workflows/conventional-commits-check.yaml b/.github/workflows/conventional-commits-check.yaml new file mode 100644 index 00000000000..72dac2aa85d --- /dev/null +++ b/.github/workflows/conventional-commits-check.yaml @@ -0,0 +1,59 @@ +name: Conventional commits check + +on: + pull_request: + types: [opened, ready_for_review, reopened, synchronize] + +permissions: + contents: read + +jobs: + conventional-commits-check: + name: Check commit messages + + runs-on: self-hosted + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Check for conventional commits + uses: webiny/action-conventional-commits@8bc41ff4e7d423d56fa4905f6ff79209a78776c7 # v1.3.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Includes type with the customization prefix "c/". + # Since all non-letters are removed from the type string + # c/feat would become "cfeat" in the validation process. + allowed-commit-types: "feat,fix,docs,style,refactor,refac,test,build,perf,ci,chore,revert,cfeat,cfix,cdocs,cstyle,crefactor,crefac,ctest,cbuild,cperf,cci,cchore,crevert" + + - name: Check for referenced issue IDs + run: | + if [ -z "${{ github.base_ref }}" ]; then + echo "Not a pull request, don't know the base. Skipped." + exit 0 + fi + + echo "Checking commit range ${from}..${to} for presence of \"Refs:\" ..." + # Debug + git branch -a + + ok=0 + nok=0 + for c in $( git rev-list "${from}..${to}" ); do + if [ -z "$( git log -1 --pretty="SHA=%h %s%n%b" ${c} | grep -E '^Refs:' )" ]; then + git log -1 --pretty="Not OK: %h %s" ${c} + (( nok++ )) + else + (( ok++ )) + fi + done + + echo "${ok} OK, ${nok} not OK" + + if [ ${nok} -ne 0 ]; then + echo 'Add Refs: ; consider using "Refs: -" where not applicable.' + exit 1 + fi + env: + from: refs/heads/${{ github.base_ref }} + to: ${{ github.ref }} diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index ceb276eaf6a..32c5f4fd94e 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -430,5 +430,6 @@ def parse_section(section): # Comma separated list of emails (default: empty) IONOS_ACCOUNT_DELETE_ALLOW_LIST = os.environ.get("IONOS_ACCOUNT_DELETE_ALLOW_LIST", "") -IONOS_USER_ID_PSEUDONYMIZATION_SALT = os.environ.get("IONOS_USER_ID_PSEUDONYMIZATION_SALT", None) +IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX = os.environ.get("IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", None) +IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX = os.environ.get("IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", None) IONOS_SURVEY_NEW_USERS_URL = os.environ.get("IONOS_SURVEY_NEW_USERS_URL", None) diff --git a/backend/open_webui/services/ionos.py b/backend/open_webui/services/ionos.py index f317dd8cc14..937b752e813 100644 --- a/backend/open_webui/services/ionos.py +++ b/backend/open_webui/services/ionos.py @@ -2,7 +2,8 @@ import hashlib from open_webui.models.users import User from open_webui.env import ( - IONOS_USER_ID_PSEUDONYMIZATION_SALT, + IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX, + IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX, ) def get_oauth_sub(user: User) -> str: @@ -32,7 +33,7 @@ def pseudonymized_user_id(user: User) -> Optional[str]: Generate pseudonymized user ID for aggregation in surveys """ - if not IONOS_USER_ID_PSEUDONYMIZATION_SALT: + if not IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX or not IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX: return None if not user.oauth_sub: @@ -40,5 +41,5 @@ def pseudonymized_user_id(user: User) -> Optional[str]: sub = get_oauth_sub(user) - salted = f"{sub}{IONOS_USER_ID_PSEUDONYMIZATION_SALT}" + salted = f"{IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX}{sub}{IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX}" return hashlib.md5(salted.encode("ascii")).hexdigest() diff --git a/backend/open_webui/test/services/ionos_test.py b/backend/open_webui/test/services/ionos_test.py index 88f57e13c38..a06f950654e 100644 --- a/backend/open_webui/test/services/ionos_test.py +++ b/backend/open_webui/test/services/ionos_test.py @@ -27,40 +27,56 @@ def around_tests(self, monkeypatch): """ Cleanup environments """ - monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT", None, raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", None, raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", None, raising = True) yield - monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT", None, raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", None, raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", None, raising = True) def test_pseudonymized_user_id_salt_defined(self, monkeypatch): """ - echo -n "SOMEUUIDsomesalt" | md5sum + echo -n "somesaltprefixSOMEUUIDsomesaltsuffix" | md5sum """ - salt = "somesalt" - monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT", salt, raising = True) + salt_prefix = "somesaltprefix" + salt_suffix = "somesaltsuffix" + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", salt_prefix, raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", salt_suffix, raising = True) oauth_sub = "oidc@SOMEUUID" - expected_hash = "3c810acae41ee75abfae48d53abbcee5" + # Note the nice nerdy start of the hex sequence :) + expected_hash = "f00c899879750f2398c315bf8e89d1a8" assert pseudonymized_user_id(get_mock_user(oauth_sub = oauth_sub)) == expected_hash - def test_pseudonymized_user_id_salt_undefined(self, monkeypatch): + def test_pseudonymized_user_id_salt_prefix_and_suffix_undefined(self, monkeypatch): + assert pseudonymized_user_id(get_mock_user(oauth_sub = "dont-care")) == None + + def test_pseudonymized_user_id_salt_prefix_undefined(self, monkeypatch): + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", "dont-care", raising = True) + assert pseudonymized_user_id(get_mock_user(oauth_sub = "dont-care")) == None + + def test_pseudonymized_user_id_salt_suffix_undefined(self, monkeypatch): + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", "dont-care", raising = True) assert pseudonymized_user_id(get_mock_user(oauth_sub = "dont-care")) == None def test_pseudonymized_user_id_oauth_sub_undefined(self, monkeypatch): - monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT", "dont-care", raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", "dont-care", raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", "dont-care", raising = True) assert pseudonymized_user_id(get_mock_user()) == None def test_pseudonymized_user_id_oauth_sub_malformed(self, monkeypatch): - monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT", "dont-care", raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", "dont-care", raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", "dont-care", raising = True) with pytest.raises(Exception): pseudonymized_user_id(get_mock_user(oauth_sub = 'oidc')) def test_pseudonymized_user_id_oauth_sub_missing_sub(self, monkeypatch): - monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT", "dont-care", raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX", "dont-care", raising = True) + monkeypatch.setattr(open_webui.services.ionos, "IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX", "dont-care", raising = True) with pytest.raises(Exception): pseudonymized_user_id(get_mock_user(oauth_sub = 'oidc@')) diff --git a/docs/IONOS/.env.ionos.sample b/docs/IONOS/.env.ionos.sample index 933bfa65dbb..a4811b49ec5 100644 --- a/docs/IONOS/.env.ionos.sample +++ b/docs/IONOS/.env.ionos.sample @@ -38,5 +38,6 @@ USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ACCESS=true # Set this to http://localhost:3000/auth to see the default Open WebUI login page. IONOS_REGISTRATION_URL=https://localhost:9999/ionos-gpt-shop -IONOS_USER_ID_PSEUDONYMIZATION_SALT=saltysalt +IONOS_USER_ID_PSEUDONYMIZATION_SALT_PREFIX=saltysaltprefix +IONOS_USER_ID_PSEUDONYMIZATION_SALT_SUFFIX=saltysaltsuffix IONOS_SURVEY_NEW_USERS_URL=https:/localhost:9001/survey/47.1