From 76cf233bb9616ac821bc82d12025ede356b793bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jan 2022 01:01:31 +0000 Subject: [PATCH 01/21] Bump msgcheck from 3.1 to 4.0.0 Bumps [msgcheck](https://github.com/flashcode/msgcheck) from 3.1 to 4.0.0. - [Release notes](https://github.com/flashcode/msgcheck/releases) - [Changelog](https://github.com/flashcode/msgcheck/blob/master/ChangeLog.md) - [Commits](https://github.com/flashcode/msgcheck/compare/v3.1...v4.0.0) --- updated-dependencies: - dependency-name: msgcheck dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d296f6d..ea5392a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,5 @@ bandit==1.7.1 black==21.12b0 flake8==4.0.1 isort==5.10.1 -msgcheck==3.1 +msgcheck==4.0.0 pydocstyle==6.1.1 From fbedb9468180cb87eea270be9731410841e8c976 Mon Sep 17 00:00:00 2001 From: Rust Saiargaliev Date: Mon, 24 Jan 2022 09:58:49 +0200 Subject: [PATCH 02/21] Drop msgcheck as it was unused (#42) --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ea5392a..69b03e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,4 @@ bandit==1.7.1 black==21.12b0 flake8==4.0.1 isort==5.10.1 -msgcheck==4.0.0 pydocstyle==6.1.1 From 6cb4d9d791a7623dd0e8000eb0b62f1904cc015b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jan 2022 08:37:38 +0100 Subject: [PATCH 03/21] Bump bandit from 1.7.1 to 1.7.2 (#43) Bumps [bandit](https://github.com/PyCQA/bandit) from 1.7.1 to 1.7.2. - [Release notes](https://github.com/PyCQA/bandit/releases) - [Commits](https://github.com/PyCQA/bandit/compare/1.7.1...1.7.2) --- updated-dependencies: - dependency-name: bandit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 69b03e1..f960850 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ Django>=2.2 Wagtail>=2.8 -bandit==1.7.1 +bandit==1.7.2 black==21.12b0 flake8==4.0.1 isort==5.10.1 From 4d2440b3899689635dfcdf3aaed519aa150780d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 01:10:50 +0000 Subject: [PATCH 04/21] Bump black from 21.12b0 to 22.1.0 Bumps [black](https://github.com/psf/black) from 21.12b0 to 22.1.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/commits/22.1.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f960850..5a31d55 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ Django>=2.2 Wagtail>=2.8 bandit==1.7.2 -black==21.12b0 +black==22.1.0 flake8==4.0.1 isort==5.10.1 pydocstyle==6.1.1 From 9a1ad2e1647e8403cc8fa5b5c650991a21cb1459 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Feb 2022 01:08:31 +0000 Subject: [PATCH 05/21] Bump actions/setup-python from 2.3.1 to 2.3.2 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.3.1...v2.3.2) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/release.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index faff9b0..9d0021b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - "pydocstyle ." runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v2.3.1 + - uses: actions/setup-python@v2.3.2 - uses: actions/checkout@v2.4.0 with: cache: 'pip' @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - run: sudo apt install -y gettext - - uses: actions/setup-python@v2.3.1 + - uses: actions/setup-python@v2.3.2 - uses: actions/setup-node@v2.5.1 with: node-version: 'lts/*' @@ -48,7 +48,7 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v2.3.1 + - uses: actions/setup-python@v2.3.2 - uses: actions/checkout@v2.4.0 with: cache: 'pip' @@ -69,7 +69,7 @@ jobs: - "3.10" steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v2.3.2 with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel @@ -90,7 +90,7 @@ jobs: python-version: ["3.10"] steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v2.3.2 with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel @@ -124,7 +124,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v2.3.2 with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffad020..2f622ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2.4.0 - - uses: actions/setup-python@v2.3.1 + - uses: actions/setup-python@v2.3.2 - name: Install Python dependencies run: python -m pip install --upgrade pip setuptools wheel twine - name: Build dist packages From aec86b281a235576b993147ec016b0d779b89d67 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Fri, 25 Feb 2022 10:52:18 +0100 Subject: [PATCH 06/21] Fix Sphinx warning and improve docstrings --- docs/customizing.rst | 2 ++ mailauth/contrib/user/models.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/customizing.rst b/docs/customizing.rst index 9626e68..0feb8ca 100644 --- a/docs/customizing.rst +++ b/docs/customizing.rst @@ -118,7 +118,9 @@ API documentation :members: .. autoattribute:: mailauth.contrib.user.models.AbstractEmailUser.email + :noindex: .. autoattribute:: mailauth.contrib.user.models.AbstractEmailUser.session_salt + :noindex: .. autoclass:: mailauth.contrib.user.models.EmailUser :members: diff --git a/mailauth/contrib/user/models.py b/mailauth/contrib/user/models.py index 42d5749..bb3d6b3 100644 --- a/mailauth/contrib/user/models.py +++ b/mailauth/contrib/user/models.py @@ -51,14 +51,14 @@ class AbstractEmailUser(AbstractUser): password = None email = CIEmailField(_("email address"), unique=True, db_index=True) - """The field is unique and case insensitive to serve as a better username.""" + """Unique and case insensitive to serve as a better username.""" - # Salt for the session hash replacing the password in this function. session_salt = models.CharField( max_length=12, editable=False, default=_get_session_salt, ) + """Salt for the session hash replacing the password in this function.""" def has_usable_password(self): return False From 94c227acf8404a902b7a03a5267142cd05f2ae57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Feb 2022 00:41:07 +0000 Subject: [PATCH 07/21] Bump actions/setup-node from 2.5.1 to 3.0.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2.5.1 to 3.0.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v2.5.1...v3.0.0) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d0021b..7334f07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: steps: - run: sudo apt install -y gettext - uses: actions/setup-python@v2.3.2 - - uses: actions/setup-node@v2.5.1 + - uses: actions/setup-node@v3.0.0 with: node-version: 'lts/*' - uses: actions/checkout@v2.4.0 From 0790c600614ceecedbb5eac89fa76d1b03770ede Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 00:56:36 +0000 Subject: [PATCH 08/21] Bump bandit from 1.7.2 to 1.7.3 Bumps [bandit](https://github.com/PyCQA/bandit) from 1.7.2 to 1.7.3. - [Release notes](https://github.com/PyCQA/bandit/releases) - [Commits](https://github.com/PyCQA/bandit/compare/1.7.2...1.7.3) --- updated-dependencies: - dependency-name: bandit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5a31d55..6a2db3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ Django>=2.2 Wagtail>=2.8 -bandit==1.7.2 +bandit==1.7.3 black==22.1.0 flake8==4.0.1 isort==5.10.1 From 0b4df0133d809dd3161b5967e4ac564030463420 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 00:28:21 +0000 Subject: [PATCH 09/21] Bump actions/setup-python from 2.3.2 to 3 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2.3.2 to 3. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.3.2...v3) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/release.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7334f07..74fdc36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - "pydocstyle ." runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v2.3.2 + - uses: actions/setup-python@v3 - uses: actions/checkout@v2.4.0 with: cache: 'pip' @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - run: sudo apt install -y gettext - - uses: actions/setup-python@v2.3.2 + - uses: actions/setup-python@v3 - uses: actions/setup-node@v3.0.0 with: node-version: 'lts/*' @@ -48,7 +48,7 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v2.3.2 + - uses: actions/setup-python@v3 - uses: actions/checkout@v2.4.0 with: cache: 'pip' @@ -69,7 +69,7 @@ jobs: - "3.10" steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel @@ -90,7 +90,7 @@ jobs: python-version: ["3.10"] steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel @@ -124,7 +124,7 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f622ab..377883b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2.4.0 - - uses: actions/setup-python@v2.3.2 + - uses: actions/setup-python@v3 - name: Install Python dependencies run: python -m pip install --upgrade pip setuptools wheel twine - name: Build dist packages From 7e51cd125ff66d8deca83be6243e5ad616666b03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Mar 2022 01:07:16 +0000 Subject: [PATCH 10/21] Bump bandit from 1.7.3 to 1.7.4 Bumps [bandit](https://github.com/PyCQA/bandit) from 1.7.3 to 1.7.4. - [Release notes](https://github.com/PyCQA/bandit/releases) - [Commits](https://github.com/PyCQA/bandit/compare/1.7.3...1.7.4) --- updated-dependencies: - dependency-name: bandit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6a2db3f..75be7c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ Django>=2.2 Wagtail>=2.8 -bandit==1.7.3 +bandit==1.7.4 black==22.1.0 flake8==4.0.1 isort==5.10.1 From 749249f4a378e4ea7eb6e20635c9c472b9ece792 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 00:38:12 +0000 Subject: [PATCH 11/21] Bump actions/checkout from 2.4.0 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.4.0...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/release.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74fdc36..81bc1e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v3 - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 with: cache: 'pip' cache-dependency-path: 'requirements.txt' @@ -36,7 +36,7 @@ jobs: - uses: actions/setup-node@v3.0.0 with: node-version: 'lts/*' - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - name: Install Python dependencies run: python -m pip install --upgrade pip setuptools wheel twine readme-renderer - run: python setup.py sdist bdist_wheel @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v3 - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 with: cache: 'pip' cache-dependency-path: 'requirements.txt' @@ -73,7 +73,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - run: python setup.py test - name: Codecov run: | @@ -94,7 +94,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - run: python -m pip install -e ".[${{ matrix.extras }}]" - run: python setup.py test - name: Codecov @@ -128,7 +128,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - run: python -m pip install "psycopg2-binary<2.9" Django~=${{ matrix.django-version }} - run: python setup.py test env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 377883b..42e9ab3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3 - uses: actions/setup-python@v3 - name: Install Python dependencies run: python -m pip install --upgrade pip setuptools wheel twine From 5cb101db1f32b2ce604612eb733e5df2d29ddbd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 00:30:15 +0000 Subject: [PATCH 12/21] Bump black from 22.1.0 to 22.3.0 Bumps [black](https://github.com/psf/black) from 22.1.0 to 22.3.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.1.0...22.3.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 75be7c2..b62c63b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ Django>=2.2 Wagtail>=2.8 bandit==1.7.4 -black==22.1.0 +black==22.3.0 flake8==4.0.1 isort==5.10.1 pydocstyle==6.1.1 From 6f2a6079676e4e90c89b841e526a547157103840 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 01:07:31 +0000 Subject: [PATCH 13/21] Bump actions/setup-node from 3.0.0 to 3.1.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.0.0...v3.1.0) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81bc1e2..19f3a44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: steps: - run: sudo apt install -y gettext - uses: actions/setup-python@v3 - - uses: actions/setup-node@v3.0.0 + - uses: actions/setup-node@v3.1.0 with: node-version: 'lts/*' - uses: actions/checkout@v3 From 746ccaaa05950459fcef44ef15d2e6ff55fa46b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Apr 2022 00:32:23 +0000 Subject: [PATCH 14/21] Bump actions/setup-node from 3.1.0 to 3.1.1 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.1.0...v3.1.1) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19f3a44..cc65f22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: steps: - run: sudo apt install -y gettext - uses: actions/setup-python@v3 - - uses: actions/setup-node@v3.1.0 + - uses: actions/setup-node@v3.1.1 with: node-version: 'lts/*' - uses: actions/checkout@v3 From 8d3634eef5f2486fab0c1dd2cf414abcf717b407 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 01:06:59 +0000 Subject: [PATCH 15/21] Bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc65f22..a0e4473 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: run: python -m pip install --upgrade pip setuptools wheel twine readme-renderer - run: python setup.py sdist bdist_wheel - run: python -m twine check dist/* - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: dist/* From d1c1aa851855dde0839cdebd9699225121ef3d0d Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 14 Apr 2022 16:22:13 +0200 Subject: [PATCH 16/21] Drop Django EOL versions from CI suite --- .github/workflows/ci.yml | 6 ++---- setup.cfg | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0e4473..0322e24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,9 +110,7 @@ jobs: matrix: python-version: ["3.10"] django-version: - - "2.2a1" - - "3.2a1" - - "4.0a1" + - "4.0" services: postgres: image: postgres @@ -129,7 +127,7 @@ jobs: python-version: ${{ matrix.python-version }} - run: python -m pip install --upgrade pip setuptools wheel - uses: actions/checkout@v3 - - run: python -m pip install "psycopg2-binary<2.9" Django~=${{ matrix.django-version }} + - run: python -m pip install "psycopg2-binary<2.9" Django~=${{ matrix.django-version }}a - run: python setup.py test env: DB_PORT: ${{ job.services.postgres.ports[5432] }} diff --git a/setup.cfg b/setup.cfg index 89a5c8b..90b3b53 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,8 +21,6 @@ classifier = Topic :: Internet :: WWW/HTTP Topic :: Internet Framework :: Django - Framework :: Django :: 2.2 - Framework :: Django :: 3.2 Framework :: Django :: 4.0 keywords = django, otp, password, email From 7c4e79bf5232a470113a70a636d94d52f0d36c04 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 14 Apr 2022 16:25:21 +0200 Subject: [PATCH 17/21] Fix bandit in CI suite --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0322e24..618c1ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: lint-command: - - "bandit -r hijack -x hijack/tests" + - "bandit -r mailauth -x tests" - "black --check --diff ." - "flake8 ." - "isort --check-only --diff ." From b6562bbe89ea6dd6b4c95560fad983b5e56f5c06 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 14 Apr 2022 16:27:37 +0200 Subject: [PATCH 18/21] Mute secruity warning --- mailauth/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mailauth/views.py b/mailauth/views.py index ecc24c5..c9d8dc0 100644 --- a/mailauth/views.py +++ b/mailauth/views.py @@ -43,7 +43,7 @@ def get_initial(self): } -INTERNAL_LOGIN_URL_TOKEN = "login-token" +INTERNAL_LOGIN_URL_TOKEN = "login-token" # nosec class LoginTokenView(DjangoLoginView): From 2b16b8c5d3417a864f30ef5e454f00961e0b0de7 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 14 Apr 2022 16:27:57 +0200 Subject: [PATCH 19/21] Fix typo --- mailauth/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mailauth/views.py b/mailauth/views.py index c9d8dc0..c82cc2c 100644 --- a/mailauth/views.py +++ b/mailauth/views.py @@ -47,7 +47,7 @@ def get_initial(self): class LoginTokenView(DjangoLoginView): - """Authenticate a user via a access token.""" + """Authenticate a user via an access token.""" redirect_field_name = REDIRECT_FIELD_NAME From afa829f1f885aaa03edc1da777d72ee75873352b Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Thu, 14 Apr 2022 16:47:23 +0200 Subject: [PATCH 20/21] Remove Django EOL version copat code --- mailauth/contrib/admin/__init__.py | 4 ---- mailauth/contrib/user/__init__.py | 4 ---- mailauth/contrib/wagtail/__init__.py | 4 ---- mailauth/signing.py | 17 +++-------------- setup.cfg | 2 +- tests/conftest.py | 3 --- tests/contrib/auth/test_models.py | 4 ---- tests/testapp/settings.py | 5 ----- 8 files changed, 4 insertions(+), 39 deletions(-) diff --git a/mailauth/contrib/admin/__init__.py b/mailauth/contrib/admin/__init__.py index f811b22..e69de29 100644 --- a/mailauth/contrib/admin/__init__.py +++ b/mailauth/contrib/admin/__init__.py @@ -1,4 +0,0 @@ -import django - -if django.VERSION < (4, 0): - default_app_config = "mailauth.contrib.admin.apps.MailAuthAdmin" diff --git a/mailauth/contrib/user/__init__.py b/mailauth/contrib/user/__init__.py index f6b481d..e69de29 100644 --- a/mailauth/contrib/user/__init__.py +++ b/mailauth/contrib/user/__init__.py @@ -1,4 +0,0 @@ -import django - -if django.VERSION < (4, 0): - default_app_config = "mailauth.contrib.user.apps.AuthConfig" diff --git a/mailauth/contrib/wagtail/__init__.py b/mailauth/contrib/wagtail/__init__.py index 8e775e4..e69de29 100644 --- a/mailauth/contrib/wagtail/__init__.py +++ b/mailauth/contrib/wagtail/__init__.py @@ -1,4 +0,0 @@ -import django - -if django.VERSION < (4, 0): - default_app_config = "mailauth.contrib.wagtail.apps.MailAuthWagtail" diff --git a/mailauth/signing.py b/mailauth/signing.py index b492f79..ff174ef 100644 --- a/mailauth/signing.py +++ b/mailauth/signing.py @@ -1,17 +1,6 @@ -import django from django.contrib.auth import get_user_model from django.core import signing -if django.VERSION >= (4, 0): - b62_encode = signing.b62_encode - b62_decode = signing.b62_decode -else: - from django.utils import baseconv - - b62_encode = baseconv.base62.encode - b62_decode = baseconv.base62.decode - - __all__ = ( "UserDoesNotExist", "UserSigner", @@ -39,7 +28,7 @@ def to_timestamp(value): """ if value is None: return "" - return b62_encode(int(value.timestamp())) + return signing.b62_encode(int(value.timestamp())) def sign(self, user): """ @@ -60,7 +49,7 @@ def sign(self, user): def _make_hash_value(self, user): last_login = self.to_timestamp(user.last_login) - user_pk = b62_encode(user.pk) + user_pk = signing.b62_encode(user.pk) return self.sep.join((user_pk, last_login)) def unsign(self, value, max_age=None, single_use=True): @@ -94,7 +83,7 @@ def unsign(self, value, max_age=None, single_use=True): """ result = super().unsign(value, max_age=max_age) user_pk, last_login = result.rsplit(self.sep, 2) - user_pk = b62_decode(user_pk) + user_pk = signing.b62_decode(user_pk) try: user = get_user_model()._default_manager.get(pk=user_pk) except get_user_model().DoesNotExist as e: diff --git a/setup.cfg b/setup.cfg index 90b3b53..00a18d0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ keywords = django, otp, password, email include_package_data = True packages = find: install_requires = - django>=2.2 + django>=4.0 setup_requires = setuptools_scm sphinx diff --git a/tests/conftest.py b/tests/conftest.py index 532a7f9..9194bfc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -import django import pytest from django.contrib.auth import get_user_model from django.utils import timezone @@ -37,8 +36,6 @@ def admin_user(db): @pytest.fixture() def signature(): """Return a signature matching the user fixture.""" - if django.VERSION < (3, 1): - return "LZ:173QUS:1Hjptg:umUR9iKN1rxDezT-dZGwqcqsM5Y" return "LZ:173QUS:1Hjptg:6oq5DS1NJ7SxJ1o-CpfgaqrImVaRpkcHrzV9yltwcHM" diff --git a/tests/contrib/auth/test_models.py b/tests/contrib/auth/test_models.py index 4a003cb..f757d1b 100644 --- a/tests/contrib/auth/test_models.py +++ b/tests/contrib/auth/test_models.py @@ -1,4 +1,3 @@ -import django import pytest from django.core.exceptions import FieldDoesNotExist @@ -29,14 +28,12 @@ def test_get_session_auth_hash__unique(self, db): assert spiderman.get_session_auth_hash() != ironman.get_session_auth_hash() - @pytest.mark.skipif(django.VERSION < (3, 1), reason="requires Django 3.1 or higher") def test_legacy_get_session_auth_hash__default(self, db): user = EmailUser(email="spiderman@avengers.com") assert user.session_salt assert user._legacy_get_session_auth_hash() - @pytest.mark.skipif(django.VERSION < (3, 1), reason="requires Django 3.1 or higher") def test_legacy_get_session_auth_hash__value_error(self, db): user = EmailUser(email="spiderman@avengers.com", session_salt=None) @@ -45,7 +42,6 @@ def test_legacy_get_session_auth_hash__value_error(self, db): assert "'session_salt' must be set" in str(e.value) - @pytest.mark.skipif(django.VERSION < (3, 1), reason="requires Django 3.1 or higher") def test_legacy_get_session_auth_hash__unique(self, db): spiderman = EmailUser(email="spiderman@avengers.com") ironman = EmailUser(email="ironman@avengers.com") diff --git a/tests/testapp/settings.py b/tests/testapp/settings.py index bacba2d..75329b0 100644 --- a/tests/testapp/settings.py +++ b/tests/testapp/settings.py @@ -13,7 +13,6 @@ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import django BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -125,10 +124,6 @@ TIME_ZONE = "UTC" USE_I18N = True - -if django.VERSION < (4, 0): - USE_L10N = True - USE_TZ = True From 51038c27cd8144ebd32e377860449471bce5175e Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Fri, 25 Feb 2022 16:40:32 +0100 Subject: [PATCH 21/21] Add anonymization feature for private user data --- docs/index.rst | 1 + docs/privacy.rst | 40 +++++++++++ .../admin/locale/de/LC_MESSAGES/django.po | 4 +- mailauth/contrib/user/admin.py | 42 ++++++++++- .../user/locale/de/LC_MESSAGES/django.po | 35 +++++++++ ...iluser_email_hash_alter_emailuser_email.py | 38 ++++++++++ mailauth/contrib/user/models.py | 34 ++++++++- mailauth/contrib/user/signals.py | 19 +++++ tests/contrib/auth/test_admin.py | 71 +++++++++++++++++++ tests/contrib/auth/test_signals.py | 20 ++++++ tests/test_models.py | 19 +++++ 11 files changed, 318 insertions(+), 5 deletions(-) create mode 100644 docs/privacy.rst create mode 100644 mailauth/contrib/user/locale/de/LC_MESSAGES/django.po create mode 100644 mailauth/contrib/user/migrations/0005_emailuser_email_hash_alter_emailuser_email.py create mode 100644 mailauth/contrib/user/signals.py create mode 100644 tests/contrib/auth/test_admin.py create mode 100644 tests/contrib/auth/test_signals.py diff --git a/docs/index.rst b/docs/index.rst index 70a773f..0a08e0a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ All Contents usage templates + privacy customizing settings contributing diff --git a/docs/privacy.rst b/docs/privacy.rst new file mode 100644 index 0000000..e180d4b --- /dev/null +++ b/docs/privacy.rst @@ -0,0 +1,40 @@ +Privacy +======== + +Anonymization +------------- + +User privacy is important, not only to meet local regulations, but also to +protect your users and allow them to exercise their rights. However, +it's not always practical to delete users, especially if they have dependent +objects, that are relevant for statistical analysis. + +Anonymization is a process of removing the user's personal data whilst keeping +related data intact. This is done by using the ``anomymize`` method. + + + +.. automethod:: mailauth.contrib.user.models.AbstractEmailUser.anonymize + :noindex: + +This method may be overwritten to provide anonymization for you custom user model. + +Related objects may also listen to the anonymize signal. + +.. autoclass:: mailauth.contrib.user.signals.anonymize + +All those methods can be conveniently triggered via the ``anonymize`` admin action. + +.. autoclass:: mailauth.contrib.user.admin.AnonymizableAdminMixin + :members: + +Liability Waiver +---------------- + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mailauth/contrib/admin/locale/de/LC_MESSAGES/django.po b/mailauth/contrib/admin/locale/de/LC_MESSAGES/django.po index 9fd6104..dd37b9a 100644 --- a/mailauth/contrib/admin/locale/de/LC_MESSAGES/django.po +++ b/mailauth/contrib/admin/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-04-12 18:14+0200\n" +"POT-Creation-Date: 2022-04-14 16:57+0200\n" "PO-Revision-Date: 2019-04-12 18:15+0200\n" "Last-Translator: Johannes Hoppe \n" "Language-Team: \n" @@ -65,6 +65,6 @@ msgstr "" msgid "Resend login email" msgstr "Login E-Mail erneut senden" -#: views.py:16 +#: views.py:17 msgid "Log in" msgstr "Anmelden" diff --git a/mailauth/contrib/user/admin.py b/mailauth/contrib/user/admin.py index 217e9c0..a3ac665 100644 --- a/mailauth/contrib/user/admin.py +++ b/mailauth/contrib/user/admin.py @@ -1,12 +1,50 @@ from django.contrib import admin from django.contrib.auth.models import Group, Permission +from django.utils.translation import gettext_lazy as _, ngettext from . import models +class AnonymizableAdminMixin: + """ + Mixin for admin classes that provides a `anonymize` action. + + This mixin calls the `anonymize` method of all user model instances. + """ + + actions = ["anonymize"] + + @admin.action( + permissions=["anonymize"], + description=_("Anonymize selected %(verbose_name_plural)s"), + ) + def anonymize(self, request, queryset): + count = queryset.count() + for user in queryset.iterator(): + user.anonymize() + + self.message_user( + request, + ngettext( + "%(count)s %(obj_name)s has successfully been anonymized.", + "%(count)s %(obj_name)s have successfully been anonymized.", + count, + ) + % { + "count": count, + "obj_name": self.model._meta.verbose_name_plural + if count > 1 + else self.model._meta.verbose_name, + }, + fail_silently=True, + ) + + def has_anonymize_permission(self, request, obj=None): + return request.user.has_perm(f"{self.opts.app_label}.anonymize", obj=obj) + + @admin.register(models.EmailUser) -class EmailUserAdmin(admin.ModelAdmin): - app_label = "asdf" +class EmailUserAdmin(AnonymizableAdminMixin, admin.ModelAdmin): list_display = ("email", "first_name", "last_name", "is_staff") list_filter = ("is_staff", "is_superuser", "is_active", "groups") search_fields = ("first_name", "last_name", "email") diff --git a/mailauth/contrib/user/locale/de/LC_MESSAGES/django.po b/mailauth/contrib/user/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..fce2313 --- /dev/null +++ b/mailauth/contrib/user/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,35 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Johannes Hoppe , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-04-14 16:56+0200\n" +"PO-Revision-Date: 2022-04-14 15:38+0200\n" +"Last-Translator: Johannes Maron \n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.0.1\n" + +#: admin.py:19 +#, python-format +msgid "Anonymize selected %(verbose_name_plural)s" +msgstr "Ausgewählte %(verbose_name_plural)s anonymisieren" + +#: admin.py:29 +#, python-format +msgid "%(count)s %(obj_name)s has successfully been anonymized." +msgid_plural "%(count)s %(obj_name)s have successfully been anonymized." +msgstr[0] "%(count)s %(obj_name)s wurde erfolgreich anonymisiert." +msgstr[1] "%(count)s %(obj_name)s wurden erfolgreich anonymisiert." + +#: models.py:56 +msgid "email address" +msgstr "E-Mail-Adresse" diff --git a/mailauth/contrib/user/migrations/0005_emailuser_email_hash_alter_emailuser_email.py b/mailauth/contrib/user/migrations/0005_emailuser_email_hash_alter_emailuser_email.py new file mode 100644 index 0000000..c9220c3 --- /dev/null +++ b/mailauth/contrib/user/migrations/0005_emailuser_email_hash_alter_emailuser_email.py @@ -0,0 +1,38 @@ +from django.db import migrations, models + +try: + from django.contrib.postgres.fields import CIEmailField +except ImportError: + CIEmailField = models.EmailField + + +class Migration(migrations.Migration): + + dependencies = [ + ("mailauth_user", "0004_auto_20200812_0722"), + ] + + operations = [ + # add new permissions + migrations.AlterModelOptions( + name="emailuser", + options={ + "permissions": [("anonymize", "Can anonymize user")], + "verbose_name": "user", + "verbose_name_plural": "users", + }, + ), + # email is now nullable + migrations.AlterField( + model_name="emailuser", + name="email", + field=CIEmailField( + blank=True, + db_index=True, + max_length=254, + null=True, + unique=True, + verbose_name="email address", + ), + ), + ] diff --git a/mailauth/contrib/user/models.py b/mailauth/contrib/user/models.py index bb3d6b3..fc310c4 100644 --- a/mailauth/contrib/user/models.py +++ b/mailauth/contrib/user/models.py @@ -5,6 +5,8 @@ from django.utils.crypto import get_random_string, salted_hmac from django.utils.translation import gettext_lazy as _ +from . import signals + try: from django.contrib.postgres.fields import CIEmailField except ImportError: @@ -50,7 +52,9 @@ class AbstractEmailUser(AbstractUser): username = None password = None - email = CIEmailField(_("email address"), unique=True, db_index=True) + email = CIEmailField( + _("email address"), blank=True, null=True, unique=True, db_index=True + ) """Unique and case insensitive to serve as a better username.""" session_salt = models.CharField( @@ -67,6 +71,9 @@ def has_usable_password(self): class Meta(AbstractUser.Meta): abstract = True + permissions = [ + ("anonymize", "Can anonymize user"), + ] def _legacy_get_session_auth_hash(self): # RemovedInDjango40Warning: pre-Django 3.1 hashes will be invalid. @@ -92,6 +99,31 @@ def get_session_auth_hash(self): algorithm=algorithm, ).hexdigest() + def anonymize(self, commit=True): + """ + Anonymize the user data for privacy purposes. + + This method will erase the email address, first and last name. + You may overwrite this method to add additional fields to anonymize:: + + class MyUser(AbstractEmailUser): + def anonymize(self, commit=True): + super().anonymize(commit=False) # do not commit yet + self.phone_number = None + if commit: + self.save() + """ + self.email = None + self.first_name = "" + self.last_name = "" + update_fields = ["email", "first_name", "last_name"] + if commit: + self.save(update_fields=update_fields) + signals.anonymize.send( + sender=self.__class__, instance=self, update_fields=tuple(update_fields) + ) + return update_fields + delattr(AbstractEmailUser, "password") diff --git a/mailauth/contrib/user/signals.py b/mailauth/contrib/user/signals.py new file mode 100644 index 0000000..3bfa2a1 --- /dev/null +++ b/mailauth/contrib/user/signals.py @@ -0,0 +1,19 @@ +from django.dispatch import Signal + +anonymize = Signal() +""" +Signal that is emitted when a user and all their data should be anonymized. + +Usage:: + + from django.dispatch import receiver + from mailauth.contrib.user.models import EmailUser + from mailauth.contrib.user.signals import anonymize + + + @receiver(anonymize, sender=EmailUser) + def anonymize_user(sender, instance, update_fields, **kwargs): + # Do something with related user data + instance.related_model.delete() + +""" diff --git a/tests/contrib/auth/test_admin.py b/tests/contrib/auth/test_admin.py new file mode 100644 index 0000000..35ada72 --- /dev/null +++ b/tests/contrib/auth/test_admin.py @@ -0,0 +1,71 @@ +from unittest.mock import Mock + +import pytest +from django.contrib import admin +from django.contrib.auth.models import Permission + +from mailauth.contrib.user.admin import AnonymizableAdminMixin +from mailauth.contrib.user.models import EmailUser + + +class TestAnonymizableAdminMixin: + def test_anonymize__none(self, rf): + class MyUserModel(EmailUser): + class Meta: + app_label = "test" + verbose_name = "singular" + verbose_name_plural = "plural" + + class MyModelAdmin(AnonymizableAdminMixin, admin.ModelAdmin): + pass + + request = rf.get("/") + MyModelAdmin(MyUserModel, admin.site).anonymize( + request, MyUserModel.objects.none() + ) + + @pytest.mark.django_db + def test_anonymize__one(self, rf, user, monkeypatch): + class MyModelAdmin(AnonymizableAdminMixin, admin.ModelAdmin): + pass + + monkeypatch.setattr(EmailUser, "anonymize", Mock()) + + request = rf.get("/") + MyModelAdmin(type(user), admin.site).anonymize( + request, type(user).objects.all() + ) + assert EmailUser.anonymize.was_called_once_with(user) + + @pytest.mark.django_db + def test_anonymize__many(self, rf, user, monkeypatch): + class MyModelAdmin(AnonymizableAdminMixin, admin.ModelAdmin): + pass + + monkeypatch.setattr(EmailUser, "anonymize", Mock()) + + request = rf.get("/") + MyModelAdmin(type(user), admin.site).anonymize( + request, type(user).objects.all() + ) + assert EmailUser.anonymize.was_called_once_with(user) + + def test_has_anonymize_permission(self, rf, user): + class MyModelAdmin(AnonymizableAdminMixin, admin.ModelAdmin): + pass + + user.is_staff = True + user.save() + request = rf.get("/") + request.user = user + assert not MyModelAdmin(type(user), admin.site).has_anonymize_permission( + request + ) + + permission = Permission.objects.get( + codename="anonymize", + ) + user.user_permissions.add(permission) + del user._perm_cache + del user._user_perm_cache + assert MyModelAdmin(type(user), admin.site).has_anonymize_permission(request) diff --git a/tests/contrib/auth/test_signals.py b/tests/contrib/auth/test_signals.py new file mode 100644 index 0000000..ef8c280 --- /dev/null +++ b/tests/contrib/auth/test_signals.py @@ -0,0 +1,20 @@ +from unittest.mock import Mock + +import pytest +from django.dispatch import receiver + +from mailauth.contrib.user.signals import anonymize + + +@pytest.mark.django_db +def test_anonymize(user): + handler = Mock() + receiver(anonymize, sender=user.__class__)(handler) + handler.assert_not_called() + user.anonymize() + handler.assert_called_once_with( + signal=anonymize, + sender=user.__class__, + instance=user, + update_fields=("email", "first_name", "last_name"), + ) diff --git a/tests/test_models.py b/tests/test_models.py index 439fcd1..06144e9 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -20,3 +20,22 @@ def test_email__ci_unique(self, db): models.EmailUser.objects.create_user("IronMan@avengers.com") with pytest.raises(IntegrityError): models.EmailUser.objects.create_user("ironman@avengers.com") + + @pytest.mark.django_db + def test_anonymize(self): + user = models.EmailUser.objects.create_user( + email="ironman@avengers.com", first_name="Tony", last_name="Stark" + ) + assert user.anonymize() == ["email", "first_name", "last_name"] + assert not user.first_name + assert not user.last_name + assert not user.email + + def test_anonymize__no_commit(self): + user = models.EmailUser( + email="ironman@avengers.com", first_name="Tony", last_name="Stark" + ) + user.anonymize(commit=False) + assert not user.first_name + assert not user.last_name + assert not user.email