diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..052864d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,36 @@ +name: Test cone.ldap + +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + + steps: + - uses: actions/checkout@v5 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Show Python version + run: python -c "import sys; print(sys.version)" + + - name: Install system dependencies + run: make system-dependencies + + - name: Install project + run: make install + + - name: Run tests an collect code coverage + run: make coverage diff --git a/.gitignore b/.gitignore index 23fb8bb..7ecfaf1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,11 @@ +*.egg-info *.pyc -/.installed.cfg -/.mr.developer.cfg -/bin/ -/coverage/ -/develop-eggs/ -/devsrc/ +/.coverage +/.mxmake/ +/build/ +/constraints-mxdev.txt /dist/ -/eggs/ -/include/ -/lib/ -/local/ -/parts/ -/pyvenv.cfg -/share/ -/src/cone.ldap.egg-info/ +/openldap/ +/requirements-mxdev.txt +/sources/ +/venv/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9884c89..0000000 --- a/.travis.yml +++ /dev/null @@ -1,46 +0,0 @@ -dist: bionic -language: python -sudo: false - -python: - - "2.7" - - "3.5" - - "3.6" - - "3.7" - - "3.8" - -addons: - apt: - packages: - - libsasl2-dev - - libssl-dev - - libdb-dev - -before_script: - - export TZ=Europe/Vienna - -install: - - virtualenv . - - bin/pip install coverage - - bin/pip install coveralls - - bin/pip install pyramid==1.9.4 - - | - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then - bin/pip install repoze.zcml==0.4 - bin/pip install repoze.workflow==0.6.1 - bin/pip install Pillow==6.2.2 - bin/pip install natsort==6.2.0 - else - bin/pip install repoze.zcml==1.1 - bin/pip install repoze.workflow==1.1 - fi - - bin/pip install --upgrade pip setuptools zc.buildout - - bin/buildout -N - -script: - - bin/py -m cone.ldap.tests.__init__ - - bin/coverage run --source src/cone/ldap -m cone.ldap.tests.__init__ - - bin/coverage report - -after_success: - - bin/coveralls diff --git a/CHANGES.rst b/CHANGES.rst index 62f9de5..ab7ef33 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,10 +1,14 @@ Changes ======= -0.3 (unreleased) ----------------- +1.1.0 (unreleased) +------------------ + +- Adopt Settings UI changes from ``cone.app``. + [rnix] -- Nothing changed yet. +- Use ``webresource`` for resource registration. + [rnix] 0.2 (2022-12-05) diff --git a/LICENSE.rst b/LICENSE.rst index 3b1bc71..9267a66 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -2,7 +2,7 @@ License ======= Copyright (c) 2019-2021, BlueDynamics Alliance, Austria -Copyright (c) 2021-2022, Cone Contributors +Copyright (c) 2021-2025, Cone Contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 80a31cc..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -include *.rst -include *.mo -include *.po -recursive-include etc * -recursive-include cfg * -recursive-include src * -recursive-exclude src *.pyc *.pyo diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0a0f4f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,806 @@ +############################################################################## +# THIS FILE IS GENERATED BY MXMAKE +# +# DOMAINS: +#: applications.zest-releaser +#: core.base +#: core.mxenv +#: core.mxfiles +#: core.packages +#: core.sources +#: i18n.gettext +#: i18n.lingua +#: ldap.openldap +#: ldap.python-ldap +#: qa.coverage +#: qa.test +#: system.dependencies +# +# SETTINGS (ALL CHANGES MADE BELOW SETTINGS WILL BE LOST) +############################################################################## + +## core.base + +# `deploy` target dependencies. +# No default value. +DEPLOY_TARGETS?= + +# target to be executed when calling `make run` +# No default value. +RUN_TARGET?= + +# Additional files and folders to remove when running clean target +# No default value. +CLEAN_FS?= + +# Optional makefile to include before default targets. This can +# be used to provide custom targets or hook up to existing targets. +# Default: include.mk +INCLUDE_MAKEFILE?=include.mk + +# Optional additional directories to be added to PATH in format +# `/path/to/dir/:/path/to/other/dir`. Gets inserted first, thus gets searched +# first. +# No default value. +EXTRA_PATH?= + +# Path to Python project relative to Makefile (repository root). +# Leave empty if Python project is in the same directory as Makefile. +# For monorepo setups, set to subdirectory name (e.g., `backend`). +# Future-proofed for multi-language monorepos (e.g., PROJECT_PATH_NODEJS). +# No default value. +PROJECT_PATH_PYTHON?= + +## system.dependencies + +# Space separated system package names. +# No default value. +SYSTEM_DEPENDENCIES?= + +## ldap.openldap + +# OpenLDAP version to download +# Default: 2.4.59 +OPENLDAP_VERSION?=2.4.59 + +# OpenLDAP base download URL +# Default: https://www.openldap.org/software/download/OpenLDAP/openldap-release/ +OPENLDAP_URL?=https://www.openldap.org/software/download/OpenLDAP/openldap-release/ + +# Build directory for OpenLDAP +# Default: $(shell echo $(realpath .))/openldap +OPENLDAP_DIR?=$(shell echo $(realpath .))/openldap + +# Build environment for OpenLDAP +# Default: PATH=/usr/local/bin:/usr/bin:/bin +OPENLDAP_ENV?=PATH=/usr/local/bin:/usr/bin:/bin + +## core.mxenv + +# Primary Python interpreter to use. It is used to create the +# virtual environment if `VENV_ENABLED` and `VENV_CREATE` are set to `true`. +# If global `uv` is used, this value is passed as `--python VALUE` to the venv creation. +# uv then downloads the Python interpreter if it is not available. +# for more on this feature read the [uv python documentation](https://docs.astral.sh/uv/concepts/python-versions/) +# Default: python3 +PRIMARY_PYTHON?=python3 + +# Minimum required Python version. +# Default: 3.10 +PYTHON_MIN_VERSION?=3.10 + +# Install packages using the given package installer method. +# Supported are `pip` and `uv`. When `uv` is selected, a global installation +# is auto-detected and used if available. Otherwise, uv is installed in the +# virtual environment or using `PRIMARY_PYTHON`, depending on the +# `VENV_ENABLED` setting. +# Default: pip +PYTHON_PACKAGE_INSTALLER?=uv + +# Python version for UV to install/use when creating virtual +# environments with global UV. Passed to `uv venv -p VALUE`. Supports version +# specs like `3.11`, `3.14`, `cpython@3.14`. Defaults to PRIMARY_PYTHON value +# for backward compatibility. +# Default: $(PRIMARY_PYTHON) +UV_PYTHON?=$(PRIMARY_PYTHON) + +# Flag whether to use virtual environment. If `false`, the +# interpreter according to `PRIMARY_PYTHON` found in `PATH` is used. +# Default: true +VENV_ENABLED?=true + +# Flag whether to create a virtual environment. If set to `false` +# and `VENV_ENABLED` is `true`, `VENV_FOLDER` is expected to point to an +# existing virtual environment. +# Default: true +VENV_CREATE?=true + +# The folder of the virtual environment. +# If `VENV_ENABLED` is `true` and `VENV_CREATE` is true it is used as the +# target folder for the virtual environment. If `VENV_ENABLED` is `true` and +# `VENV_CREATE` is false it is expected to point to an existing virtual +# environment. If `VENV_ENABLED` is `false` it is ignored. +# Default: .venv +VENV_FOLDER?=venv + +# mxdev to install in virtual environment. +# Default: mxdev +MXDEV?=mxdev + +# mxmake to install in virtual environment. +# Default: mxmake +MXMAKE?=mxmake + +## core.mxfiles + +# The config file to use. +# Default: mx.ini +PROJECT_CONFIG?=mx.ini + +## core.packages + +# Allow prerelease and development versions. +# By default, the package installer only finds stable versions. +# Default: false +PACKAGES_ALLOW_PRERELEASES?=false + +## qa.test + +# The command which gets executed. Defaults to the location the +# :ref:`run-tests` template gets rendered to if configured. +# Default: .mxmake/files/run-tests.sh +TEST_COMMAND?=.mxmake/files/run-tests.sh + +# Additional Python requirements for running tests to be +# installed (via pip). +# Default: pytest +TEST_REQUIREMENTS?=pytest + +# Additional make targets the test target depends on. +# No default value. +TEST_DEPENDENCY_TARGETS?= + +## qa.coverage + +# The command which gets executed. Defaults to the location the +# :ref:`run-coverage` template gets rendered to if configured. +# Default: .mxmake/files/run-coverage.sh +COVERAGE_COMMAND?=.mxmake/files/run-coverage.sh + +## applications.zest-releaser + +# Options to pass to zest.releaser prerelease command. +# No default value. +ZEST_RELEASER_PRERELEASE_OPTIONS?= + +# Options to pass to zest.releaser release command. +# No default value. +ZEST_RELEASER_RELEASE_OPTIONS?= + +# Options to pass to zest.releaser postrelease command. +# No default value. +ZEST_RELEASER_POSTRELEASE_OPTIONS?= + +# Options to pass to zest.releaser fullrelease command. +# No default value. +ZEST_RELEASER_FULLRELEASE_OPTIONS?= + +## i18n.gettext + +# Path of directory containing the message catalogs. +# Default: locale +GETTEXT_LOCALES_PATH?=src/cone/ldap/locale + +# Translation domain to use. +# No default value. +GETTEXT_DOMAIN?=cone.ldap + +# Space separated list of language identifiers. +# No default value. +GETTEXT_LANGUAGES?=en de + +## i18n.lingua + +# Path of directory to extract translatable texts from. +# Default: src +LINGUA_SEARCH_PATH?=src/cone/ldap + +# Python packages containing lingua extensions. +# No default value. +LINGUA_PLUGINS?=yafowil.lingua + +# Command line options passed to `pot-create` +# No default value. +LINGUA_OPTIONS?= + +############################################################################## +# END SETTINGS - DO NOT EDIT BELOW THIS LINE +############################################################################## + +INSTALL_TARGETS?= +DIRTY_TARGETS?= +CLEAN_TARGETS?= +PURGE_TARGETS?= +CHECK_TARGETS?= +TYPECHECK_TARGETS?= +FORMAT_TARGETS?= + +export PATH:=$(if $(EXTRA_PATH),$(EXTRA_PATH):,)$(PATH) + +# Helper variable: adds trailing slash to PROJECT_PATH_PYTHON only if non-empty +PYTHON_PROJECT_PREFIX=$(if $(PROJECT_PATH_PYTHON),$(PROJECT_PATH_PYTHON)/,) + +# Defensive settings for make: https://tech.davis-hansson.com/p/make/ +SHELL:=bash +.ONESHELL: +# for Makefile debugging purposes add -x to the .SHELLFLAGS +.SHELLFLAGS:=-eu -o pipefail -O inherit_errexit -c +.SILENT: +.DELETE_ON_ERROR: +MAKEFLAGS+=--warn-undefined-variables +MAKEFLAGS+=--no-builtin-rules + +# mxmake folder +MXMAKE_FOLDER?=.mxmake + +# Sentinel files +SENTINEL_FOLDER?=$(MXMAKE_FOLDER)/sentinels +SENTINEL?=$(SENTINEL_FOLDER)/about.txt +$(SENTINEL): $(firstword $(MAKEFILE_LIST)) + @mkdir -p $(SENTINEL_FOLDER) + @echo "Sentinels for the Makefile process." > $(SENTINEL) + +############################################################################## +# system dependencies +############################################################################## + +.PHONY: system-dependencies +system-dependencies: + @echo "Install system dependencies" + @test -z "$(SYSTEM_DEPENDENCIES)" && echo "No System dependencies defined" + @test -z "$(SYSTEM_DEPENDENCIES)" \ + || sudo apt-get install -y $(SYSTEM_DEPENDENCIES) + +############################################################################## +# openldap +############################################################################## + +# case `system.dependencies` domain is included +SYSTEM_DEPENDENCIES+=libdb-dev libsasl2-dev + +OPENLDAP_TARGET:=$(SENTINEL_FOLDER)/openldap.sentinel +$(OPENLDAP_TARGET): $(SENTINEL) + @echo "Building openldap server in '$(OPENLDAP_DIR)'" + @test -d $(OPENLDAP_DIR) || curl -o openldap-$(OPENLDAP_VERSION).tgz \ + $(OPENLDAP_URL)/openldap-$(OPENLDAP_VERSION).tgz + @test -d $(OPENLDAP_DIR) || tar xf openldap-$(OPENLDAP_VERSION).tgz + @test -d $(OPENLDAP_DIR) || rm openldap-$(OPENLDAP_VERSION).tgz + @test -d $(OPENLDAP_DIR) || mv openldap-$(OPENLDAP_VERSION) $(OPENLDAP_DIR) + @env -i -C $(OPENLDAP_DIR) $(OPENLDAP_ENV) bash -c \ + './configure \ + --with-tls \ + --enable-slapd=yes \ + --enable-overlays \ + --prefix=$(OPENLDAP_DIR) \ + && make depend \ + && make -j4 \ + && make install' + @touch $(OPENLDAP_TARGET) + +.PHONY: openldap +openldap: $(OPENLDAP_TARGET) + +.PHONY: openldap-dirty +openldap-dirty: + @test -d $(OPENLDAP_DIR) \ + && env -i -C $(OPENLDAP_DIR) $(OPENLDAP_ENV) bash -c 'make clean' + @rm -f $(OPENLDAP_TARGET) + +.PHONY: openldap-clean +openldap-clean: + @rm -f $(OPENLDAP_TARGET) + @rm -rf $(OPENLDAP_DIR) + +INSTALL_TARGETS+=openldap +DIRTY_TARGETS+=openldap-dirty +CLEAN_TARGETS+=openldap-clean + +############################################################################## +# mxenv +############################################################################## + +OS?= + +# Determine the executable path +ifeq ("$(VENV_ENABLED)", "true") +export VIRTUAL_ENV=$(abspath $(VENV_FOLDER)) +ifeq ("$(OS)", "Windows_NT") +VENV_EXECUTABLE_FOLDER=$(VIRTUAL_ENV)/Scripts +else +VENV_EXECUTABLE_FOLDER=$(VIRTUAL_ENV)/bin +endif +export PATH:=$(VENV_EXECUTABLE_FOLDER):$(PATH) +MXENV_PYTHON=python +else +MXENV_PYTHON=$(PRIMARY_PYTHON) +endif + +# Determine the package installer with non-interactive flags +ifeq ("$(PYTHON_PACKAGE_INSTALLER)","uv") +PYTHON_PACKAGE_COMMAND=uv pip --no-progress +else +PYTHON_PACKAGE_COMMAND=$(MXENV_PYTHON) -m pip +endif + +# Auto-detect global uv availability (simple existence check) +ifeq ("$(PYTHON_PACKAGE_INSTALLER)","uv") +UV_AVAILABLE:=$(shell command -v uv >/dev/null 2>&1 && echo "true" || echo "false") +else +UV_AVAILABLE:=false +endif + +# Determine installation strategy +# depending on the PYTHON_PACKAGE_INSTALLER and UV_AVAILABLE +# - both vars can be false or +# - one of them can be true, +# - but never boths. +USE_GLOBAL_UV:=$(shell [[ "$(PYTHON_PACKAGE_INSTALLER)" == "uv" && "$(UV_AVAILABLE)" == "true" ]] && echo "true" || echo "false") +USE_LOCAL_UV:=$(shell [[ "$(PYTHON_PACKAGE_INSTALLER)" == "uv" && "$(UV_AVAILABLE)" == "false" ]] && echo "true" || echo "false") + +# Check if global UV is outdated (non-blocking warning) +ifeq ("$(USE_GLOBAL_UV)","true") +UV_OUTDATED:=$(shell uv self update --dry-run 2>&1 | grep -q "Would update" && echo "true" || echo "false") +else +UV_OUTDATED:=false +endif + +MXENV_TARGET:=$(SENTINEL_FOLDER)/mxenv.sentinel +$(MXENV_TARGET): $(SENTINEL) + # Validation: Check Python version if not using global uv +ifneq ("$(USE_GLOBAL_UV)","true") + @$(PRIMARY_PYTHON) -c "import sys; vi = sys.version_info; sys.exit(1 if (int(vi[0]), int(vi[1])) >= tuple(map(int, '$(PYTHON_MIN_VERSION)'.split('.'))) else 0)" \ + && echo "Need Python >= $(PYTHON_MIN_VERSION)" && exit 1 || : +else + @echo "Using global uv for Python $(UV_PYTHON)" +endif + # Validation: Check VENV_FOLDER is set if venv enabled + @[[ "$(VENV_ENABLED)" == "true" && "$(VENV_FOLDER)" == "" ]] \ + && echo "VENV_FOLDER must be configured if VENV_ENABLED is true" && exit 1 || : + # Validation: Check uv not used with system Python + @[[ "$(VENV_ENABLED)" == "false" && "$(PYTHON_PACKAGE_INSTALLER)" == "uv" ]] \ + && echo "Package installer uv does not work with a global Python interpreter." && exit 1 || : + # Warning: Notify if global UV is outdated +ifeq ("$(UV_OUTDATED)","true") + @echo "WARNING: A newer version of uv is available. Run 'uv self update' to upgrade." +endif + + # Create virtual environment +ifeq ("$(VENV_ENABLED)", "true") +ifeq ("$(VENV_CREATE)", "true") +ifeq ("$(USE_GLOBAL_UV)","true") + @echo "Setup Python Virtual Environment using global uv at '$(VENV_FOLDER)'" + @uv venv --allow-existing --no-progress -p $(UV_PYTHON) --seed $(VENV_FOLDER) +else + @echo "Setup Python Virtual Environment using module 'venv' at '$(VENV_FOLDER)'" + @$(PRIMARY_PYTHON) -m venv $(VENV_FOLDER) + @$(MXENV_PYTHON) -m ensurepip -U +endif +endif +else + @echo "Using system Python interpreter" +endif + + # Install uv locally if needed +ifeq ("$(USE_LOCAL_UV)","true") + @echo "Install uv in virtual environment" + @$(MXENV_PYTHON) -m pip install uv +endif + + # Install/upgrade core packages + @$(PYTHON_PACKAGE_COMMAND) install -U pip setuptools wheel + @echo "Install/Update MXStack Python packages" + @$(PYTHON_PACKAGE_COMMAND) install -U $(MXDEV) $(MXMAKE) + @touch $(MXENV_TARGET) + +.PHONY: mxenv +mxenv: $(MXENV_TARGET) + +.PHONY: mxenv-dirty +mxenv-dirty: + @rm -f $(MXENV_TARGET) + +.PHONY: mxenv-clean +mxenv-clean: mxenv-dirty +ifeq ("$(VENV_ENABLED)", "true") +ifeq ("$(VENV_CREATE)", "true") + @rm -rf $(VENV_FOLDER) +endif +else + @$(PYTHON_PACKAGE_COMMAND) uninstall -y $(MXDEV) + @$(PYTHON_PACKAGE_COMMAND) uninstall -y $(MXMAKE) +endif + +INSTALL_TARGETS+=mxenv +DIRTY_TARGETS+=mxenv-dirty +CLEAN_TARGETS+=mxenv-clean + +############################################################################## +# python-ldap +############################################################################## + +# case `system.dependencies` domain is included +SYSTEM_DEPENDENCIES+=python3-dev libldap2-dev libssl-dev libsasl2-dev + +PYTHON_LDAP_TARGET:=$(SENTINEL_FOLDER)/python-ldap.sentinel +$(PYTHON_LDAP_TARGET): $(MXENV_TARGET) $(OPENLDAP_TARGET) + @$(PYTHON_PACKAGE_COMMAND) install \ + --force-reinstall \ + python-ldap + @touch $(PYTHON_LDAP_TARGET) + +.PHONY: python-ldap +python-ldap: $(PYTHON_LDAP_TARGET) + +.PHONY: python-ldap-dirty +python-ldap-dirty: + @rm -f $(PYTHON_LDAP_TARGET) + +.PHONY: python-ldap-clean +python-ldap-clean: python-ldap-dirty + @test -e $(MXENV_PYTHON) && $(MXENV_PYTHON) -m pip uninstall -y python-ldap || : + +INSTALL_TARGETS+=python-ldap +DIRTY_TARGETS+=python-ldap-dirty +CLEAN_TARGETS+=python-ldap-clean + +############################################################################## +# sources +############################################################################## + +SOURCES_TARGET:=$(SENTINEL_FOLDER)/sources.sentinel +$(SOURCES_TARGET): $(PROJECT_CONFIG) $(MXENV_TARGET) + @echo "Checkout project sources" + @mxdev -f -c $(PROJECT_CONFIG) + @touch $(SOURCES_TARGET) + +.PHONY: sources +sources: $(SOURCES_TARGET) + +.PHONY: sources-dirty +sources-dirty: + @rm -f $(SOURCES_TARGET) + +.PHONY: sources-purge +sources-purge: sources-dirty + @rm -rf sources + +INSTALL_TARGETS+=sources +DIRTY_TARGETS+=sources-dirty +PURGE_TARGETS+=sources-purge + +############################################################################## +# mxfiles +############################################################################## + +# case `core.sources` domain not included +SOURCES_TARGET?= + +# File generation target +MXMAKE_FILES?=$(MXMAKE_FOLDER)/files + +# set environment variables for mxmake +define set_mxfiles_env + @export MXMAKE_FILES=$(1) +endef + +# unset environment variables for mxmake +define unset_mxfiles_env + @unset MXMAKE_FILES +endef + +$(PROJECT_CONFIG): +ifneq ("$(wildcard $(PROJECT_CONFIG))","") + @touch $(PROJECT_CONFIG) +else + @echo "[settings]" > $(PROJECT_CONFIG) +endif + +LOCAL_PACKAGE_FILES:=$(wildcard $(PYTHON_PROJECT_PREFIX)pyproject.toml $(PYTHON_PROJECT_PREFIX)setup.cfg $(PYTHON_PROJECT_PREFIX)setup.py $(PYTHON_PROJECT_PREFIX)requirements.txt $(PYTHON_PROJECT_PREFIX)constraints.txt) + +FILES_TARGET:=requirements-mxdev.txt +$(FILES_TARGET): $(PROJECT_CONFIG) $(MXENV_TARGET) $(SOURCES_TARGET) $(LOCAL_PACKAGE_FILES) + @echo "Create project files" + @mkdir -p $(MXMAKE_FILES) + $(call set_mxfiles_env,$(MXMAKE_FILES)) + @mxdev -n -c $(PROJECT_CONFIG) + $(call unset_mxfiles_env) + @test -e $(MXMAKE_FILES)/pip.conf && cp $(MXMAKE_FILES)/pip.conf $(VENV_FOLDER)/pip.conf || : + @touch $(FILES_TARGET) + +.PHONY: mxfiles +mxfiles: $(FILES_TARGET) + +.PHONY: mxfiles-dirty +mxfiles-dirty: + @touch $(PROJECT_CONFIG) + +.PHONY: mxfiles-clean +mxfiles-clean: mxfiles-dirty + @rm -rf constraints-mxdev.txt requirements-mxdev.txt $(MXMAKE_FILES) + +INSTALL_TARGETS+=mxfiles +DIRTY_TARGETS+=mxfiles-dirty +CLEAN_TARGETS+=mxfiles-clean + +############################################################################## +# packages +############################################################################## + +# additional sources targets which requires package re-install on change +-include $(MXMAKE_FILES)/additional_sources_targets.mk +ADDITIONAL_SOURCES_TARGETS?= + +INSTALLED_PACKAGES=$(MXMAKE_FILES)/installed.txt + +ifeq ("$(PACKAGES_ALLOW_PRERELEASES)","true") +ifeq ("$(PYTHON_PACKAGE_INSTALLER)","uv") +PACKAGES_PRERELEASES=--prerelease=allow +else +PACKAGES_PRERELEASES=--pre +endif +else +PACKAGES_PRERELEASES= +endif + +PACKAGES_TARGET:=$(INSTALLED_PACKAGES) +$(PACKAGES_TARGET): $(FILES_TARGET) $(ADDITIONAL_SOURCES_TARGETS) + @echo "Install python packages" + @$(PYTHON_PACKAGE_COMMAND) install $(PACKAGES_PRERELEASES) -r $(FILES_TARGET) + @$(PYTHON_PACKAGE_COMMAND) freeze > $(INSTALLED_PACKAGES) + @touch $(PACKAGES_TARGET) + +.PHONY: packages +packages: $(PACKAGES_TARGET) + +.PHONY: packages-dirty +packages-dirty: + @rm -f $(PACKAGES_TARGET) + +.PHONY: packages-clean +packages-clean: + @test -e $(FILES_TARGET) \ + && test -e $(MXENV_PYTHON) \ + && $(MXENV_PYTHON) -m pip uninstall -y -r $(FILES_TARGET) \ + || : + @rm -f $(PACKAGES_TARGET) + +INSTALL_TARGETS+=packages +DIRTY_TARGETS+=packages-dirty +CLEAN_TARGETS+=packages-clean + +############################################################################## +# test +############################################################################## + +TEST_TARGET:=$(SENTINEL_FOLDER)/test.sentinel +$(TEST_TARGET): $(MXENV_TARGET) + @echo "Install $(TEST_REQUIREMENTS)" + @$(PYTHON_PACKAGE_COMMAND) install $(TEST_REQUIREMENTS) + @touch $(TEST_TARGET) + +.PHONY: test +test: $(FILES_TARGET) $(SOURCES_TARGET) $(PACKAGES_TARGET) $(TEST_TARGET) $(TEST_DEPENDENCY_TARGETS) + @test -z "$(TEST_COMMAND)" && echo "No test command defined" && exit 1 || : + @echo "Run tests using $(TEST_COMMAND)" + @/usr/bin/env bash -c "$(TEST_COMMAND)" + +.PHONY: test-dirty +test-dirty: + @rm -f $(TEST_TARGET) + +.PHONY: test-clean +test-clean: test-dirty + @test -e $(MXENV_PYTHON) && $(MXENV_PYTHON) -m pip uninstall -y $(TEST_REQUIREMENTS) || : + @rm -rf .pytest_cache + +INSTALL_TARGETS+=$(TEST_TARGET) +CLEAN_TARGETS+=test-clean +DIRTY_TARGETS+=test-dirty + +############################################################################## +# coverage +############################################################################## + +COVERAGE_TARGET:=$(SENTINEL_FOLDER)/coverage.sentinel +$(COVERAGE_TARGET): $(TEST_TARGET) + @echo "Install Coverage" + @$(PYTHON_PACKAGE_COMMAND) install -U coverage + @touch $(COVERAGE_TARGET) + +.PHONY: coverage +coverage: $(FILES_TARGET) $(SOURCES_TARGET) $(PACKAGES_TARGET) $(COVERAGE_TARGET) + @test -z "$(COVERAGE_COMMAND)" && echo "No coverage command defined" && exit 1 || : + @echo "Run coverage using $(COVERAGE_COMMAND)" + @/usr/bin/env bash -c "$(COVERAGE_COMMAND)" + +.PHONY: coverage-dirty +coverage-dirty: + @rm -f $(COVERAGE_TARGET) + +.PHONY: coverage-clean +coverage-clean: coverage-dirty + @test -e $(MXENV_PYTHON) && $(MXENV_PYTHON) -m pip uninstall -y coverage || : + @rm -rf .coverage htmlcov + +INSTALL_TARGETS+=$(COVERAGE_TARGET) +DIRTY_TARGETS+=coverage-dirty +CLEAN_TARGETS+=coverage-clean + +############################################################################## +# zest-releaser +############################################################################## + +ZEST_RELEASER_TARGET:=$(SENTINEL_FOLDER)/zest-releaser.sentinel +$(ZEST_RELEASER_TARGET): $(MXENV_TARGET) + @echo "Install zest.releaser" + @$(PYTHON_PACKAGE_COMMAND) install zest.releaser + @touch $(ZEST_RELEASER_TARGET) + +.PHONY: zest-releaser-prerelease +zest-releaser-prerelease: $(ZEST_RELEASER_TARGET) + @echo "Run prerelease" + @prerelease $(ZEST_RELEASER_PRERELEASE_OPTIONS) + +.PHONY: zest-releaser-release +zest-releaser-release: $(ZEST_RELEASER_TARGET) + @echo "Run release" + @release $(ZEST_RELEASER_RELEASE_OPTIONS) + +.PHONY: zest-releaser-postrelease +zest-releaser-postrelease: $(ZEST_RELEASER_TARGET) + @echo "Run postrelease" + @postrelease $(ZEST_RELEASER_POSTRELEASE_OPTIONS) + +.PHONY: zest-releaser-fullrelease +zest-releaser-fullrelease: $(ZEST_RELEASER_TARGET) + @echo "Run fullrelease" + @fullrelease $(ZEST_RELEASER_FULLRELEASE_OPTIONS) + +.PHONY: zest-releaser-dirty +zest-releaser-dirty: + @rm -f $(ZEST_RELEASER_TARGET) + +.PHONY: zest-releaser-clean +zest-releaser-clean: zest-releaser-dirty + @test -e $(MXENV_PYTHON) && $(MXENV_PYTHON) -m pip uninstall -y zest.releaser || : + +INSTALL_TARGETS+=$(ZEST_RELEASER_TARGET) +DIRTY_TARGETS+=zest-releaser-dirty +CLEAN_TARGETS+=zest-releaser-clean + +############################################################################## +# gettext +############################################################################## + +# case `system.dependencies` domain is included +SYSTEM_DEPENDENCIES+=gettext + +.PHONY: gettext-create +gettext-create: + @if [ ! -e "$(GETTEXT_LOCALES_PATH)/$(GETTEXT_DOMAIN).pot" ]; then \ + echo "Create pot file"; \ + mkdir -p "$(GETTEXT_LOCALES_PATH)"; \ + touch "$(GETTEXT_LOCALES_PATH)/$(GETTEXT_DOMAIN).pot"; \ + fi + @for lang in $(GETTEXT_LANGUAGES); do \ + if [ ! -e "$(GETTEXT_LOCALES_PATH)/$$lang/LC_MESSAGES/$(GETTEXT_DOMAIN).po" ]; then \ + mkdir -p "$(GETTEXT_LOCALES_PATH)/$$lang/LC_MESSAGES"; \ + msginit \ + -i "$(GETTEXT_LOCALES_PATH)/$(GETTEXT_DOMAIN).pot" \ + -o "$(GETTEXT_LOCALES_PATH)/$$lang/LC_MESSAGES/$(GETTEXT_DOMAIN).po" \ + -l $$lang; \ + fi \ + done + +.PHONY: gettext-update +gettext-update: + @echo "Update translations" + @for lang in $(GETTEXT_LANGUAGES); do \ + msgmerge -o \ + "$(GETTEXT_LOCALES_PATH)/$$lang/LC_MESSAGES/$(GETTEXT_DOMAIN).po" \ + "$(GETTEXT_LOCALES_PATH)/$$lang/LC_MESSAGES/$(GETTEXT_DOMAIN).po" \ + "$(GETTEXT_LOCALES_PATH)/$(GETTEXT_DOMAIN).pot"; \ + done + +.PHONY: gettext-compile +gettext-compile: + @echo "Compile message catalogs" + @for lang in $(GETTEXT_LANGUAGES); do \ + msgfmt --statistics -o \ + "$(GETTEXT_LOCALES_PATH)/$$lang/LC_MESSAGES/$(GETTEXT_DOMAIN).mo" \ + "$(GETTEXT_LOCALES_PATH)/$$lang/LC_MESSAGES/$(GETTEXT_DOMAIN).po"; \ + done + +############################################################################## +# lingua +############################################################################## + +LINGUA_TARGET:=$(SENTINEL_FOLDER)/lingua.sentinel +$(LINGUA_TARGET): $(MXENV_TARGET) + @echo "Install Lingua" + @$(PYTHON_PACKAGE_COMMAND) install chameleon lingua $(LINGUA_PLUGINS) + @touch $(LINGUA_TARGET) + +PHONY: lingua-extract +lingua-extract: $(LINGUA_TARGET) + @echo "Extract messages" + @pot-create \ + "$(LINGUA_SEARCH_PATH)" $(LINGUA_OPTIONS) \ + -o "$(GETTEXT_LOCALES_PATH)/$(GETTEXT_DOMAIN).pot" + +PHONY: lingua +lingua: gettext-create lingua-extract gettext-update gettext-compile + +.PHONY: lingua-dirty +lingua-dirty: + @rm -f $(LINGUA_TARGET) + +.PHONY: lingua-clean +lingua-clean: lingua-dirty + @test -e $(MXENV_PYTHON) && $(MXENV_PYTHON) -m pip uninstall -y \ + chameleon lingua $(LINGUA_PLUGINS) || : + +INSTALL_TARGETS+=$(LINGUA_TARGET) +DIRTY_TARGETS+=lingua-dirty +CLEAN_TARGETS+=lingua-clean + +############################################################################## +# Custom includes +############################################################################## + +-include $(INCLUDE_MAKEFILE) + +############################################################################## +# Default targets +############################################################################## + +INSTALL_TARGET:=$(SENTINEL_FOLDER)/install.sentinel +$(INSTALL_TARGET): $(INSTALL_TARGETS) + @touch $(INSTALL_TARGET) + +.PHONY: install +install: $(INSTALL_TARGET) + @touch $(INSTALL_TARGET) + +.PHONY: run +run: $(RUN_TARGET) + +.PHONY: deploy +deploy: $(DEPLOY_TARGETS) + +.PHONY: dirty +dirty: $(DIRTY_TARGETS) + @rm -f $(INSTALL_TARGET) + +.PHONY: clean +clean: dirty $(CLEAN_TARGETS) + @rm -rf $(CLEAN_TARGETS) $(MXMAKE_FOLDER) $(CLEAN_FS) + +.PHONY: purge +purge: clean $(PURGE_TARGETS) + +.PHONY: runtime-clean +runtime-clean: + @echo "Remove runtime artifacts, like byte-code and caches." + @find . -name '*.py[c|o]' -delete + @find . -name '*~' -exec rm -f {} + + @find . -name '__pycache__' -exec rm -fr {} + + +.PHONY: check +check: $(CHECK_TARGETS) + +.PHONY: typecheck +typecheck: $(TYPECHECK_TARGETS) + +.PHONY: format +format: $(FORMAT_TARGETS) diff --git a/README.rst b/README.rst index cafd9fc..1592e31 100644 --- a/README.rst +++ b/README.rst @@ -6,11 +6,9 @@ :target: https://pypi.python.org/pypi/cone.ldap :alt: Number of PyPI downloads -.. image:: https://travis-ci.org/bluedynamics/cone.ldap.svg?branch=master - :target: https://travis-ci.org/bluedynamics/cone.ldap - -.. image:: https://coveralls.io/repos/github/bluedynamics/cone.ldap/badge.svg?branch=master - :target: https://coveralls.io/github/bluedynamics/cone.ldap?branch=master +.. image:: https://github.com/conestack/cone.ldap/actions/workflows/test.yml/badge.svg + :target: https://github.com/conestack/cone.ldap/actions/workflows/test.yml + :alt: Test cone.ldap Plugin for `cone.app `_ providing LDAP integration. @@ -46,24 +44,24 @@ On debian based systems install: Installation ------------ -``cone.ldap`` contains a buildout configuration. Download or checkout package +``cone.ldap`` contains a Makefile. Download or checkout package and run: .. code-block:: shell - cone.ldap$ ./bootstrap.sh python3 + cone.ldap$ make install Start Test LDAP server with appropriate LDIF layer: .. code-block:: shell - cone.ldap$ ./bin/testldap start groupOfNames_10_10 + cone.ldap$ make start-ldap-gon-10-10 Start the application: .. code-block:: shell - cone.ldap$ ./bin/pserve cfg/gon_10_10/ldap_gon_10_10.ini + cone.ldap$ make run-gon-10-10 and browse ``http://localhost:8081/``. Default ``admin`` user password is ``admin``. diff --git a/bootstrap.sh b/bootstrap.sh deleted file mode 100755 index 430cef9..0000000 --- a/bootstrap.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -for dir in lib include local bin share parts; do - if [ -d "$dir" ]; then - rm -rf "$dir" - fi -done - -PY=$1 -if [ "$PY" == "" ]; then - PY=python2 -fi - -virtualenv -p $PY . -./bin/pip install -U pip wheel setuptools zc.buildout -./bin/buildout -N diff --git a/buildout.cfg b/buildout.cfg deleted file mode 100644 index 282b866..0000000 --- a/buildout.cfg +++ /dev/null @@ -1,35 +0,0 @@ -[buildout] -extends = etc/base.cfg - -extensions = mr.developer -sources-dir = ${buildout:directory}/devsrc -always-checkout = force -auto-checkout = * - -[remotes] -cs = https://github.com/conestack -cs_push = git@github.com:conestack -bda = https://github.com/bluedynamics -bda_push = git@github.com:bluedynamics - -[sources] -odict = git ${remotes:cs}/odict.git pushurl=${remotes:cs_push}/odict.git -plumber = git ${remotes:cs}/plumber.git pushurl=${remotes:cs_push}/plumber.git -node = git ${remotes:cs}/node.git pushurl=${remotes:cs_push}/node.git -node.ext.ugm = git ${remotes:cs}/node.ext.ugm.git pushurl=${remotes:cs_push}/node.ext.ugm.git -node.ext.ldap = git ${remotes:cs}/node.ext.ldap.git pushurl=${remotes:cs_push}/node.ext.ldap.git -yafowil = git ${remotes:cs}/yafowil.git pushurl=${remotes:cs_push}/yafowil.git -yafowil.yaml = git ${remotes:cs}/yafowil.yaml.git pushurl=${remotes:cs_push}/yafowil.yaml.git -yafowil.webob = git ${remotes:cs}/yafowil.webob.git pushurl=${remotes:cs_push}/yafowil.webob.git -yafowil.bootstrap = git ${remotes:cs}/yafowil.bootstrap.git pushurl=${remotes:cs_push}/yafowil.bootstrap.git -yafowil.widget.array = git ${remotes:cs}/yafowil.widget.array.git pushurl=${remotes:cs_push}/yafowil.widget.array.git -yafowil.widget.autocomplete = git ${remotes:cs}/yafowil.widget.autocomplete.git pushurl=${remotes:cs_push}/yafowil.widget.autocomplete.git -yafowil.widget.datetime = git ${remotes:cs}/yafowil.widget.datetime.git pushurl=${remotes:cs_push}/yafowil.widget.datetime.git -yafowil.widget.dict = git ${remotes:cs}/yafowil.widget.dict.git pushurl=${remotes:cs_push}/yafowil.widget.dict.git -yafowil.widget.image = git ${remotes:cs}/yafowil.widget.image.git pushurl=${remotes:cs_push}/yafowil.widget.image.git -cone.tile = git ${remotes:cs}/cone.tile.git pushurl=${remotes:cs_push}/cone.tile.git -cone.app = git ${remotes:cs}/cone.app.git pushurl=${remotes:cs_push}/cone.app.git -cone.ugm = git ${remotes:cs}/cone.ugm.git pushurl=${remotes:cs_push}/cone.ugm.git -bdajax = git ${remotes:cs}/bdajax.git pushurl=${remotes:cs_push}/bdajax.git -bda.cache = git ${remotes:bda}/bda.cache.git pushurl=${remotes:bda_push}/bda.cache.git -bda.intellidatetime = git ${remotes:bda}/bda.intellidatetime.git pushurl=${remotes:bda_push}/bda.intellidatetime.git diff --git a/etc/base.cfg b/etc/base.cfg deleted file mode 100644 index aaa8d4a..0000000 --- a/etc/base.cfg +++ /dev/null @@ -1,81 +0,0 @@ -[buildout] -# lxml requirements - apt-get install libxml2-dev libxslt1-dev -extends = - ldap.cfg - versions.cfg - -develop = . - -parts += - instance - test - coverage - testldap - py - -find-links = - http://effbot.org/downloads - -[instance] -recipe = zc.recipe.egg:scripts -dependent-scripts = true -eggs = - ${python-ldap:egg} - waitress - cone.ldap - cone.ugm - -[test] -recipe = zc.recipe.testrunner -eggs = - odict - plumber[test] - yafowil[test] - yafowil.yaml[test] - yafowil.webob - yafowil.widget.array - yafowil.widget.autocomplete - yafowil.widget.datetime - yafowil.widget.dict - yafowil.widget.image - cone.tile[test] - cone.app[test] - cone.ugm[test] - cone.ldap[test] - node.ext.ldap[test] - node.ext.ugm - node -environment = testenv -defaults = ['--auto-color', '--auto-progress'] - -[coverage] -recipe = zc.recipe.egg:scripts -eggs = - coverage - ${test:eggs} -initialization = - import os - os.environ['ADDITIONAL_LDIF_LAYERS'] = '${testenv:ADDITIONAL_LDIF_LAYERS}' - os.environ['SLAPD_BIN'] = '${testenv:SLAPD_BIN}' - os.environ['SLAPD_URIS'] = '${testenv:SLAPD_URIS}' - os.environ['LDAP_DELETE_BIN'] = '${testenv:LDAP_DELETE_BIN}' - os.environ['LDAP_ADD_BIN'] = '${testenv:LDAP_ADD_BIN}' - -[coveralls] -recipe = zc.recipe.egg:scripts -eggs = - coveralls - ${test:eggs} - -[py] -recipe = zc.recipe.egg -eggs = - ${test:eggs} -initialization = - import os - os.environ['ADDITIONAL_LDIF_LAYERS'] = '${testenv:ADDITIONAL_LDIF_LAYERS}' - os.environ['SLAPD_BIN'] = '${testenv:SLAPD_BIN}' - os.environ['SLAPD_URIS'] = '${testenv:SLAPD_URIS}' - os.environ['LDAP_DELETE_BIN'] = '${testenv:LDAP_DELETE_BIN}' - os.environ['LDAP_ADD_BIN'] = '${testenv:LDAP_ADD_BIN}' -interpreter = py diff --git a/etc/ldap.cfg b/etc/ldap.cfg deleted file mode 100644 index 2a8d4c6..0000000 --- a/etc/ldap.cfg +++ /dev/null @@ -1,45 +0,0 @@ -[buildout] -parts = - openldap - python-ldap - -[openldap] -# this build needs (on debian based systems): -# apt-get install libsasl2-dev libssl-dev libdb-dev -recipe = zc.recipe.cmmi>=1.1.5 -url = http://mirror.eu.oneandone.net/software/openldap/openldap-release/openldap-2.4.59.tgz -extra_options = --with-sasl --with-tls --enable-slapd=yes --enable-overlays -#osx-env -#environment = -# CPPFLAGS=-I/opt/local/include/db44 -# LDFLAGS=-L/opt/local/lib/db44 - -[python-ldap] -recipe = zc.recipe.egg:custom -egg = python-ldap -include-dirs = - ${openldap:location}/include -library-dirs = - ${openldap:location}/lib -rpath = - ${openldap:location}/lib - -[testenv] -TESTRUN_MARKER=1 -LDAP_ADD_BIN = ${openldap:location}/bin/ldapadd -LDAP_DELETE_BIN = ${openldap:location}/bin/ldapdelete -SLAPD_BIN = ${openldap:location}/libexec/slapd -SLAPD_URIS = ldap://127.0.0.1:12345 -ADDITIONAL_LDIF_LAYERS = - -[testldap] -recipe = zc.recipe.egg:script -eggs = - node.ext.ldap[test] -initialization = - import os - os.environ['ADDITIONAL_LDIF_LAYERS'] = '${testenv:ADDITIONAL_LDIF_LAYERS}' - os.environ['SLAPD_BIN'] = '${testenv:SLAPD_BIN}' - os.environ['SLAPD_URIS'] = '${testenv:SLAPD_URIS}' - os.environ['LDAP_DELETE_BIN'] = '${testenv:LDAP_DELETE_BIN}' - os.environ['LDAP_ADD_BIN'] = '${testenv:LDAP_ADD_BIN}' diff --git a/etc/versions.cfg b/etc/versions.cfg deleted file mode 100644 index 94ce3bf..0000000 --- a/etc/versions.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[buildout] -versions = versions - -[versions] -zc.buildout = -setuptools = -pyramid = 1.9.4 diff --git a/i18n.sh b/i18n.sh deleted file mode 100755 index 1902556..0000000 --- a/i18n.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# Usage: -# Initial catalog creation (lang is the language identifier): -# ./i18n.sh lang -# Updating translation and compile catalog: -# ./i18n.sh - -# configuration -DOMAIN="cone.ldap" -SEARCH_PATH="src/cone/ldap" -LOCALES_PATH="src/cone/ldap/locale" -# end configuration - -# create locales folder if not exists -if [ ! -d "$LOCALES_PATH" ]; then - echo "Locales directory not exists, create" - mkdir -p "$LOCALES_PATH" -fi - -# create pot if not exists -if [ ! -f "$LOCALES_PATH"/$DOMAIN.pot ]; then - echo "Create pot file" - touch "$LOCALES_PATH"/$DOMAIN.pot -fi - -# no arguments, extract and update -if [ $# -eq 0 ]; then - echo "Extract messages" - pot-create "$SEARCH_PATH" -o "$LOCALES_PATH"/$DOMAIN.pot - - echo "Update translations" - for po in "$LOCALES_PATH"/*/LC_MESSAGES/$DOMAIN.po; do - msgmerge -o "$po" "$po" "$LOCALES_PATH"/$DOMAIN.pot - done - - echo "Compile message catalogs" - for po in "$LOCALES_PATH"/*/LC_MESSAGES/*.po; do - msgfmt -o "${po%.*}.mo" "$po" - done - -# first argument represents language identifier, create catalog -else - cd "$LOCALES_PATH" - mkdir -p $1/LC_MESSAGES - msginit -i $DOMAIN.pot -o $1/LC_MESSAGES/$DOMAIN.po -l $1 -fi diff --git a/include.mk b/include.mk new file mode 100644 index 0000000..c27dbeb --- /dev/null +++ b/include.mk @@ -0,0 +1,56 @@ +############################################################################## +# test ldap server +############################################################################## + +define set_testldap_env + @export LDAP_ADD_BIN=$(OPENLDAP_DIR)/bin/ldapadd + @export LDAP_DELETE_BIN=$(OPENLDAP_DIR)/bin/ldapdelete + @export SLAPD_BIN=$(OPENLDAP_DIR)/libexec/slapd + @export SLAPD_URIS=ldap://127.0.0.1:12345 + @export ADDITIONAL_LDIF_LAYERS= +endef + +.PHONY: start-ldap-base +start-ldap-base: $(INSTALL_TARGETS) + $(call set_testldap_env) + @$(VENV_FOLDER)/bin/testldap start base + +.PHONY: start-ldap-gon-10-10 +start-ldap-gon-10-10: $(INSTALL_TARGETS) + $(call set_testldap_env) + @$(VENV_FOLDER)/bin/testldap start groupOfNames_10_10 + +.PHONY: start-ldap-gon-100-100 +start-ldap-gon-100-100: $(INSTALL_TARGETS) + $(call set_testldap_env) + @$(VENV_FOLDER)/bin/testldap start groupOfNames_100_100 + +.PHONY: start-ldap-posix +start-ldap-posix: $(INSTALL_TARGETS) + $(call set_testldap_env) + @$(VENV_FOLDER)/bin/testldap start posixGroups + +.PHONY: stop-ldap +stop-ldap: $(INSTALL_TARGETS) + $(call set_testldap_env) + @$(VENV_FOLDER)/bin/testldap stop + +############################################################################## +# test instance +############################################################################## + +.PHONY: run-base +run-base: $(INSTALL_TARGETS) + @$(VENV_FOLDER)/bin/pserve cfg/base/ldap_base.ini + +.PHONY: run-gon-10-10 +run-gon-10-10: $(INSTALL_TARGETS) + @$(VENV_FOLDER)/bin/pserve cfg/gon_10_10/ldap_gon_10_10.ini + +.PHONY: run-gon-100-100 +run-gon-100-100: $(INSTALL_TARGETS) + @$(VENV_FOLDER)/bin/pserve cfg/gon_100_100/ldap_gon_100_100.ini + +.PHONY: run-posix +run-posix: $(INSTALL_TARGETS) + @$(VENV_FOLDER)/bin/pserve cfg/posix/ldap_posix.ini diff --git a/mx.ini b/mx.ini new file mode 100644 index 0000000..df84165 --- /dev/null +++ b/mx.ini @@ -0,0 +1,232 @@ +[settings] +threads = 5 + +# constack git URLs +cs = https://github.com/conestack +cs_push = git@github.com:conestack + +# checkout source packages +checkout_packages = true + +# feature branch to checkout +feature_branch = refactor-package-layout + +# main package +main-package = -e .[test] + +# fixed dependency package versions +version-overrides = + pyramid==2.0.2 + +mxmake-templates = + run-tests + run-coverage + +mxmake-test-path = src +mxmake-source-path = src/cone/ldap + +[mxmake-env] +TESTRUN_MARKER = 1 +LDAP_ADD_BIN = openldap/bin/ldapadd +LDAP_DELETE_BIN = openldap/bin/ldapdelete +SLAPD_BIN = openldap/libexec/slapd +SLAPD_URIS = ldap://127.0.0.1:12345 +ADDITIONAL_LDIF_LAYERS = + +[mxmake-run-tests] +environment = env + +[mxmake-run-coverage] +environment = env + +############################################################################### +# base packages +############################################################################### + +[odict] +use = ${settings:checkout_packages} +url = ${settings:cs}/odict.git +pushurl = ${settings:cs_push}/odict.git +branch = ${settings:feature_branch} +mxmake-test-path = tests +mxmake-source-path = src/odict + +[plumber] +use = ${settings:checkout_packages} +url = ${settings:cs}/plumber.git +pushurl = ${settings:cs_push}/plumber.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = tests +mxmake-source-path = src/plumber + +############################################################################### +# node packages +############################################################################### + +[node] +use = ${settings:checkout_packages} +url = ${settings:cs}/node.git +pushurl = ${settings:cs_push}/node.git +branch = ${settings:feature_branch} +mxmake-test-path = src +mxmake-source-path = src/node + +[node.ext.ugm] +use = ${settings:checkout_packages} +url = ${settings:cs}/node.ext.ugm.git +pushurl = ${settings:cs_push}/node.ext.ugm.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/node/ext/ugm + +[node.ext.ldap] +use = ${settings:checkout_packages} +url = ${settings:cs}/node.ext.ldap.git +pushurl = ${settings:cs_push}/node.ext.ldap.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/node/ext/ldap + +############################################################################### +# yafowil packages +############################################################################### + +[yafowil] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.git +pushurl = ${settings:cs_push}/yafowil.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/yafowil + +[yafowil.yaml] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.yaml.git +pushurl = ${settings:cs_push}/yafowil.yaml.git +branch = ${settings:feature_branch} +mxmake-test-path = src +mxmake-source-path = src/yafowil/yaml + +[yafowil.lingua] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.lingua.git +pushurl = ${settings:cs_push}/yafowil.lingua.git +branch = ${settings:feature_branch} +mxmake-test-path = src +mxmake-source-path = src/yafowil/lingua + +[yafowil.webob] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.webob.git +pushurl = ${settings:cs_push}/yafowil.webob.git +branch = ${settings:feature_branch} +mxmake-test-path = src +mxmake-source-path = src/yafowil/webob + +[yafowil.bootstrap] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.bootstrap.git +pushurl = ${settings:cs_push}/yafowil.bootstrap.git +branch = ${settings:feature_branch} +mxmake-test-path = src +mxmake-source-path = src/yafowil/bootstrap + +[yafowil.widget.array] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.widget.array.git +pushurl = ${settings:cs_push}/yafowil.widget.array.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/yafowil/widget/array +mxmake-omit-path = src/yafowil/widget/array/example.py + +[yafowil.widget.autocomplete] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.widget.autocomplete.git +pushurl = ${settings:cs_push}/yafowil.widget.autocomplete.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/yafowil/widget/autocomplete +mxmake-omit-path = src/yafowil/widget/autocomplete/example.py + +[yafowil.widget.datetime] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.widget.datetime.git +pushurl = ${settings:cs_push}/yafowil.widget.datetime.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/yafowil/widget/datetime +mxmake-omit-path = src/yafowil/widget/datetime/example.py + +[yafowil.widget.dict] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.widget.dict.git +pushurl = ${settings:cs_push}/yafowil.widget.dict.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/yafowil/widget/dict +mxmake-omit-path = src/yafowil/widget/dict/example.py + +[yafowil.widget.image] +use = ${settings:checkout_packages} +url = ${settings:cs}/yafowil.widget.image.git +pushurl = ${settings:cs_push}/yafowil.widget.image.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/yafowil/widget/image +mxmake-omit-path = src/yafowil/widget/image/example.py + +############################################################################### +# cone packages +############################################################################### + +[treibstoff] +use = ${settings:checkout_packages} +url = ${settings:cs}/treibstoff.git +pushurl = ${settings:cs_push}/treibstoff.git +branch = ${settings:feature_branch} + +[webresource] +use = ${settings:checkout_packages} +url = ${settings:cs}/webresource.git +pushurl = ${settings:cs_push}/webresource.git +branch = master +extras = test +mxmake-test-path = tests +mxmake-source-path = webresource + +[cone.tile] +use = ${settings:checkout_packages} +url = ${settings:cs}/cone.tile.git +pushurl = ${settings:cs_push}/cone.tile.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/cone/tile + +[cone.app] +use = ${settings:checkout_packages} +url = ${settings:cs}/cone.app.git +pushurl = ${settings:cs_push}/cone.app.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/cone/app + +[cone.ugm] +use = ${settings:checkout_packages} +url = ${settings:cs}/cone.ugm.git +pushurl = ${settings:cs_push}/cone.ugm.git +branch = ${settings:feature_branch} +extras = test +mxmake-test-path = src +mxmake-source-path = src/cone/ugm diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2b015c7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,70 @@ +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[project] +name = "cone.ldap" +version = "1.1.0.dev0" +description = "LDAP integration for cone.app" +dynamic = ["readme"] +requires-python = ">=3.10" +license = {text = "Simplified BSD"} +authors = [{name = "Cone Contributors", email = "dev@conestack.org"}] +classifiers = [ + "Environment :: Web Environment", + "Programming Language :: Python", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", + "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", +] +dependencies = [ + "node.ext.ldap>1.99", + "cone.ugm>1.0.99,<2.0.0", + "yafowil.widget.array>1.99", + "yafowil.widget.dict>1.99", + "yafowil.yaml>2.99" +] + +[project.optional-dependencies] +test = [ + "cone.ugm[test]", + "node.ext.ldap[test]", +] + +[project.urls] +Homepage = "http://github.com/conestack/cone.ldap" + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/x-rst" +fragments = [ + {path = "README.rst"}, + {text = "\n\n"}, + {path = "CHANGES.rst"}, + {text = "\n\n"}, + {path = "LICENSE.rst"}, +] + +[tool.hatch.build.targets.sdist] +exclude = [ + "/.github/", + "/Makefile", + "/mx.ini", + "/package.json", + "/pnpm-lock.yaml", + "include.mk", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/cone"] + +[tool.pytest.ini_options] +consider_namespace_packages = true +addopts = ["--import-mode=importlib"] +pythonpath = "src" + +[tool.zest-releaser] +create-wheel = true diff --git a/run_tests.sh b/run_tests.sh deleted file mode 100755 index b2125ad..0000000 --- a/run_tests.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -e - -export LDAP_ADD_BIN=parts/openldap/bin/ldapadd -export LDAP_DELETE_BIN=parts/openldap/bin/ldapdelete -export SLAPD_BIN=parts/openldap/libexec/slapd -export SLAPD_URIS=ldap://127.0.0.1:12345 -export ADDITIONAL_LDIF_LAYERS= - -./bin/py -m cone.ugm.tests.__init__ -./bin/py -m cone.ldap.tests.__init__ - -unset LDAP_ADD_BIN -unset LDAP_DELETE_BIN -unset SLAPD_BIN -unset SLAPD_URIS -unset ADDITIONAL_LDIF_LAYERS diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 81049c3..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[zest.releaser] -create-wheel = yes diff --git a/setup.py b/setup.py deleted file mode 100644 index 040cc69..0000000 --- a/setup.py +++ /dev/null @@ -1,70 +0,0 @@ -from setuptools import find_packages -from setuptools import setup -from setuptools.command.test import test -import os - - -def read_file(name): - with open(os.path.join(os.path.dirname(__file__), name)) as f: - return f.read() - - -version = '0.3.dev0' -shortdesc = 'LDAP integration for cone.app' -longdesc = '\n\n'.join([read_file(name) for name in [ - 'README.rst', - 'CHANGES.rst', - 'LICENSE.rst' -]]) - - -class Test(test): - - def run_tests(self): - from cone.ldap import tests - tests.run_tests() - - -setup( - name='cone.ldap', - version=version, - description=shortdesc, - long_description=longdesc, - classifiers=[ - 'Environment :: Web Environment', - 'Programming Language :: Python', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)' - ], - keywords='', - author='Cone Contributors', - author_email='dev@conestack.org', - url='http://github.com/conestack/cone.ldap', - license='Simplified BSD', - packages=find_packages('src'), - package_dir={'': 'src'}, - namespace_packages=['cone'], - include_package_data=True, - zip_safe=False, - install_requires=[ - 'setuptools', - 'node.ext.ldap', - 'cone.ugm', - 'yafowil.widget.array', - 'yafowil.widget.dict', - 'yafowil.yaml' - ], - extras_require=dict( - test=[ - 'lxml', - 'yafowil.yaml', - 'zope.testrunner' - ] - ), - tests_require=[ - 'lxml', - 'yafowil.yaml', - 'zope.testrunner' - ], - cmdclass=dict(test=Test) -) diff --git a/src/cone/__init__.py b/src/cone/__init__.py deleted file mode 100644 index de40ea7..0000000 --- a/src/cone/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__('pkg_resources').declare_namespace(__name__) diff --git a/src/cone/ldap/__init__.py b/src/cone/ldap/__init__.py index d4d954a..f234ecb 100644 --- a/src/cone/ldap/__init__.py +++ b/src/cone/ldap/__init__.py @@ -1,11 +1,9 @@ -from cone.app import cfg from cone.app import get_root from cone.app import main_hook from cone.app import register_config from cone.app.ugm import ugm_backend from cone.app.ugm import UGMFactory -from cone.ldap import browser -from cone.ldap.browser import static_resources +from cone.ldap.browser import configure_resources from cone.ldap.settings import ldap_cfg from cone.ldap.settings import LDAPGroupsSettings from cone.ldap.settings import LDAPRolesSettings @@ -36,13 +34,13 @@ def initialize_ldap(config, global_config, settings): register_config('ldap_groups', LDAPGroupsSettings) register_config('ldap_roles', LDAPRolesSettings) - # custom UGM styles - cfg.merged.css.protected.append((static_resources, 'styles.css')) - # add translation config.add_translation_dirs('cone.ldap:locale/') - config.scan(browser) + # static resources + configure_resources(config, settings) + + config.scan('cone.ldap.browser') @ugm_backend('ldap') diff --git a/src/cone/ldap/browser/__init__.py b/src/cone/ldap/browser/__init__.py index 4b50200..2139c82 100644 --- a/src/cone/ldap/browser/__init__.py +++ b/src/cone/ldap/browser/__init__.py @@ -1,10 +1,8 @@ from cone.ugm.browser.principal import default_form_field_factory from cone.ugm.browser.principal import user_field from functools import partial -from pyramid.static import static_view - - -static_resources = static_view('static', use_subpath=True) +import os +import webresource as wr ldap_cn_field_factory = user_field('cn', backend='ldap')( @@ -13,3 +11,21 @@ ldap_sn_field_factory = user_field('sn', backend='ldap')( partial(default_form_field_factory, required=True) ) + +resources_dir = os.path.join(os.path.dirname(__file__), 'static') +cone_ldap_resources = wr.ResourceGroup( + name='cone.ldap-ldap', + directory=resources_dir, + path='ldap' +) +cone_ldap_resources.add(wr.StyleResource( + name='cone-ldap-css', + resource='cone.ldap.css' +)) + + +def configure_resources(config, settings): + config.register_resource(cone_ldap_resources) + + include = 'authenticated' if settings.get('ugm.backend') == 'ldap' else False + config.set_resource_include('cone-ldap-css', include) diff --git a/src/cone/ldap/browser/settings.py b/src/cone/ldap/browser/settings.py index 79497d1..37ddcce 100644 --- a/src/cone/ldap/browser/settings.py +++ b/src/cone/ldap/browser/settings.py @@ -1,10 +1,10 @@ -from cone.app.browser.ajax import AjaxAction +from cone.app.browser.ajax import AjaxEvent from cone.app.browser.ajax import ajax_continue from cone.app.browser.ajax import ajax_message from cone.app.browser.form import Form from cone.app.browser.form import YAMLForm -from cone.app.browser.layout import ProtectedContentTile -from cone.app.browser.settings import SettingsBehavior +from cone.app.browser.settings import SettingsForm +from cone.app.browser.settings import settings_form from cone.app.browser.utils import make_url from cone.app.ugm import ugm_backend from cone.ldap.settings import LDAPGroupsSettings @@ -52,23 +52,24 @@ def ldap_connectivity(self): return self.model.parent['ldap_server'].ldap_connectivity +@tile(name='create_container', interface=LDAPUsersSettings, permission='manage') +@tile(name='create_container', interface=LDAPGroupsSettings, permission='manage') +@tile(name='create_container', interface=LDAPRolesSettings, permission='manage') class CreateContainerAction(Tile): @property def continuation(self): - raise NotImplementedError( - 'Abstract ``CreateContainerAction`` ' - 'does not implement ``continuation``' - ) + url = make_url(self.request, node=self.model) + return AjaxEvent(url, 'contextchanged', '#layout') def render(self): + localizer = get_localizer(self.request) try: - message = self.model.create_container() + message = localizer.translate(self.model.create_container()) ajax_message(self.request, message, 'info') continuation = self.continuation ajax_continue(self.request, continuation) except Exception as e: - localizer = get_localizer(self.request) message = localizer.translate(_( 'cannot_create_container', default="Cannot create container: ${error}", @@ -116,22 +117,10 @@ def additional_aliases_extractor(self, widget, data): return extracted -@tile( - name='content', - path='templates/server_settings.pt', +@settings_form( interface=LDAPServerSettings, - permission='manage') -class ServerSettingsTile(ProtectedContentTile): - - @property - def ldap_status(self): - if self.model.ldap_connectivity: - return 'OK' - return _('server_down', default='Down') - - -@tile(name='editform', interface=LDAPServerSettings, permission='manage') -@plumbing(SettingsBehavior, YAMLForm) + path='cone.ldap.browser:templates/server_settings.pt') +@plumbing(SettingsForm, YAMLForm) class ServerSettingsForm(Form): action_resource = u'edit' form_template = 'cone.ldap.browser:forms/server_settings.yaml' @@ -140,6 +129,12 @@ class ServerSettingsForm(Form): def message_factory(self): return _ + @property + def ldap_status(self): + if self.model.ldap_connectivity: + return 'OK' + return _('server_down', default='Down') + def save(self, widget, data): model = self.model for attr_name in ['uri', 'user']: @@ -156,32 +151,11 @@ def save(self, widget, data): initialize_ugm_backend() -@tile( - name='content', - path='templates/users_settings.pt', +@settings_form( interface=LDAPUsersSettings, - permission='manage') -class UsersSettingsTile(ProtectedContentTile, CreateContainerTrigger): - - @property - def ldap_users(self): - if self.model.container_exists: - return 'OK' - return _('inexistent', default='Inexistent') - - -@tile(name='create_container', interface=LDAPUsersSettings, permission='manage') -class UsersCreateContainerAction(CreateContainerAction): - - @property - def continuation(self): - url = make_url(self.request, node=self.model) - return AjaxAction(url, 'content', 'inner', '.ldap_users') - - -@tile(name='editform', interface=LDAPUsersSettings, permission='manage') -@plumbing(SettingsBehavior, YAMLForm) -class UsersSettingsForm(Form, ScopeVocabMixin, AliasesMixin): + path='cone.ldap.browser:templates/users_settings.pt') +@plumbing(SettingsForm, YAMLForm) +class UsersSettingsForm(Form, ScopeVocabMixin, AliasesMixin, CreateContainerTrigger): action_resource = u'edit' form_template = 'cone.ldap.browser:forms/users_settings.yaml' reserved_alias_names = ['rdn', 'id', 'password'] @@ -191,6 +165,12 @@ class UsersSettingsForm(Form, ScopeVocabMixin, AliasesMixin): def message_factory(self): return _ + @property + def ldap_users(self): + if self.model.container_exists: + return 'OK' + return _('inexistent', default='Inexistent') + def required_if_users_account_expiration(self, widget, data): extracted = data.extracted if extracted is UNSET: @@ -225,32 +205,11 @@ def save(self, widget, data): initialize_ugm_backend() -@tile( - name='content', - path='templates/groups_settings.pt', +@settings_form( interface=LDAPGroupsSettings, - permission='manage') -class GroupsSettingsTile(ProtectedContentTile, CreateContainerTrigger): - - @property - def ldap_groups(self): - if self.model.container_exists: - return 'OK' - return _('inexistent', default='Inexistent') - - -@tile(name='create_container', interface=LDAPGroupsSettings, permission='manage') -class GroupsCreateContainerAction(CreateContainerAction): - - @property - def continuation(self): - url = make_url(self.request, node=self.model) - return AjaxAction(url, 'content', 'inner', '.ldap_groups') - - -@tile(name='editform', interface=LDAPGroupsSettings, permission='manage') -@plumbing(SettingsBehavior, YAMLForm) -class GroupsSettingsForm(Form, ScopeVocabMixin, AliasesMixin): + path='cone.ldap.browser:templates/groups_settings.pt') +@plumbing(SettingsForm, YAMLForm) +class GroupsSettingsForm(Form, ScopeVocabMixin, AliasesMixin, CreateContainerTrigger): action_resource = u'edit' form_template = 'cone.ldap.browser:forms/groups_settings.yaml' reserved_alias_names = ['rdn', 'id'] @@ -260,6 +219,12 @@ class GroupsSettingsForm(Form, ScopeVocabMixin, AliasesMixin): def message_factory(self): return _ + @property + def ldap_groups(self): + if self.model.container_exists: + return 'OK' + return _('inexistent', default='Inexistent') + def save(self, widget, data): model = self.model for attr_name in [ @@ -281,32 +246,11 @@ def save(self, widget, data): initialize_ugm_backend() -@tile( - name='content', - path='templates/roles_settings.pt', +@settings_form( interface=LDAPRolesSettings, - permission='manage') -class RolesSettingsTile(ProtectedContentTile, CreateContainerTrigger): - - @property - def ldap_roles(self): - if self.model.container_exists: - return 'OK' - return _('inexistent', default='Inexistent') - - -@tile(name='create_container', interface=LDAPRolesSettings, permission='manage') -class RolesCreateContainerAction(CreateContainerAction): - - @property - def continuation(self): - url = make_url(self.request, node=self.model) - return AjaxAction(url, 'content', 'inner', '.ldap_roles') - - -@tile(name='editform', interface=LDAPRolesSettings, permission='manage') -@plumbing(SettingsBehavior, YAMLForm) -class RolesSettingsForm(Form, ScopeVocabMixin): + path='cone.ldap.browser:templates/roles_settings.pt') +@plumbing(SettingsForm, YAMLForm) +class RolesSettingsForm(Form, ScopeVocabMixin, CreateContainerTrigger): action_resource = u'edit' form_template = 'cone.ldap.browser:forms/roles_settings.yaml' @@ -314,6 +258,12 @@ class RolesSettingsForm(Form, ScopeVocabMixin): def message_factory(self): return _ + @property + def ldap_roles(self): + if self.model.container_exists: + return 'OK' + return _('inexistent', default='Inexistent') + @property def roles_aliases_attrmap(self): attrs = self.model.attrs diff --git a/src/cone/ldap/browser/static/styles.css b/src/cone/ldap/browser/static/cone.ldap.css similarity index 100% rename from src/cone/ldap/browser/static/styles.css rename to src/cone/ldap/browser/static/cone.ldap.css diff --git a/src/cone/ldap/browser/templates/groups_settings.pt b/src/cone/ldap/browser/templates/groups_settings.pt index 041a2ea..a4d2442 100644 --- a/src/cone/ldap/browser/templates/groups_settings.pt +++ b/src/cone/ldap/browser/templates/groups_settings.pt @@ -3,9 +3,9 @@ xmlns:i18n="http://xml.zope.org/namespaces/i18n" i18n:domain="cone.ldap" omit-tag="True"> -
+
-

Groups Settings

+

Title

Check Groups DN: @@ -43,7 +43,7 @@

- +
- \ No newline at end of file + diff --git a/src/cone/ldap/browser/templates/roles_settings.pt b/src/cone/ldap/browser/templates/roles_settings.pt index 9d0ea15..2b789f4 100644 --- a/src/cone/ldap/browser/templates/roles_settings.pt +++ b/src/cone/ldap/browser/templates/roles_settings.pt @@ -3,9 +3,9 @@ xmlns:i18n="http://xml.zope.org/namespaces/i18n" i18n:domain="cone.ldap" omit-tag="True"> -
+
-

Roles Settings

+

Title

Check Roles DN: @@ -43,7 +43,7 @@

- +
- \ No newline at end of file + diff --git a/src/cone/ldap/browser/templates/server_settings.pt b/src/cone/ldap/browser/templates/server_settings.pt index c057ba3..fdeb867 100644 --- a/src/cone/ldap/browser/templates/server_settings.pt +++ b/src/cone/ldap/browser/templates/server_settings.pt @@ -2,9 +2,9 @@ xmlns:i18n="http://xml.zope.org/namespaces/i18n" i18n:domain="cone.ldap" omit-tag="True"> -
+
-

LDAP Server Settings

+

Title

Server Connection: @@ -15,7 +15,7 @@

