graphene-shield 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- graphene_shield-0.1.0/CHANGELOG.md +61 -0
- graphene_shield-0.1.0/LICENSE +21 -0
- graphene_shield-0.1.0/PKG-INFO +731 -0
- graphene_shield-0.1.0/README.md +683 -0
- graphene_shield-0.1.0/pyproject.toml +95 -0
- graphene_shield-0.1.0/src/graphene_shield/__init__.py +8 -0
- graphene_shield-0.1.0/src/graphene_shield/auth.py +153 -0
- graphene_shield-0.1.0/src/graphene_shield/authentication.py +125 -0
- graphene_shield-0.1.0/src/graphene_shield/conf.py +133 -0
- graphene_shield-0.1.0/src/graphene_shield/connections.py +330 -0
- graphene_shield-0.1.0/src/graphene_shield/files.py +375 -0
- graphene_shield-0.1.0/src/graphene_shield/filters.py +77 -0
- graphene_shield-0.1.0/src/graphene_shield/messages.py +68 -0
- graphene_shield-0.1.0/src/graphene_shield/mutations.py +206 -0
- graphene_shield-0.1.0/src/graphene_shield/pagination.py +271 -0
- graphene_shield-0.1.0/src/graphene_shield/py.typed +0 -0
- graphene_shield-0.1.0/src/graphene_shield/queries.py +186 -0
- graphene_shield-0.1.0/src/graphene_shield/responses.py +50 -0
- graphene_shield-0.1.0/src/graphene_shield/sorting.py +79 -0
- graphene_shield-0.1.0/src/graphene_shield/utils.py +95 -0
- graphene_shield-0.1.0/src/graphene_shield/views.py +103 -0
- graphene_shield-0.1.0/tests/__init__.py +0 -0
- graphene_shield-0.1.0/tests/conftest.py +26 -0
- graphene_shield-0.1.0/tests/schema.py +176 -0
- graphene_shield-0.1.0/tests/settings.py +14 -0
- graphene_shield-0.1.0/tests/test_auth.py +157 -0
- graphene_shield-0.1.0/tests/test_authentication.py +176 -0
- graphene_shield-0.1.0/tests/test_conf.py +10 -0
- graphene_shield-0.1.0/tests/test_files.py +230 -0
- graphene_shield-0.1.0/tests/test_mutations.py +132 -0
- graphene_shield-0.1.0/tests/test_pagination.py +260 -0
- graphene_shield-0.1.0/tests/test_queries.py +170 -0
- graphene_shield-0.1.0/tests/test_views.py +124 -0
- graphene_shield-0.1.0/tests/testapp/__init__.py +0 -0
- graphene_shield-0.1.0/tests/testapp/models.py +33 -0
- graphene_shield-0.1.0/tests/urls.py +10 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-10-24
|
|
4
|
+
|
|
5
|
+
First release, extracted from the GCC backend's `core/base_configure` layer.
|
|
6
|
+
|
|
7
|
+
Fixes compared with the in-house code:
|
|
8
|
+
|
|
9
|
+
- `perform_mutation` exceptions no longer send `str(exc)` to clients by default (`EXPOSE_EXCEPTION_MESSAGES`).
|
|
10
|
+
- `perform_mutation` returning `None`, or not being implemented, now raises an error instead of returning `null`.
|
|
11
|
+
- `GraphQLError`s raised inside mutations (e.g. auth failures) are no longer swallowed into `response`.
|
|
12
|
+
- JWT tokens are decoded once. Expired, bad-signature, wrong-algorithm, wrong-audience and wrong-issuer tokens each get their own code, and an expired token no longer escapes as a raw `ExpiredSignatureError`. The signing key, algorithms, claim names and revocation prefix are configurable. Inactive users are rejected.
|
|
13
|
+
- Pagination limits are enforced with coded GraphQL errors instead of `assert` (which `python -O` removes). Negative `first`/`last`/`offset` values are rejected.
|
|
14
|
+
- Multi-field sort enums (`["created_at", "pk"]`) now order by every field, not only the first.
|
|
15
|
+
- `totalCount` reuses the count already computed for pagination instead of re-evaluating the queryset.
|
|
16
|
+
- `ResponseField.to_dict()` now includes `field` and `errors`. The error view lists every error message, not only the first.
|
|
17
|
+
- Removed the per-node `version` field (it started two `git` subprocesses for every row) and the domain-specific `requestsCount` field (it referenced an undefined `ApprovalStatus`).
|
|
18
|
+
- Filtering on `isActive`/`createdAt`/`updatedAt` for a model without that column now returns `BAD_REQUEST`. The old code silently ignored the filter and returned unfiltered rows. Added `IdFilterInput` for such models, and `common_field_map` to remap column names.
|
|
19
|
+
- Sorting on a field the model lacks now returns `INVALID_SORT_FIELD`. Before, it surfaced as an uncoded, masked error during pagination.
|
|
20
|
+
- File limits are function arguments, not settings: `validate_file(max_file_size=..., allowed_types=...)` and `contains_pdf_active_content(max_scan_bytes=...)`. The defaults (10 MB, PDF only, 1 MB scan) are `files.DEFAULT_MAX_FILE_SIZE`, `DEFAULT_ALLOWED_TYPES` and `DEFAULT_PDF_SCAN_BYTES`.
|
|
21
|
+
- Removed the digital-signature exemption from the PDF active-content check. The old code accepted any PDF containing signature marker text, which anyone could fake by appending `/ByteRange`. Only active content is checked now, so a signed PDF with JavaScript is rejected like any other.
|
|
22
|
+
|
|
23
|
+
Security review fixes:
|
|
24
|
+
|
|
25
|
+
- **PDF active-content check rebuilt.** The old check could be bypassed by writing actions without spaces (`/A<</S/JavaScript/JS(...)>>`), by `#`-escaped names (`/J#61vaScript`), by hiding dictionaries in compressed object streams, or by placing JavaScript in the middle of a file larger than the old 1 MB head + 200 KB tail window. It now parses the PDF with `pypdf` (in the `files` extra), plus a byte scan of the file's dictionaries. Stream bodies are skipped, because random image bytes often look like `/JS `. Files that can't be inspected are rejected. `/XFA`, `/SubmitForm`, `/ImportData`, `/GoToR` and `/GoToE` are now detected too.
|
|
26
|
+
- Macro-enabled Office files (`vbaProject.bin` inside the zip) are rejected instead of passing as DOCX/XLSX/PPTX.
|
|
27
|
+
- HTML/SVG/XML content is no longer accepted as TXT, and libmagic's `text/html` is no longer mapped to TXT.
|
|
28
|
+
- All JPEG variants are recognised (any `FF D8 FF` header). Valid photos were being rejected before.
|
|
29
|
+
- `allowed_types=[]` now allows nothing. Before, it silently fell back to PDF.
|
|
30
|
+
- `perform_mutation` runs in `transaction.atomic()` by default. Before, an exception after a partial write left that data committed.
|
|
31
|
+
- Exceptions raised in `validate_input` are handled like those in `perform_mutation`.
|
|
32
|
+
- New `login_required` mutation attribute authenticates before validation, so anonymous callers can't probe validation results (e.g. "email already registered").
|
|
33
|
+
- Input sanitising keeps `InputObjectType` values intact. Before, they became plain dicts and `data.input.name` broke. It no longer touches values under `password`/`secret`/`token` keys, and no longer HTML-unescapes by default (new `UNESCAPE_HTML_INPUT` setting). Unescaping turned `<script>` into `<script>`, which defeats upstream escaping.
|
|
34
|
+
- JWT: tokens whose `token_type` claim isn't `access` are rejected (`WRONG_TOKEN_TYPE`), so refresh tokens can't be used as access tokens. The `Bearer` scheme is matched case-insensitively. A malformed user id in a token gives `USER_NOT_FOUND` instead of an unhandled `ValidationError`.
|
|
35
|
+
- Pagination: an invalid `after`/`before` cursor is a `BAD_REQUEST`. Before, combining it with `offset` crashed with a `TypeError`. `first: 0` now counts as given for `enforce_first_or_last`.
|
|
36
|
+
- Views: operations deeper than `MAX_QUERY_DEPTH` (default 15) are rejected.
|
|
37
|
+
- An unknown key in `GRAPHENE_SHIELD` raises `ImproperlyConfigured`. Before, a typo such as `EXPOSE_EXCEPTION_MESSAGE` was silently ignored.
|
|
38
|
+
- Requires `hatchling>=1.27` to build (needed for the PEP 639 `license` field).
|
|
39
|
+
|
|
40
|
+
Authentication:
|
|
41
|
+
|
|
42
|
+
- `JWT_AUTH_HEADER_PREFIX` accepts a list, and defaults to `["Bearer", "JWT"]` (any case). `JWT <token>`, the django-graphql-jwt default, used to be rejected. Add `""` to accept a bare token with no prefix.
|
|
43
|
+
- New `JWT_AUTH_HEADER_NAME` setting for tokens sent in another header (e.g. `X-Auth-Token`).
|
|
44
|
+
- New `AUTHENTICATOR` setting and `graphene_shield.authentication` module (no PyJWT needed). `login_required` and the new `@login_required` decorator work with JWT (default), Django sessions (`session_authenticate`), or your own function. Before, `login_required` only understood JWT and rejected users logged in through a session.
|
|
45
|
+
- Using the default JWT authenticator without PyJWT installed raises a clear `ImproperlyConfigured` that names the fix.- `JWT_USER_VALIDATOR` is renamed to `USER_VALIDATOR` and runs for every authenticator (JWT, sessions, custom), once per check. `payload` is `None` for non-JWT authenticators. The old name raises an error that names the new one. If the validator rejects a user, `request.user` is restored, so other fields in the same query don't see that user as logged in.
|
|
46
|
+
|
|
47
|
+
Pagination:
|
|
48
|
+
|
|
49
|
+
- New `PAGINATION` setting and per-field `pagination=` argument with three modes:
|
|
50
|
+
- `"offset"`: the existing Relay connection with `offset`.
|
|
51
|
+
- `"keyset"`: Relay cursors that hold the last row's sort values. It stays fast on deep pages and never skips or repeats rows when data changes.
|
|
52
|
+
- `"page"`: `page`/`pageSize` returning `items` and `pageInfo { page pageSize totalCount totalPages hasNextPage hasPreviousPage }`, plus a `DEFAULT_PAGE_SIZE` setting.
|
|
53
|
+
- `COUNT(*)` only runs when `totalCount`/`totalPages` is selected. Before, forward pages always ran it. With `first`/`after`, `hasPreviousPage` is now `true` after the first page (the Relay spec allows this).
|
|
54
|
+
- Unordered querysets are ordered by primary key before paginating, so pages can't overlap.
|
|
55
|
+
|
|
56
|
+
Performance:
|
|
57
|
+
|
|
58
|
+
- PDF validation is about 17× faster on an 8 MB file (124 ms → 7 ms). Upload content is read once instead of twice. The byte scan locates streams in one linear pass instead of two regex passes, and checks every marker in one search. libmagic only runs when the file signature can't decide the type.
|
|
59
|
+
- Authentication is cached per request. A query with several guarded fields decodes the token, loads the user and runs `USER_VALIDATOR` once instead of once per field (5 fields: 5 user queries → 1). Rejections are cached too; errors that might be temporary are not.
|
|
60
|
+
- The error envelope no longer re-parses successful responses, which are most responses.
|
|
61
|
+
- Hardening: the PDF byte scan only treats `stream` as a stream when it follows a dictionary's `>>`. A name such as `/upstream` can no longer hide the dictionary after it.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 deepak prajapati
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|