Skip to content

Feature/add user business access clean - #432

Open
delcroip wants to merge 49 commits into
developfrom
feature/add-user-business-access-clean
Open

Feature/add user business access clean#432
delcroip wants to merge 49 commits into
developfrom
feature/add-user-business-access-clean

Conversation

@delcroip

@delcroip delcroip commented Jun 30, 2026

Copy link
Copy Markdown
Member

Description

cleaned version of the feature/add-user-business-access PR without the inpersonification and GQL error handling

Type of Change

  • Feature
  • Bug fix: better user managements regarding CA and EO
  • Chore (Refactor, Docs, CI/CD)
  • Other, please specify

Related Issue(s) / Task(s)

  • Requires [link to github PR], [link to github PR] needs to be merged first before this one
  • Relates to [link to github PR], this needs to be merged before [link to github PR]
  • External reference (e.g., Jira):

Demo

Upload screenshots/gifs or link to any demo video here.

Checklist

  • Unit tests added/modified
  • I18n / translation handled

zikani03 and others added 30 commits June 29, 2026 16:16
…ss and ValidityMixin

- Added UserBusinessAccess model with admin registration for user-specific business access control
- Imported and exported ValidityMixin, OpenIMISBusinessModel, and UserBusinessAccess in models init
- Removed HistoryBusinessModel class from history_model.py, likely moved to openimis_model for better separation of concerns
- This refactoring improves code organization by centralizing business-related models and mixins, enhancing maintainability and modularity in the OpenIMIS core module.
… model

- Updated uuid fields in HistoricalInteractiveUser and InteractiveUser models to use uuidv7 with db_index and unique constraints for better UUID handling.
- Added new HistoricalUserBusinessAccess model to enable historical tracking of UserBusinessAccess changes, including fields for versioning, validity dates, and audit trails.
- Renamed migration file and updated dependency to follow admin user migration sequence
- Added check_permissions import to utils module for enhanced permission validation
- Modified test helper to separately handle staff and superuser flags for technical users, improving role management in tests
…ixin

Add getter and setter properties for date_valid_to and date_valid_from in the ValidityMixin class to facilitate migration from versioned models by providing backward-compatible attribute names.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
… Django User model

- Changed "staff" to "is_staff" and "superuser" to "is_superuser" to align with Django's User model attributes, ensuring proper test user creation.
…ion methods

- Extracted auto-provisioning user group logic into a dedicated `_auto_provisioning_user_group` method to reduce duplication.
- Added `_create_interactive_user` method to handle interactive user creation separately from tech users.
- Updated `create_user` and `create_superuser` to use the new interactive user creation and auto-provisioning flow.
- Refactored `auto_provision_user` to call the extracted method, improving code readability and maintainability.
- Add @staticmethod decorator to id_from_global method in GraphQL test case
- Modify InteractiveUser.is_superuser property and add setter for better user management
- Refactor User.has_perms method to support business object permissions via UserBusinessAccess model
- Include detailed docstring explaining the new permission evaluation logic with OR/AND support and time-bound access checks

This improves fine-grained access control in openIMIS by allowing permissions tied to specific business objects, enhancing security and flexibility for user rights management.
Add a new Django management command `generate_permissions_map` that collects all GraphQL permissions, generates a sorted permissions map JSON file, and optionally syncs the Django Permission model with these permissions. This improves automation of permission management in the openIMIS backend by ensuring consistency between GQL-defined permissions and Django's permission system.
Include the is_superuser boolean in the UserSerializer fields to expose superuser status in API responses, enhancing user data visibility for administrative purposes.
…output and cleaner operation names

- Change default output path from absolute '../../solution-builder/solution/permissions_map.json' to relative './permissions_map.json' for better portability
- Strip 'mutation_' and 'query_' prefixes from operation names to simplify permission mappings and improve readability
- Refactored UserManager methods to properly handle language assignment and return user objects from auto_provision_user
- Improved create_test_interactive_user to filter properties correctly for User and InteractiveUser models
- Added _instance_to_gql_input method in openIMISGraphQLTestCase for converting model instances to GraphQL inputs with camelCase fields
- Minor formatting and import additions for better code consistency and functionality
…ation

