gitlabform 6.2.3__tar.gz → 6.2.4__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.
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/pr-ci-workflow.yml +2 -2
- {gitlabform-6.2.3 → gitlabform-6.2.4}/PKG-INFO +1 -1
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/changelog.md +16 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/__init__.py +2 -2
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/core.py +1 -1
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/groups.py +1 -1
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/python_gitlab.py +1 -1
- gitlabform-6.2.4/gitlabform/processors/group/group_ldap_links_processor.py +106 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/pyproject.toml +1 -1
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tbump.toml +1 -1
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_group_ldap_links.py +23 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/gitlab/test_core.py +14 -0
- gitlabform-6.2.4/tests/unit/gitlab/test_groups.py +15 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/gitlab/test_python_gitlab.py +8 -0
- gitlabform-6.2.4/tests/unit/processors/test_group_ldap_links_processor.py +165 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/uv.lock +4 -4
- gitlabform-6.2.3/gitlabform/gitlab/group_ldap_links.py +0 -40
- gitlabform-6.2.3/gitlabform/processors/group/group_ldap_links_processor.py +0 -16
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.coveragerc +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.dockerignore +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.git-blame-ignore-revs +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/CODEOWNERS +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/ISSUE_TEMPLATE/question-or-other-issue.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/actions/setup-uv-local/README.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/actions/setup-uv-local/action.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/dependabot.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/_main.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/_releases.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/build.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/codecov-upload.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/prs-entrypoint-forks.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/prs-entrypoint-main.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/static-analysis.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/tests-acceptance.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.github/workflows/tests-unit.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.gitignore +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.lgtm.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.mypy.ini +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.overrides/gitlabform-logo-favicon.png +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.overrides/gitlabform-logo-favicon.svg +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/.python-version +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/CONTRIBUTING.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/Dockerfile +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/LICENSE +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/README.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/codecov.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/common.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/docker.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/docs.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/env.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/gitlab/await-healthy.sh +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/gitlab/gitlab.rb +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/gitlab/healthcheck-and-setup.sh +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/gitlab/run_gitlab_in_docker.sh +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/gitlab/tests.Dockerfile +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/infra.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/main.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/package.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/qa.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/dev/release.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/automation.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/contrib/coding_guidelines.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/contrib/implementation_design.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/contrib/index.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/contrib/local_development.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/contrib/releases.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/contrib/workflows.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/features.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/Opencast-logo.png +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/egnyte-logo.svg +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/elastic-path-logo.svg +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/gitlabform-logo-square.png +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/gitlabform-logo.png +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/gitlabform-logo.svg +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/group-ldap-links-provider.png +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/liquidlight-logo.svg +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/roche-logo.png +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/stroeer-online-marketing-logo.svg +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/images/ti8m-logo.png +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/index.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/installation.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/archive_unarchive.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/avatar.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/badges.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/ci_cd_variables.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/deploy_keys.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/files.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/group_ldap_links.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/group_saml_links.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/index.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/integrations.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/job_token_scope.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/labels.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/members.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/merge_requests.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/pipeline_schedules.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/project_security_settings.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/project_transfer.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/protected_branches.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/protected_environments.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/push_mirrors.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/push_rules.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/resource_groups.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/settings.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/tags_protection.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/reference/webhooks.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/requirements.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/running.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/similar_apps.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/docs/upgrade.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlab-config/config.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlab-config/gitlab-config.md +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/configuration/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/configuration/common.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/configuration/core.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/configuration/groups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/configuration/projects.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/configuration/transform.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/constants.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/commits.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/merge_requests.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/pipelines.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/project_merge_requests_approvals.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/project_protected_environments.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/projects.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/gitlab/variables.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/lists/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/lists/filter.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/lists/groups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/lists/projects.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/output.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/abstract_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/application/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/application/application_settings_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/defining_keys.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_badges_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_hooks_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_labels_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_members_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_protected_branches_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_push_rules_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_saml_links_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_settings_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/group/group_variables_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/multiple_entities_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/badges_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/branches_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/deploy_keys_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/files_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/hooks_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/integrations_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/job_token_scope_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/members_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/merge_requests_approval_rules.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/merge_requests_approvals.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/project_labels_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/project_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/project_push_rules_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/project_security_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/project_settings_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/project_variables_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/remote_mirrors_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/resource_groups_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/schedules_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/project/tags_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/shared/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/shared/protected_environments_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/util/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/util/badges_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/util/branch_protection.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/util/decorators.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/util/difference_logger.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/util/labels_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/processors/util/variables_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/run.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/gitlabform/util.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/mkdocs.yml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/prek.toml +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/conftest.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_branches.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_branches_users_case_insensitive.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_group_protected_branches.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_group_push_rules.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_group_saml_links.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_group_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_group_variables.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_merge_request_approval_rules.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_merge_request_approvals_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_protected_environments.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_push_rules.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/premium/test_tags.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_application_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_archive_project.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_badges.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_branches.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_deploy_keys.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_deploy_keys_all_projects.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_files.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_files_templates.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_avatar.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_badges.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_hooks.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_labels.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_members_case_insensitive.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_members_groups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_members_users.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_group_variables.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_hooks.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_integrations.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_job_token_scope.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_members.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_members_add_group.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_members_enforce.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_project_avatar.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_project_group_members_case_insensitive.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_project_labels.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_project_members_case_insensitve.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_project_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_project_variables.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_remote_mirrors.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_resource_groups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_running.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_scheduled_for_deletion_project.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_schedules.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_tags.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_token_from_config.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_transfer_project.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/standard/test_wildcard_patterns.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/ultimate/test_group_members.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/ultimate/test_group_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/ultimate/test_members.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/ultimate/test_project_security_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/acceptance/ultimate/test_project_settings.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_case_sensitivity.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_inheritance_break_projects_and_groups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_inheritance_break_subgroups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_inheritance_break_validation.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_projects_and_groups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_skip_groups_skip_projects.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_subgroups.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_wildcard_patterns.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/test_yaml_version.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/transform/test_access_level_transformer.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/transform/test_group_transformer.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/transform/test_implicit_name_transformer.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/transform/test_user_and_group_transformers.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/configuration/transform/test_user_transformer.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/__init__.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_abstract_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_abstract_processors.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_branches_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_deploy_keys_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_difference_logger.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_files_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_group_members_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_group_protected_branches_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_group_saml_links_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_merge_requests_approval_rules_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_schedules_processor_extended_cron_pattern.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/processors/test_util_badges_processor.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/test_access_levels.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/test_non_empty_configs_provider.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/test_output.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/test_parse_args.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/test_projects_provider.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/test_show_summary.py +0 -0
- {gitlabform-6.2.3 → gitlabform-6.2.4}/tests/unit/test_utils.py +0 -0
|
@@ -77,7 +77,7 @@ jobs:
|
|
|
77
77
|
# uses: actions/setup-example@v1
|
|
78
78
|
|
|
79
79
|
- name: Initialize CodeQL
|
|
80
|
-
uses: github/codeql-action/init@v4.38.
|
|
80
|
+
uses: github/codeql-action/init@v4.38.2
|
|
81
81
|
with:
|
|
82
82
|
languages: ${{ matrix.language }}
|
|
83
83
|
build-mode: ${{ matrix.build-mode }}
|
|
@@ -89,6 +89,6 @@ jobs:
|
|
|
89
89
|
# queries: security-extended,security-and-quality
|
|
90
90
|
|
|
91
91
|
- name: Perform CodeQL Analysis
|
|
92
|
-
uses: github/codeql-action/analyze@v4.38.
|
|
92
|
+
uses: github/codeql-action/analyze@v4.38.2
|
|
93
93
|
with:
|
|
94
94
|
category: "/language:${{matrix.language}}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: gitlabform
|
|
3
|
-
Version: 6.2.
|
|
3
|
+
Version: 6.2.4
|
|
4
4
|
Summary: 🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML
|
|
5
5
|
Project-URL: Homepage, https://gitlabform.github.io/gitlabform/
|
|
6
6
|
Project-URL: Repository, https://github.com/gitlabform/gitlabform.git
|
|
@@ -6,6 +6,22 @@ For details on how to migrate between major versions, please refer to the [upgra
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 6.2.4
|
|
10
|
+
|
|
11
|
+
### Performance
|
|
12
|
+
|
|
13
|
+
* skip fetching projects when querying groups [#1436](https://github.com/gitlabform/gitlabform/pull/1436). ([ryanpham-gitlab](https://github.com/ryanpham-gitlab))
|
|
14
|
+
|
|
15
|
+
### Refactors
|
|
16
|
+
|
|
17
|
+
* migrate group ldap links processor to python-gitlab [#1428](https://github.com/gitlabform/gitlabform/pull/1428). ([rickbrouwer](https://github.com/rickbrouwer))
|
|
18
|
+
|
|
19
|
+
### Dependencies
|
|
20
|
+
|
|
21
|
+
* Update various dependencies to newer versions.
|
|
22
|
+
|
|
23
|
+
Thanks to all the contributors of this release!
|
|
24
|
+
|
|
9
25
|
## 6.2.3
|
|
10
26
|
|
|
11
27
|
### Bug Fixes
|
|
@@ -7,7 +7,7 @@ from typing import List
|
|
|
7
7
|
from gitlab import Gitlab as GitlabClient, GraphQL
|
|
8
8
|
|
|
9
9
|
from gitlabform.gitlab.commits import GitLabCommits
|
|
10
|
-
from gitlabform.gitlab.
|
|
10
|
+
from gitlabform.gitlab.groups import GitLabGroups
|
|
11
11
|
from gitlabform.gitlab.merge_requests import GitLabMergeRequests
|
|
12
12
|
from gitlabform.gitlab.pipelines import GitLabPipelines
|
|
13
13
|
from gitlabform.gitlab.project_protected_environments import (
|
|
@@ -48,8 +48,8 @@ class AccessLevel(enum.IntEnum):
|
|
|
48
48
|
|
|
49
49
|
class GitLab(
|
|
50
50
|
GitLabCommits,
|
|
51
|
+
GitLabGroups,
|
|
51
52
|
GitLabMergeRequests,
|
|
52
|
-
GitLabGroupLDAPLinks,
|
|
53
53
|
GitLabPipelines,
|
|
54
54
|
GitLabProjectProtectedEnvironments,
|
|
55
55
|
GitLabProjectMergeRequestsApprovals,
|
|
@@ -154,7 +154,7 @@ class GitLabCore:
|
|
|
154
154
|
|
|
155
155
|
@functools.lru_cache()
|
|
156
156
|
def _get_group_id(self, path) -> int:
|
|
157
|
-
group = self._make_requests_to_api("groups/%s", path, "GET")
|
|
157
|
+
group = self._make_requests_to_api("groups/%s?with_projects=false", path, "GET")
|
|
158
158
|
return int(group["id"])
|
|
159
159
|
|
|
160
160
|
@functools.lru_cache()
|
|
@@ -29,7 +29,7 @@ class GitLabGroups(GitLabCore):
|
|
|
29
29
|
raise NotFoundException(f"Group/subgroup with path '{some_string}' not found.")
|
|
30
30
|
|
|
31
31
|
def get_group(self, name):
|
|
32
|
-
return self._make_requests_to_api("groups/%s", name)
|
|
32
|
+
return self._make_requests_to_api("groups/%s?with_projects=false", name)
|
|
33
33
|
|
|
34
34
|
def get_group_descendants(self, group_id_or_path):
|
|
35
35
|
return self._make_requests_to_api("groups/%s/descendant_groups", group_id_or_path)
|
|
@@ -84,7 +84,7 @@ class PythonGitlab(Gitlab):
|
|
|
84
84
|
|
|
85
85
|
@functools.lru_cache()
|
|
86
86
|
def get_group_by_path_cached(self, groupname: str) -> Group:
|
|
87
|
-
group: Group = self.groups.get(groupname)
|
|
87
|
+
group: Group = self.groups.get(groupname, with_projects=False)
|
|
88
88
|
if group:
|
|
89
89
|
return group
|
|
90
90
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from logging import info
|
|
2
|
+
from typing import Dict, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from gitlab.exceptions import GitlabCreateError, GitlabListError
|
|
5
|
+
from gitlab.v4.objects import Group, GroupLDAPGroupLink
|
|
6
|
+
|
|
7
|
+
from gitlabform.gitlab import GitLab
|
|
8
|
+
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
9
|
+
|
|
10
|
+
# A link is identified by provider + exactly one of (cn, filter).
|
|
11
|
+
DefiningKey = Tuple[Optional[str], Optional[str], Optional[str]]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GroupLDAPLinksProcessor(AbstractProcessor):
|
|
15
|
+
def __init__(self, gitlab: GitLab):
|
|
16
|
+
super().__init__("group_ldap_links", gitlab)
|
|
17
|
+
|
|
18
|
+
def _process_configuration(self, group_path_and_name: str, configuration: Dict):
|
|
19
|
+
configured_links: Dict = configuration.get("group_ldap_links", {})
|
|
20
|
+
enforce = configured_links.pop("enforce", False)
|
|
21
|
+
|
|
22
|
+
group: Group = self.gl.get_group_by_path_cached(group_path_and_name)
|
|
23
|
+
existing_links: List[GroupLDAPGroupLink] = self._list_existing_links(group)
|
|
24
|
+
|
|
25
|
+
processed_keys: set = set()
|
|
26
|
+
|
|
27
|
+
for entity_name, link_config in configured_links.items():
|
|
28
|
+
key = self._defining_key_of_config(link_config)
|
|
29
|
+
matching = next(
|
|
30
|
+
(link for link in existing_links if self._defining_key_of_link(link) == key),
|
|
31
|
+
None,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if link_config.get("delete", False):
|
|
35
|
+
if matching:
|
|
36
|
+
info(f"Deleting {entity_name} of {self.configuration_name} in {group_path_and_name}")
|
|
37
|
+
matching.delete()
|
|
38
|
+
else:
|
|
39
|
+
info(
|
|
40
|
+
f"Not deleting {entity_name} of {self.configuration_name} in {group_path_and_name},"
|
|
41
|
+
f" because it doesn't exist"
|
|
42
|
+
)
|
|
43
|
+
processed_keys.add(key)
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
if matching:
|
|
47
|
+
if self._needs_update(matching.asdict(), link_config):
|
|
48
|
+
# The API has no endpoint to update an LDAP link
|
|
49
|
+
# (https://docs.gitlab.com/api/group_ldap_links/), so we recreate it.
|
|
50
|
+
info(f" * Recreating {entity_name} of {self.configuration_name} in {group_path_and_name}")
|
|
51
|
+
matching.delete()
|
|
52
|
+
self._create_link(group, link_config)
|
|
53
|
+
else:
|
|
54
|
+
info(
|
|
55
|
+
f" * {entity_name} of {self.configuration_name} in {group_path_and_name}"
|
|
56
|
+
f" doesn't need an update."
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
info(f" * Adding {entity_name} of {self.configuration_name} in {group_path_and_name}")
|
|
60
|
+
self._create_link(group, link_config)
|
|
61
|
+
processed_keys.add(key)
|
|
62
|
+
|
|
63
|
+
if enforce:
|
|
64
|
+
for existing in existing_links:
|
|
65
|
+
if self._defining_key_of_link(existing) not in processed_keys:
|
|
66
|
+
info(
|
|
67
|
+
f"Deleting LDAP link (provider={existing.provider},"
|
|
68
|
+
f" cn={getattr(existing, 'cn', None)},"
|
|
69
|
+
f" filter={getattr(existing, 'filter', None)})"
|
|
70
|
+
f" of {self.configuration_name} in {group_path_and_name}"
|
|
71
|
+
f" as it's not in config and enforce is set to true."
|
|
72
|
+
)
|
|
73
|
+
existing.delete()
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def _list_existing_links(group: Group) -> List[GroupLDAPGroupLink]:
|
|
77
|
+
# GitLab returns 404 (not an empty list) when a group has no LDAP links configured;
|
|
78
|
+
# python-gitlab surfaces that as GitlabListError.
|
|
79
|
+
try:
|
|
80
|
+
return group.ldap_group_links.list(get_all=True)
|
|
81
|
+
except GitlabListError as e:
|
|
82
|
+
if e.response_code == 404:
|
|
83
|
+
return []
|
|
84
|
+
raise
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _create_link(group: Group, link_config: Dict) -> None:
|
|
88
|
+
try:
|
|
89
|
+
group.ldap_group_links.create(link_config)
|
|
90
|
+
except GitlabCreateError as e:
|
|
91
|
+
# GitLab returns 404 instead of 400 when the create parameters are invalid
|
|
92
|
+
# (e.g. a non-existent provider), so a 404 here does not mean a missing group.
|
|
93
|
+
if e.response_code == 404:
|
|
94
|
+
raise GitlabCreateError(
|
|
95
|
+
f"Invalid parameters for an LDAP group link: {link_config}",
|
|
96
|
+
response_code=404,
|
|
97
|
+
) from e
|
|
98
|
+
raise
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def _defining_key_of_config(link_config: Dict) -> DefiningKey:
|
|
102
|
+
return (link_config.get("provider"), link_config.get("cn"), link_config.get("filter"))
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def _defining_key_of_link(link: GroupLDAPGroupLink) -> DefiningKey:
|
|
106
|
+
return (link.provider, getattr(link, "cn", None), getattr(link, "filter", None))
|
|
@@ -10,7 +10,7 @@ packages = ["gitlabform"]
|
|
|
10
10
|
|
|
11
11
|
[project]
|
|
12
12
|
name = "gitlabform"
|
|
13
|
-
version = "6.2.
|
|
13
|
+
version = "6.2.4"
|
|
14
14
|
requires-python = ">=3.12.0"
|
|
15
15
|
authors = [{ name = "Greg Dubicki and Contributors" }]
|
|
16
16
|
description = "🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML"
|
|
@@ -120,3 +120,26 @@ class TestGroupLDAPLinks:
|
|
|
120
120
|
assert ldap_links[0].provider == "LDAP Main"
|
|
121
121
|
assert ldap_links[0].cn == "devops"
|
|
122
122
|
assert ldap_links[0].group_access == AccessLevel.get_value("developer")
|
|
123
|
+
|
|
124
|
+
def test__delete_ldap_links(self, gl, group):
|
|
125
|
+
"""Test deleting an LDAP link of a group with 'delete: true'."""
|
|
126
|
+
|
|
127
|
+
# Verify group has 1 ldap link - last state of the group in previous test
|
|
128
|
+
assert len(group.ldap_group_links.list()) == 1
|
|
129
|
+
|
|
130
|
+
delete_ldap_link = f"""
|
|
131
|
+
projects_and_groups:
|
|
132
|
+
{group.full_path}/*:
|
|
133
|
+
group_ldap_links:
|
|
134
|
+
devops_users:
|
|
135
|
+
provider: LDAP Main
|
|
136
|
+
cn: devops
|
|
137
|
+
delete: true
|
|
138
|
+
"""
|
|
139
|
+
run_gitlabform(delete_ldap_link, group)
|
|
140
|
+
|
|
141
|
+
# Verify that the last LDAP link was removed.
|
|
142
|
+
# GitLab API returns 404 if there are no LDAP links configured.
|
|
143
|
+
refreshed_group = gl.groups.get(group.id)
|
|
144
|
+
with pytest.raises(GitlabListError):
|
|
145
|
+
refreshed_group.ldap_group_links.list(get_all=True)
|
|
@@ -4,6 +4,20 @@ import pytest
|
|
|
4
4
|
from unittest.mock import patch, MagicMock
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
def test_get_group_id_excludes_projects():
|
|
8
|
+
from gitlabform.gitlab.core import GitLabCore
|
|
9
|
+
|
|
10
|
+
core = GitLabCore.__new__(GitLabCore)
|
|
11
|
+
core._make_requests_to_api = MagicMock(return_value={"id": "123"})
|
|
12
|
+
|
|
13
|
+
assert core._get_group_id("parent/group") == 123
|
|
14
|
+
core._make_requests_to_api.assert_called_once_with(
|
|
15
|
+
"groups/%s?with_projects=false",
|
|
16
|
+
"parent/group",
|
|
17
|
+
"GET",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
7
21
|
class TestGitLabCoreRetryConfiguration:
|
|
8
22
|
"""
|
|
9
23
|
Tests for GitLabCore retry configuration.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from unittest.mock import MagicMock
|
|
2
|
+
|
|
3
|
+
from gitlabform.gitlab.groups import GitLabGroups
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_get_group_excludes_projects():
|
|
7
|
+
gitlab = GitLabGroups.__new__(GitLabGroups)
|
|
8
|
+
gitlab._make_requests_to_api = MagicMock(return_value={})
|
|
9
|
+
|
|
10
|
+
gitlab.get_group("parent/group")
|
|
11
|
+
|
|
12
|
+
gitlab._make_requests_to_api.assert_called_once_with(
|
|
13
|
+
"groups/%s?with_projects=false",
|
|
14
|
+
"parent/group",
|
|
15
|
+
)
|
|
@@ -8,6 +8,14 @@ from unittest.mock import MagicMock, patch
|
|
|
8
8
|
|
|
9
9
|
class TestPythonGitlab:
|
|
10
10
|
|
|
11
|
+
def test_get_group_by_path_cached_excludes_projects(self):
|
|
12
|
+
python_gitlab = PythonGitlab(MagicMock())
|
|
13
|
+
group = MagicMock()
|
|
14
|
+
python_gitlab.groups.get = MagicMock(return_value=group)
|
|
15
|
+
|
|
16
|
+
assert python_gitlab.get_group_by_path_cached("parent/group") is group
|
|
17
|
+
python_gitlab.groups.get.assert_called_once_with("parent/group", with_projects=False)
|
|
18
|
+
|
|
11
19
|
def test_get_member_role_id_cached_gets_role_id_from_roles_in_group_on_saas(self):
|
|
12
20
|
group_name = "Test"
|
|
13
21
|
role_name = "custom_role"
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from gitlab.exceptions import GitlabCreateError, GitlabListError
|
|
5
|
+
|
|
6
|
+
from gitlabform.processors.group.group_ldap_links_processor import GroupLDAPLinksProcessor
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestGroupLDAPLinksProcessor:
|
|
10
|
+
def setup_method(self):
|
|
11
|
+
with patch("gitlabform.processors.abstract_processor.GitlabWrapper"):
|
|
12
|
+
self.processor = GroupLDAPLinksProcessor(MagicMock())
|
|
13
|
+
self.gl = MagicMock()
|
|
14
|
+
self.processor.gl = self.gl
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def _link(provider: str = "LDAP Main", cn=None, filter=None, group_access: int = 30) -> MagicMock:
|
|
18
|
+
link = MagicMock()
|
|
19
|
+
link.provider = provider
|
|
20
|
+
link.cn = cn
|
|
21
|
+
link.filter = filter
|
|
22
|
+
link.group_access = group_access
|
|
23
|
+
link.asdict.return_value = {
|
|
24
|
+
"provider": provider,
|
|
25
|
+
"cn": cn,
|
|
26
|
+
"filter": filter,
|
|
27
|
+
"group_access": group_access,
|
|
28
|
+
}
|
|
29
|
+
return link
|
|
30
|
+
|
|
31
|
+
def _group(self, *links: MagicMock) -> MagicMock:
|
|
32
|
+
group = MagicMock()
|
|
33
|
+
group.ldap_group_links.list.return_value = list(links)
|
|
34
|
+
self.gl.get_group_by_path_cached.return_value = group
|
|
35
|
+
return group
|
|
36
|
+
|
|
37
|
+
def _process(self, group_ldap_links: dict) -> None:
|
|
38
|
+
self.processor._process_configuration("test/group", {"group_ldap_links": group_ldap_links})
|
|
39
|
+
|
|
40
|
+
# ------------------------------------------------------------------ add
|
|
41
|
+
|
|
42
|
+
def test_creates_link_when_missing(self):
|
|
43
|
+
group = self._group()
|
|
44
|
+
link_config = {"provider": "LDAP Main", "cn": "devops", "group_access": 40}
|
|
45
|
+
|
|
46
|
+
self._process({"devops_users": link_config})
|
|
47
|
+
|
|
48
|
+
group.ldap_group_links.create.assert_called_once_with(link_config)
|
|
49
|
+
|
|
50
|
+
def test_cn_link_does_not_match_filter_link(self):
|
|
51
|
+
# provider + cn and provider + filter are different identities
|
|
52
|
+
existing = self._link(cn="devops")
|
|
53
|
+
group = self._group(existing)
|
|
54
|
+
link_config = {"provider": "LDAP Main", "filter": "(devType=security)", "group_access": 30}
|
|
55
|
+
|
|
56
|
+
self._process({"security_users": link_config})
|
|
57
|
+
|
|
58
|
+
group.ldap_group_links.create.assert_called_once_with(link_config)
|
|
59
|
+
existing.delete.assert_not_called()
|
|
60
|
+
|
|
61
|
+
# --------------------------------------------------------------- update
|
|
62
|
+
|
|
63
|
+
def test_recreates_link_when_group_access_changed(self):
|
|
64
|
+
existing = self._link(cn="devops", group_access=40)
|
|
65
|
+
group = self._group(existing)
|
|
66
|
+
link_config = {"provider": "LDAP Main", "cn": "devops", "group_access": 30}
|
|
67
|
+
|
|
68
|
+
self._process({"devops_users": link_config})
|
|
69
|
+
|
|
70
|
+
# the API has no update endpoint for LDAP links, so it is deleted and added again
|
|
71
|
+
existing.delete.assert_called_once()
|
|
72
|
+
group.ldap_group_links.create.assert_called_once_with(link_config)
|
|
73
|
+
|
|
74
|
+
def test_no_churn_when_configured_link_matches_existing(self):
|
|
75
|
+
existing = self._link(cn="devops", group_access=40)
|
|
76
|
+
group = self._group(existing)
|
|
77
|
+
|
|
78
|
+
self._process({"devops_users": {"provider": "LDAP Main", "cn": "devops", "group_access": 40}})
|
|
79
|
+
|
|
80
|
+
existing.delete.assert_not_called()
|
|
81
|
+
group.ldap_group_links.create.assert_not_called()
|
|
82
|
+
|
|
83
|
+
# --------------------------------------------------------------- delete
|
|
84
|
+
|
|
85
|
+
def test_delete_true_removes_matching_link(self):
|
|
86
|
+
existing = self._link(cn="devops")
|
|
87
|
+
group = self._group(existing)
|
|
88
|
+
|
|
89
|
+
self._process({"devops_users": {"provider": "LDAP Main", "cn": "devops", "delete": True}})
|
|
90
|
+
|
|
91
|
+
existing.delete.assert_called_once()
|
|
92
|
+
group.ldap_group_links.create.assert_not_called()
|
|
93
|
+
|
|
94
|
+
def test_delete_true_on_non_existent_is_logged_and_skipped(self, caplog):
|
|
95
|
+
group = self._group()
|
|
96
|
+
|
|
97
|
+
with caplog.at_level("INFO"):
|
|
98
|
+
self._process({"devops_users": {"provider": "LDAP Main", "cn": "devops", "delete": True}})
|
|
99
|
+
|
|
100
|
+
matching = [r for r in caplog.records if "Not deleting devops_users of group_ldap_links" in r.message]
|
|
101
|
+
assert len(matching) == 1
|
|
102
|
+
group.ldap_group_links.create.assert_not_called()
|
|
103
|
+
|
|
104
|
+
# -------------------------------------------------------------- enforce
|
|
105
|
+
|
|
106
|
+
def test_enforce_deletes_unmanaged_links(self):
|
|
107
|
+
managed = self._link(cn="devops", group_access=40)
|
|
108
|
+
stray = self._link(filter="(devType=security)")
|
|
109
|
+
self._group(managed, stray)
|
|
110
|
+
|
|
111
|
+
self._process(
|
|
112
|
+
{
|
|
113
|
+
"enforce": True,
|
|
114
|
+
"devops_users": {"provider": "LDAP Main", "cn": "devops", "group_access": 40},
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
stray.delete.assert_called_once()
|
|
119
|
+
managed.delete.assert_not_called()
|
|
120
|
+
|
|
121
|
+
def test_without_enforce_unmanaged_links_are_left_alone(self):
|
|
122
|
+
stray = self._link(cn="stray")
|
|
123
|
+
self._group(stray)
|
|
124
|
+
|
|
125
|
+
self._process({})
|
|
126
|
+
|
|
127
|
+
stray.delete.assert_not_called()
|
|
128
|
+
|
|
129
|
+
# ------------------------------------------------------- empty list 404
|
|
130
|
+
|
|
131
|
+
def test_list_404_is_treated_as_no_links(self):
|
|
132
|
+
# GitLab returns 404 instead of an empty list when a group has no LDAP links
|
|
133
|
+
group = self._group()
|
|
134
|
+
group.ldap_group_links.list.side_effect = GitlabListError("404 Not Found", response_code=404)
|
|
135
|
+
link_config = {"provider": "LDAP Main", "cn": "devops", "group_access": 40}
|
|
136
|
+
|
|
137
|
+
self._process({"devops_users": link_config})
|
|
138
|
+
|
|
139
|
+
group.ldap_group_links.create.assert_called_once_with(link_config)
|
|
140
|
+
|
|
141
|
+
def test_other_list_error_is_reraised(self):
|
|
142
|
+
group = self._group()
|
|
143
|
+
group.ldap_group_links.list.side_effect = GitlabListError("403 Forbidden", response_code=403)
|
|
144
|
+
|
|
145
|
+
with pytest.raises(GitlabListError):
|
|
146
|
+
self._process({"devops_users": {"provider": "LDAP Main", "cn": "devops", "group_access": 40}})
|
|
147
|
+
|
|
148
|
+
group.ldap_group_links.create.assert_not_called()
|
|
149
|
+
|
|
150
|
+
# -------------------------------------------------------- create errors
|
|
151
|
+
|
|
152
|
+
def test_create_404_is_reraised_with_clearer_message(self):
|
|
153
|
+
# GitLab returns 404 instead of 400 when the create parameters are invalid
|
|
154
|
+
group = self._group()
|
|
155
|
+
group.ldap_group_links.create.side_effect = GitlabCreateError("404 Not Found", response_code=404)
|
|
156
|
+
|
|
157
|
+
with pytest.raises(GitlabCreateError, match="Invalid parameters"):
|
|
158
|
+
self._process({"devops_users": {"provider": "nonexistent", "cn": "devops", "group_access": 40}})
|
|
159
|
+
|
|
160
|
+
def test_other_create_error_is_reraised_unchanged(self):
|
|
161
|
+
group = self._group()
|
|
162
|
+
group.ldap_group_links.create.side_effect = GitlabCreateError("400 Bad Request", response_code=400)
|
|
163
|
+
|
|
164
|
+
with pytest.raises(GitlabCreateError, match="400 Bad Request"):
|
|
165
|
+
self._process({"devops_users": {"provider": "LDAP Main", "cn": "devops", "group_access": 40}})
|
|
@@ -555,7 +555,7 @@ wheels = [
|
|
|
555
555
|
|
|
556
556
|
[[package]]
|
|
557
557
|
name = "gitlabform"
|
|
558
|
-
version = "6.2.
|
|
558
|
+
version = "6.2.4"
|
|
559
559
|
source = { editable = "." }
|
|
560
560
|
dependencies = [
|
|
561
561
|
{ name = "certifi" },
|
|
@@ -1939,11 +1939,11 @@ wheels = [
|
|
|
1939
1939
|
|
|
1940
1940
|
[[package]]
|
|
1941
1941
|
name = "urllib3"
|
|
1942
|
-
version = "2.
|
|
1942
|
+
version = "2.8.0"
|
|
1943
1943
|
source = { registry = "https://pypi.org/simple" }
|
|
1944
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1944
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" }
|
|
1945
1945
|
wheels = [
|
|
1946
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1946
|
+
{ url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" },
|
|
1947
1947
|
]
|
|
1948
1948
|
|
|
1949
1949
|
[[package]]
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from gitlabform.gitlab.core import NotFoundException, InvalidParametersException
|
|
2
|
-
from gitlabform.gitlab.groups import GitLabGroups
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class GitLabGroupLDAPLinks(GitLabGroups):
|
|
6
|
-
def get_ldap_group_links(self, group):
|
|
7
|
-
group_id = self.get_group_id_case_insensitive(group)
|
|
8
|
-
return self._make_requests_to_api("groups/%s/ldap_group_links", group_id, expected_codes=[200, 404])
|
|
9
|
-
|
|
10
|
-
def add_ldap_group_link(self, group, data):
|
|
11
|
-
group_id = self.get_group_id_case_insensitive(group)
|
|
12
|
-
data["id"] = group_id
|
|
13
|
-
|
|
14
|
-
try:
|
|
15
|
-
return self._make_requests_to_api(
|
|
16
|
-
"groups/%s/ldap_group_links",
|
|
17
|
-
group_id,
|
|
18
|
-
method="POST",
|
|
19
|
-
data=data,
|
|
20
|
-
expected_codes=[200, 201],
|
|
21
|
-
)
|
|
22
|
-
# this is a GitLab API bug - it returns 404 here instead of 400 for bad requests...
|
|
23
|
-
except NotFoundException:
|
|
24
|
-
raise InvalidParametersException(f"Invalid parameters for a Group LDAP link for group {group}: {data}")
|
|
25
|
-
|
|
26
|
-
def delete_ldap_group_link(self, group, data):
|
|
27
|
-
if "group_access" in data:
|
|
28
|
-
del data["group_access"]
|
|
29
|
-
|
|
30
|
-
group_id = self.get_group_id_case_insensitive(group)
|
|
31
|
-
data["id"] = group_id
|
|
32
|
-
|
|
33
|
-
# 404 means that the LDAP group link is already removed, so let's accept it for idempotency
|
|
34
|
-
self._make_requests_to_api(
|
|
35
|
-
"groups/%s/ldap_group_links",
|
|
36
|
-
group_id,
|
|
37
|
-
method="DELETE",
|
|
38
|
-
data=data,
|
|
39
|
-
expected_codes=[204, 404],
|
|
40
|
-
)
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from gitlabform.gitlab import GitLab
|
|
2
|
-
from gitlabform.processors.defining_keys import And, Or, Key, Xor
|
|
3
|
-
from gitlabform.processors.multiple_entities_processor import MultipleEntitiesProcessor
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class GroupLDAPLinksProcessor(MultipleEntitiesProcessor):
|
|
7
|
-
def __init__(self, gitlab: GitLab):
|
|
8
|
-
super().__init__(
|
|
9
|
-
"group_ldap_links",
|
|
10
|
-
gitlab,
|
|
11
|
-
list_method_name="get_ldap_group_links",
|
|
12
|
-
add_method_name="add_ldap_group_link",
|
|
13
|
-
delete_method_name="delete_ldap_group_link",
|
|
14
|
-
defining=And(Key("provider"), Or(Key("cn"), Key("filter"))),
|
|
15
|
-
required_to_create_or_update=And(Key("provider"), Xor(Key("cn"), Key("filter"))),
|
|
16
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|