- +
- \ No newline at end of file + diff --git a/src/cone/ldap/browser/templates/users_settings.pt b/src/cone/ldap/browser/templates/users_settings.pt index 917ff4f..d063573 100644 --- a/src/cone/ldap/browser/templates/users_settings.pt +++ b/src/cone/ldap/browser/templates/users_settings.pt @@ -3,9 +3,9 @@ xmlns:i18n="http://xml.zope.org/namespaces/i18n" i18n:domain="cone.ldap" omit-tag="True"> -
+
-

Users Settings

+

Title

Check Users DN: @@ -43,7 +43,7 @@

- +
- \ No newline at end of file + diff --git a/src/cone/ldap/settings.py b/src/cone/ldap/settings.py index 804fad4..1018551 100644 --- a/src/cone/ldap/settings.py +++ b/src/cone/ldap/settings.py @@ -1,5 +1,5 @@ -from cone.app.model import Metadata from cone.app.model import Properties +from cone.app.model import node_info from cone.app.utils import format_traceback from cone.ugm.settings import UGMSettings from cone.ugm.utils import general_settings @@ -37,25 +37,21 @@ factory_defaults.role = dict() +@node_info( + name='ldap_server_settings', + title=_('server_settings_node', default='LDAP Server Settings'), + description=_( + 'server_settings_node_description', + default='General LDAP Server Settings' + ), + icon='glyphicon glyphicon-hdd') class LDAPServerSettings(UGMSettings): + category = _('ldap', default='LDAP') @property def config_file(self): return ldap_cfg.server_config - @instance_property - def metadata(self): - metadata = Metadata() - metadata.title = _( - 'server_settings_node', - default='LDAP Server' - ) - metadata.description = _( - 'server_settings_node_description', - default='General LDAP Server Settings' - ) - return metadata - @property def ldap_connectivity(self): try: @@ -154,7 +150,16 @@ def create_container(self): return message +@node_info( + name='ldap_user_settings', + title=_('user_settings_node', default='LDAP User Settings'), + description=_( + 'user_settings_node_description', + default='User specific LDAP Settings' + ), + icon='ion-person') class LDAPUsersSettings(LDAPContainerSettings): + category = _('ldap', default='LDAP') @property def config_file(self): @@ -164,19 +169,6 @@ def config_file(self): def container_dn(self): return self.attrs.users_dn - @instance_property - def metadata(self): - metadata = Metadata() - metadata.title = _( - 'user_settings_node', - default='LDAP Users' - ) - metadata.description = _( - 'user_settings_node_description', - default='User specific LDAP Settings' - ) - return metadata - @instance_property def ldap_ucfg(self): ugm_settings = general_settings(self).attrs @@ -227,7 +219,16 @@ def invalidate(self): super(LDAPUsersSettings, self).invalidate(attrs=['ldap_ucfg']) +@node_info( + name='ldap_group_settings', + title=_('group_settings_node', default='LDAP Group Settings'), + description=_( + 'group_settings_node_description', + default='Group specific LDAP Settings' + ), + icon='ion-person-stalker') class LDAPGroupsSettings(LDAPContainerSettings): + category = _('ldap', default='LDAP') @property def config_file(self): @@ -237,18 +238,6 @@ def config_file(self): def container_dn(self): return self.attrs.groups_dn - @instance_property - def metadata(self): - metadata = Metadata() - metadata.title = _( - 'group_settings_node', - default='LDAP Groups') - metadata.description = _( - 'group_settings_node_description', - default='Group specific LDAP Settings' - ) - return metadata - @instance_property def ldap_gcfg(self): ugm_settings = general_settings(self).attrs @@ -272,7 +261,16 @@ def invalidate(self): super(LDAPGroupsSettings, self).invalidate(attrs=['ldap_gcfg']) +@node_info( + name='ldap_role_settings', + title=_('role_settings_node', default='LDAP Role Settings'), + description=_( + 'role_settings_node_description', + default='Role specific LDAP Settings' + ), + icon='glyphicon glyphicon-tasks') class LDAPRolesSettings(LDAPContainerSettings): + category = _('ldap', default='LDAP') @property def config_file(self): @@ -282,18 +280,6 @@ def config_file(self): def container_dn(self): return self.attrs.roles_dn - @instance_property - def metadata(self): - metadata = Metadata() - metadata.title = _( - 'role_settings_node', - default='LDAP Roles') - metadata.description = _( - 'role_settings_node_description', - default='Role specific LDAP Settings' - ) - return metadata - @instance_property def ldap_rcfg(self): settings = self.attrs diff --git a/src/cone/ldap/tests/__init__.py b/src/cone/ldap/tests/__init__.py deleted file mode 100644 index fe200ff..0000000 --- a/src/cone/ldap/tests/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -import sys -import unittest - - -def test_suite(): - from cone.ldap.tests import test_ldap - from cone.ldap.tests import test_settings - from cone.ldap.tests import test_ugm - - from cone.ldap.tests import test_browser_settings - - suite = unittest.TestSuite() - - suite.addTest(unittest.findTestCases(test_ldap)) - suite.addTest(unittest.findTestCases(test_settings)) - suite.addTest(unittest.findTestCases(test_ugm)) - - suite.addTest(unittest.findTestCases(test_browser_settings)) - - return suite - - -def run_tests(): - from zope.testrunner.runner import Runner - - runner = Runner(found_suites=[test_suite()]) - runner.run() - sys.exit(int(runner.failed)) - - -if __name__ == '__main__': - run_tests() diff --git a/src/cone/ldap/tests/test_browser_settings.py b/src/cone/ldap/tests/test_browser_settings.py index e9ed2c6..48c3a0c 100644 --- a/src/cone/ldap/tests/test_browser_settings.py +++ b/src/cone/ldap/tests/test_browser_settings.py @@ -1,7 +1,6 @@ from cone.app import get_root -from cone.app.browser.ajax import AjaxAction +from cone.app.browser.ajax import AjaxEvent from cone.app.browser.ajax import AjaxMessage -from cone.app.browser.utils import make_url from cone.ldap import testing from cone.ldap.browser.settings import CreateContainerAction from cone.ldap.browser.settings import CreateContainerTrigger @@ -44,23 +43,6 @@ def test_CreateContainerAction(self): cca = CreateContainerAction() cca.model = settings cca.request = self.layer.new_request() - err = self.expectError( - NotImplementedError, - lambda: cca.continuation - ) - expected = ( - 'Abstract ``CreateContainerAction`` ' - 'does not implement ``continuation``' - ) - self.assertEqual(str(err), expected) - - class MyCreateContainerAction(CreateContainerAction): - @property - def continuation(self): - url = make_url(self.request, node=self.model) - return AjaxAction(url, 'content', 'inner', '.ldap_cca') - - cca = MyCreateContainerAction() request = self.layer.new_request() settings.attrs.users_dn = 'ou=cca,dc=other-domain,dc=com' @@ -88,10 +70,12 @@ def continuation(self): message = request.environ['cone.app.continuation'][0] self.assertTrue(isinstance(message, AjaxMessage)) self.assertEqual(message.flavor, 'info') - self.assertEqual(message.payload, 'created_principal_container') - action = request.environ['cone.app.continuation'][1] - self.assertTrue(isinstance(action, AjaxAction)) - self.assertEqual(action.selector, '.ldap_cca') + self.assertEqual(message.payload, 'Created ou=cca') + event = request.environ['cone.app.continuation'][1] + self.assertTrue(isinstance(event, AjaxEvent)) + self.assertEqual(event.target, 'http://example.com/settings/ldap_users') + self.assertEqual(event.name, 'contextchanged') + self.assertEqual(event.selector, '#layout') @principals( users={ diff --git a/src/cone/ldap/tests/test_ugm.py b/src/cone/ldap/tests/test_ugm.py index dac5acf..f998fa8 100644 --- a/src/cone/ldap/tests/test_ugm.py +++ b/src/cone/ldap/tests/test_ugm.py @@ -29,160 +29,160 @@ class TestLayout( unittest.TestCase, - test_layout.TestLayoutBase + test_layout.LayoutTests ): pass class TestModelLocalmanager( NodeTestCase, - test_localmanager.TestModelLocalmanagerBase + test_localmanager.ModelLocalmanagerTests ): layer = testing.ldap_layer class TestSettings( NodeTestCase, - test_settings.TestSettingsBase + test_settings.SettingsTests ): layer = testing.ldap_layer class TestUtils( unittest.TestCase, - test_utils.TestUtilsBase + test_utils.UtilsTests ): layer = testing.ldap_layer class TestModelGroup( unittest.TestCase, - test_model_group.TestModelGroupBase + test_model_group.ModelGroupTests ): layer = testing.ldap_layer class TestModelGroups( NodeTestCase, - test_model_groups.TestModelGroupsBase + test_model_groups.ModelGroupsTests ): layer = testing.ldap_layer class TestModelUser( unittest.TestCase, - test_model_user.TestModelUserBase + test_model_user.ModelUserTests ): layer = testing.ldap_layer class TestModelUsers( NodeTestCase, - test_model_users.TestModelUsersBase + test_model_users.ModelUsersTests ): layer = testing.ldap_layer class TestBrowserActions( TileTestCase, - test_browser_actions.TestBrowserActionsBase + test_browser_actions.BrowserActionsTests ): layer = testing.ldap_layer class TestBrowserAutoincrement( TileTestCase, - test_browser_autoincrement.TestBrowserAutoincrementBase + test_browser_autoincrement.BrowserAutoincrementTests ): layer = testing.ldap_layer class TestBrowserExpires( TileTestCase, - test_browser_expires.TestBrowserExpiresBase + test_browser_expires.BrowserExpiresTests ): layer = testing.ldap_layer class TestBrowserGroup( TileTestCase, - test_browser_group.TestBrowserGroupBase + test_browser_group.BrowserGroupTests ): layer = testing.ldap_layer class TestBrowserGroups( TileTestCase, - test_browser_groups.TestBrowserGroupsBase + test_browser_groups.BrowserGroupsTests ): layer = testing.ldap_layer # class TestBrowserPassword( # TileTestCase, -# test_browser_password.TestBrowserPasswordBase +# test_browser_password.BrowserPasswordTests # ): # layer = testing.ldap_layer class TestBrowserPortrait( TileTestCase, - test_browser_portrait.TestBrowserPortraitBase + test_browser_portrait.BrowserPortraitTests ): layer = testing.ldap_layer class TestBrowserPrincipal( TileTestCase, - test_browser_principal.TestBrowserPrincipalBase + test_browser_principal.BrowserPrincipalTests ): layer = testing.ldap_layer class TestBrowserRemote( TileTestCase, - test_browser_remote.TestBrowserRemoteBase + test_browser_remote.BrowserRemoteTests ): layer = testing.ldap_layer class TestBrowserRoles( TileTestCase, - test_browser_roles.TestBrowserRolesBase + test_browser_roles.BrowserRolesTests ): layer = testing.ldap_layer class TestBrowserRoot( TileTestCase, - test_browser_root.TestBrowserRootBase + test_browser_root.BrowserRootTests ): layer = testing.ldap_layer class TestBrowserSettings( TileTestCase, - test_browser_settings.TestBrowserSettingsBase + test_browser_settings.BrowserSettingsTests ): layer = testing.ldap_layer class TestBrowserUser( TileTestCase, - test_browser_user.TestBrowserUserBase + test_browser_user.BrowserUserTests ): layer = testing.ldap_layer class TestBrowserUsers( TileTestCase, - test_browser_users.TestBrowserUsersBase + test_browser_users.BrowserUsersTests ): layer = testing.ldap_layer class TestBrowserUtils( TileTestCase, - test_browser_utils.TestBrowserUtilsBase + test_browser_utils.BrowserUtilsTests ): layer = testing.ldap_layer