- Added app_label = 'core' to Meta classes in Role, RoleRight, InteractiveUser, Officer, ClaimAdmin, UserRole, UserGroup, and RoleMutation models
- Ensures models are correctly associated with the 'core' app, preventing potential issues in Django migrations and app registry
Replaced the `exclude` list parameter with a `map_mutation_field` dict. This allows renaming fields (e.g. uuid→i_user_id), conditional exclusion via special "_exclude_" value, and more flexible conversion of model instances to GraphQL mutation inputs.

Updated impersonation tests to use the new helper, supply UUID-based clientMutationId, pass userTypes, verify mutation status via get_mutation_result, and create test users with custom_props.
Use data.get("roles", []) instead of data["roles"] in
create_or_update_interactive_user to prevent KeyError when the
roles key is missing from the input. Improves robustness of
user creation/update flow.
…ss and ValidityMixin

- Added UserBusinessAccess model with admin registration for user-specific business access control
- Imported and exported ValidityMixin, OpenIMISBusinessModel, and UserBusinessAccess in models init
- Removed HistoryBusinessModel class from history_model.py, likely moved to openimis_model for better separation of concerns
- This refactoring improves code organization by centralizing business-related models and mixins, enhancing maintainability and modularity in the OpenIMIS core module.
… model

- Updated uuid fields in HistoricalInteractiveUser and InteractiveUser models to use uuidv7 with db_index and unique constraints for better UUID handling.
- Added new HistoricalUserBusinessAccess model to enable historical tracking of UserBusinessAccess changes, including fields for versioning, validity dates, and audit trails.
Introduce a new `mutation_on_queryset_from_filter` decorator as a
complement to `mutation_on_uuids_from_filter`. Instead of materializing
a list of UUIDs from filtered results, the new decorator operates
directly on a lazy Django queryset, avoiding unnecessary DB round-trips
when mutation logic can work with a queryset directly.

Key behaviors:
- Applies JSON-serialized filters from `query_filters_field` as Django
  Q objects using the GQL type's `filter_fields` plus any explicit
  filter handlers.
- If a queryset is already present in `data` under `queryset_key`, it
  further narrows it with the mutation filters, allowing callers to
  pre-apply row security or custom base filters.
- If no queryset is provided, it falls back to
  `django_object.get_queryset(...)` with the effective user, ensuring
  model-level access control (ROW_SECURITY, location restrictions, etc.)
  is honored automatically.
- Also updates `mutation_on_uuids_from_filter` to pass the current user
  through `get_queryset`, making it consistent with the new decorator.

Export the new decorator from `core/gql/gql_mutations/__init__.py`.
Pass `silent=True` to `user.save()` when applying custom user properties
in `create_test_interactive_user` to avoid triggering side effects such
No change in the User exception
Add a `link_type` CharField to both `UserBusinessAccess` and
`HistoricalUserBusinessAccess` models to support categorizing
business access links. Includes database indexes on `(user,
link_type)` and `(link_type, object_id)` for query performance.
Also removes duplicate class definition that was present in the
model file.
…ters

Added `link_type` field to `list_display` and `list_filter` in the
`UserBusinessAccessAdmin` class to improve visibility and filtering
of business access records by their link type in the Django admin interface.
…_types field to UserGQLType

- Move `UserTypeEnum` and user type constants (`UT_INTERACTIVE`, `UT_TECHNICAL`, `UT_OFFICER`, `UT_CLAIM_ADMIN`) from `schema.py` to a new `core/user_types.py` module for better separation of concerns
- Add `user_types` field to `UserGQLType` with a permission-guarded resolver using `get_user_types` helper
- Add missing newline at end of `decorators.py`
- Remove extra blank lines in `mutation_by_filter.py` and `jwt_authentication.py`
- Fix missing spaces in dict literals (`"message":str(e)` → `"message": str(e)`)
- Remove unused `e` variable in `except Throttled` clause
- Remove trailing whitespace across multiple files
- Remove unused imports (`os`, `settings`, `filter_validity`) in migration and management command files
Introduce a custom `createsuperuser` command that overrides Django's
stock implementation to use openIMIS's `core.User.create_superuser`
logic. The command supports:

- Interactive and non-interactive (`--noinput`) modes
- `--username`, `--password`, `--email`, `--last-name`, and
  `--other-names` arguments
- `DJANGO_SUPERUSER_PASSWORD` environment variable for CI/Docker use

The command is registered in `CoreConfig.ready()` by clearing Django's
management command cache and pointing `createsuperuser` to the core
module, ensuring the custom implementation takes precedence over
Django's built-in one.
…ll user

- Return `True` early if the user is a superuser, ensuring superusers
  are treated as IMIS administrators
- Add a null check on `user` before calling `isinstance` to prevent
  potential `AttributeError` when `_u` is not set
- Refactor `check_authentication` to support bare decorator usage
  (no parentheses), explicit `user` argument, and thread-local user
  resolution via `get_current_user()` fallback
- Extract core authentication and business access guard logic into a
  new `core/access.py` module (`guard_user_access`, `is_http_request`)
  to improve separation of concerns and reusability
- Add `check_permissions` decorator to enforce `user.has_perms` checks
  with optional `access_requirements` fallback for business-level access
- Support both HTTP view and service-layer usage patterns across all
  decorators, with consistent error responses per context
Refactored `user_authentication` to consolidate the auto-provision
fallback into a single conditional, eliminating redundant early returns.

Additionally, after successful authentication, `login()` is now called
for staff users when a session is available. This ensures the Django
session is properly initialized for staff users, which is required for
session-based authentication flows (e.g., Django admin access).
delcroip added 9 commits June 29, 2026 16:16
The migration 0037 now includes a RunPython operation to set is_superuser=True for users with an active role of is_system=64.
Replace multiple queries fetching admin role and user IDs with a single
filtered update on the User model using related fields. This improves
efficiency and correctness by eliminating intermediate lists and
ensuring all validity constraints are applied directly in the database.
The filter condition `validity_to__isnull=True` on the User model was incorrectly excluding users with a non-null validity_to from being migrated to superuser status. Removing it ensures all admin users are correctly made superusers.
Remove deprecated ClearUserContextMiddleware and CustomJSONWebTokenMiddleware along with related unused imports. The impersonation logic has been superseded by alternative implementation. Correct Permissions-Policy directive by adding missing equals sign for microphone.
@gitguardian

gitguardian Bot commented Jun 30, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 5 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
33827020 Triggered Username Password 3089872 core/tests/test_utils.py View secret
34430641 Triggered Generic Password 110bd83 core/tests/test_graphql.py View secret
34430642 Triggered Generic Password 110bd83 core/tests/test_graphql.py View secret
29130333 Triggered Generic Password c73105d core/tests/test_graphql.py View secret
33869950 Triggered Generic Password 86ea8d2 core/management/commands/createsuperuser.py View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@delcroip

delcroip commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

flake8 runs locally, the only line with defect is not part on the source branch.

poke @zikani03, can you merge this block user creation with vite

@zikani03 zikani03 added the enhancement New feature or request label Jul 7, 2026
…y return type

- Replace Python uuidv7() with cross-database GenerateUUIDv7 Func for native UUIDv7.
- Change filter_validity in OpenIMISHistoryMixin to return a list of Q objects.
- Update imports in history_model.py and openimis_model.py.
@delcroip delcroip added bug Something isn't working next-release labels Jul 8, 2026
@sunilparajuli

Copy link
Copy Markdown
Member

@delcroip its a big changes i will review and will get you back

Comment thread core/access.py Outdated
Comment thread core/models/user.py
Comment thread core/schema.py Outdated

@sunilparajuli sunilparajuli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added review

- Raise ValueError for empty perm_list in has_role_perms
- Add early return in has_perms when no perms and no access requirements
- Catch ObjectDoesNotExist when fetching system role in update_or_create_user
- Improve error message for invalid content_type_label

@sunilparajuli sunilparajuli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks good to go!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request next-release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants