Compare commits
	
		
			No commits in common. "release" and "v1.2.0" have entirely different histories.
		
	
	
		
	
		
|  | @ -1,27 +0,0 @@ | ||||||
| name: "CI" |  | ||||||
| 
 |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: |  | ||||||
|       - "release" |  | ||||||
| 
 |  | ||||||
| jobs: |  | ||||||
|   run-checks: |  | ||||||
|     name: "Checks" |  | ||||||
|     runs-on: "ubuntu-latest" |  | ||||||
|     steps: |  | ||||||
|       - uses: "actions/checkout@v4" |  | ||||||
|       - uses: "actions/setup-python@v5" |  | ||||||
|         with: |  | ||||||
|           python-version: "3.10" |  | ||||||
|       - uses: "Gr1N/setup-poetry@v8" |  | ||||||
|         with: |  | ||||||
|           poetry-version: "1.7.1" |  | ||||||
|       - name: "Install deps" |  | ||||||
|         run: | |  | ||||||
|           set -x |  | ||||||
|           poetry install |  | ||||||
|       - name: "Run CI task" |  | ||||||
|         run: | |  | ||||||
|           set -x |  | ||||||
|           poetry run inv ci |  | ||||||
							
								
								
									
										13
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								CHANGELOG.md
									
									
									
									
									
								
							|  | @ -1,18 +1,5 @@ | ||||||
| ## Keep It Secret Changelog | ## Keep It Secret Changelog | ||||||
| 
 | 
 | ||||||
| #### v1.3.0 (2024-10-24) |  | ||||||
| 
 |  | ||||||
| * Support for approle auth in Hashicorp Vault integration. |  | ||||||
| 
 |  | ||||||
| #### v1.2.2 (2024-06-05) |  | ||||||
| 
 |  | ||||||
| * TeamCity integration for private builds. |  | ||||||
| 
 |  | ||||||
| #### v1.2.1 (2024-05-29) |  | ||||||
| 
 |  | ||||||
| * Gitea Actions integration. |  | ||||||
| * `README.md` language fixes. |  | ||||||
| 
 |  | ||||||
| #### v1.2.0 (2024-02-08) | #### v1.2.0 (2024-02-08) | ||||||
| 
 | 
 | ||||||
| * Hashicorp Vault integration. | * Hashicorp Vault integration. | ||||||
|  |  | ||||||
							
								
								
									
										88
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								Dockerfile
									
									
									
									
									
								
							|  | @ -1,88 +0,0 @@ | ||||||
| ARG APP_USER_UID=10001 |  | ||||||
| ARG APP_USER_GID=10001 |  | ||||||
| ARG IMAGE_TAG=development.000000 |  | ||||||
| 
 |  | ||||||
| FROM python:3.10.14-slim-bookworm AS base |  | ||||||
| 
 |  | ||||||
| ARG APP_USER_UID |  | ||||||
| ARG APP_USER_GID |  | ||||||
| ARG IMAGE_TAG |  | ||||||
| 
 |  | ||||||
| ENV PYTHONUNBUFFERED=1 \ |  | ||||||
|     PYTHONDONTWRITEBYTECODE=1 \ |  | ||||||
|     PIP_NO_CACHE_DIR=off \ |  | ||||||
|     PIP_DISABLE_PIP_VERSION_CHECK=on \ |  | ||||||
|     PIP_DEFAULT_TIMEOUT=100 \ |  | ||||||
|     PIP_INDEX_URL="https://nexus.bthlabs.pl/repository/pypi/simple/" \ |  | ||||||
|     POETRY_VERSION=1.7.1 \ |  | ||||||
|     POETRY_HOME="/srv/poetry" \ |  | ||||||
|     POETRY_NO_INTERACTION=1 \ |  | ||||||
|     VIRTUAL_ENV="/srv/venv" \ |  | ||||||
|     KEEP_IT_SECRET_IMAGE_TAG=${IMAGE_TAG} |  | ||||||
| 
 |  | ||||||
| RUN if [ ! $(getent group ${APP_USER_GID}) ];then groupadd -g ${APP_USER_GID} app; fi && \ |  | ||||||
|     useradd -m -d /home/app -u ${APP_USER_UID} -g ${APP_USER_GID} app && \ |  | ||||||
|     apt-get update && \ |  | ||||||
|     apt-get install -y --no-install-recommends wait-for-it dumb-init curl && \ |  | ||||||
|     (curl -sSL https://install.python-poetry.org | python -) && \ |  | ||||||
|     python3.10 -m venv ${VIRTUAL_ENV} && \ |  | ||||||
|     mkdir /srv/app /srv/bin /srv/lib /srv/log /srv/run && \ |  | ||||||
|     chown -R ${APP_USER_UID}:${APP_USER_GID} /srv |  | ||||||
| 
 |  | ||||||
| ENV PATH="${VIRTUAL_ENV}/bin:/srv/bin:/srv/poetry/bin:${PATH}" |  | ||||||
| 
 |  | ||||||
| USER app |  | ||||||
| WORKDIR /srv/app |  | ||||||
| 
 |  | ||||||
| FROM base AS development |  | ||||||
| 
 |  | ||||||
| ARG APP_USER_UID |  | ||||||
| ARG APP_USER_GID |  | ||||||
| ARG IMAGE_TAG |  | ||||||
| 
 |  | ||||||
| USER app |  | ||||||
| WORKDIR /srv/app |  | ||||||
| 
 |  | ||||||
| FROM development AS deployment-build |  | ||||||
| 
 |  | ||||||
| ARG APP_USER_UID |  | ||||||
| ARG APP_USER_GID |  | ||||||
| ARG IMAGE_TAG |  | ||||||
| 
 |  | ||||||
| ADD --chown=$APP_USER_UID:$APP_USER_GID . /srv/app |  | ||||||
| RUN poetry install --no-dev |  | ||||||
| 
 |  | ||||||
| FROM deployment-build AS ci |  | ||||||
| 
 |  | ||||||
| ARG APP_USER_UID |  | ||||||
| ARG APP_USER_GID |  | ||||||
| ARG IMAGE_TAG |  | ||||||
| 
 |  | ||||||
| RUN poetry install |  | ||||||
| 
 |  | ||||||
| FROM base AS deployment |  | ||||||
| 
 |  | ||||||
| ARG APP_USER_UID |  | ||||||
| ARG APP_USER_GID |  | ||||||
| ARG IMAGE_TAG |  | ||||||
| 
 |  | ||||||
| COPY --from=deployment-build /srv/app /srv/app |  | ||||||
| COPY --from=deployment-build /srv/venv /srv/venv |  | ||||||
| RUN chown -R $APP_USER_UID:$APP_USER_GID /srv |  | ||||||
| 
 |  | ||||||
| USER root |  | ||||||
| 
 |  | ||||||
| RUN apt-get clean autoclean && \ |  | ||||||
|     apt-get autoremove --yes && \ |  | ||||||
|     rm -rf /var/lib/apt /var/lib/dpkg && \ |  | ||||||
|     rm -rf /home/app/.cache |  | ||||||
| 
 |  | ||||||
| USER app |  | ||||||
| 
 |  | ||||||
| ENV PYTHONPATH="/srv/app" |  | ||||||
| ENV DJANGO_SETTINGS_MODULE="settings" |  | ||||||
| 
 |  | ||||||
| EXPOSE 8000 |  | ||||||
| 
 |  | ||||||
| ENTRYPOINT ["/usr/bin/dumb-init"] |  | ||||||
| CMD ["echo NOOP"] |  | ||||||
|  | @ -65,7 +65,7 @@ to provide secrets suitable for the development environment: | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| The `ProductionSecrets` class uses environment variables and AWS Secrets | The `ProductionSecrets` class uses environment variables and AWS Secrets | ||||||
| Manager to provide secrets suitable for the production environment: | Manager to provide secrets suitable for the development environment: | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| >>> production_secrets = ProductionSecrets() | >>> production_secrets = ProductionSecrets() | ||||||
|  |  | ||||||
|  | @ -11,10 +11,10 @@ | ||||||
| project = 'Keep It Secret' | project = 'Keep It Secret' | ||||||
| copyright = '2023-present Tomek Wójcik' | copyright = '2023-present Tomek Wójcik' | ||||||
| author = 'Tomek Wójcik' | author = 'Tomek Wójcik' | ||||||
| version = '1.3.0' | version = '1.2.0' | ||||||
| 
 | 
 | ||||||
| # The full version, including alpha/beta/rc tags | # The full version, including alpha/beta/rc tags | ||||||
| release = '1.3.0' | release = '1.2.0' | ||||||
| 
 | 
 | ||||||
| # -- General configuration --------------------------------------------------- | # -- General configuration --------------------------------------------------- | ||||||
| # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration | ||||||
|  |  | ||||||
|  | @ -39,15 +39,9 @@ to be installed: | ||||||
| 
 | 
 | ||||||
| **API** | **API** | ||||||
| 
 | 
 | ||||||
| .. autoclass:: keep_it_secret.ext.vault.BaseVaultSecrets |  | ||||||
|     :members: |  | ||||||
| 
 |  | ||||||
| .. autoclass:: keep_it_secret.ext.vault.VaultSecrets | .. autoclass:: keep_it_secret.ext.vault.VaultSecrets | ||||||
|     :members: |     :members: | ||||||
| 
 | 
 | ||||||
| .. autoclass:: keep_it_secret.ext.vault.AppRoleVaultSecrets |  | ||||||
|     :members: |  | ||||||
| 
 |  | ||||||
| .. autoclass:: keep_it_secret.ext.vault.VaultKV2Field | .. autoclass:: keep_it_secret.ext.vault.VaultKV2Field | ||||||
|     :members: |     :members: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ from .fields import (  # noqa: F401 | ||||||
| ) | ) | ||||||
| from .secrets import Secrets  # noqa: F401 | from .secrets import Secrets  # noqa: F401 | ||||||
| 
 | 
 | ||||||
| __version__ = '1.3.0' | __version__ = '1.2.0' | ||||||
| 
 | 
 | ||||||
| __all__ = [ | __all__ = [ | ||||||
|     'AbstractField', |     'AbstractField', | ||||||
|  |  | ||||||
|  | @ -9,9 +9,10 @@ from keep_it_secret.fields import EnvField, Field | ||||||
| from keep_it_secret.secrets import Secrets | from keep_it_secret.secrets import Secrets | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class BaseVaultSecrets(Secrets): | class VaultSecrets(Secrets): | ||||||
|     """ |     """ | ||||||
|     Base :py:class:`keep_it_secret.Secrets` subclass for Vault-base secrets. |     Concrete :py:class:`keep_it_secret.Secrets` subclass that maps environment | ||||||
|  |     variables to Vault credentials. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     url: str = EnvField.new('VAULT_URL', required=True) |     url: str = EnvField.new('VAULT_URL', required=True) | ||||||
|  | @ -21,6 +22,13 @@ class BaseVaultSecrets(Secrets): | ||||||
|     :type: ``str`` |     :type: ``str`` | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  |     token: str = EnvField.new('VAULT_TOKEN', required=True) | ||||||
|  |     """ | ||||||
|  |     Maps ``VAULT_TOKEN`` environment variable. | ||||||
|  | 
 | ||||||
|  |     :type: ``str`` | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|     client_cert_path: str | None = EnvField.new('VAULT_CLIENT_CERT_PATH', required=False) |     client_cert_path: str | None = EnvField.new('VAULT_CLIENT_CERT_PATH', required=False) | ||||||
|     """ |     """ | ||||||
|     Maps ``VAULT_CLIENT_CERT_PATH`` environment variable. |     Maps ``VAULT_CLIENT_CERT_PATH`` environment variable. | ||||||
|  | @ -54,6 +62,7 @@ class BaseVaultSecrets(Secrets): | ||||||
|         """ |         """ | ||||||
|         result: dict[str, typing.Any] = { |         result: dict[str, typing.Any] = { | ||||||
|             'url': self.url, |             'url': self.url, | ||||||
|  |             'token': self.token, | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if self.client_cert_path is not None and self.client_key_path is not None: |         if self.client_cert_path is not None and self.client_key_path is not None: | ||||||
|  | @ -76,58 +85,6 @@ class BaseVaultSecrets(Secrets): | ||||||
|         return self.client |         return self.client | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class VaultSecrets(BaseVaultSecrets): |  | ||||||
|     """ |  | ||||||
|     Concrete :py:class:`BaseVaultSecrets` subclass that uses token to |  | ||||||
|     authenticate with Vault. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     token: str = EnvField.new('VAULT_TOKEN', required=True) |  | ||||||
|     """ |  | ||||||
|     Maps ``VAULT_TOKEN`` environment variable. |  | ||||||
| 
 |  | ||||||
|     :type: ``str`` |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def as_hvac_client_kwargs(self) -> dict[str, typing.Any]: |  | ||||||
|         result = super().as_hvac_client_kwargs() |  | ||||||
|         result['token'] = self.token |  | ||||||
| 
 |  | ||||||
|         return result |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class AppRoleVaultSecrets(BaseVaultSecrets): |  | ||||||
|     """ |  | ||||||
|     Concrete :py:class:`BaseVaultSecrets` subclass that uses app role to |  | ||||||
|     authenticate with Vault. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     role_id: str = EnvField.new('VAULT_ROLE_ID', required=True) |  | ||||||
|     """ |  | ||||||
|     Maps ``VAULT_ROLE_ID`` environment variable. |  | ||||||
| 
 |  | ||||||
|     :type: ``str`` |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     secret_id: str = EnvField.new('VAULT_SECRET_ID', required=True) |  | ||||||
|     """ |  | ||||||
|     Maps ``VAULT_SECRET_ID`` environment variable. |  | ||||||
| 
 |  | ||||||
|     :type: ``str`` |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def get_client(self) -> hvac.Client: |  | ||||||
|         if self.client is None: |  | ||||||
|             super().get_client() |  | ||||||
| 
 |  | ||||||
|             self.client.auth.approle.login(  # type: ignore[attr-defined] |  | ||||||
|                 role_id=self.role_id, |  | ||||||
|                 secret_id=self.secret_id, |  | ||||||
|             ) |  | ||||||
| 
 |  | ||||||
|         return self.client |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class VaultKV2Field(Field): | class VaultKV2Field(Field): | ||||||
|     """ |     """ | ||||||
|     Concrete :py:class:`keep_it_secret.Field` subclass that uses Hashicorp |     Concrete :py:class:`keep_it_secret.Field` subclass that uses Hashicorp | ||||||
|  |  | ||||||
							
								
								
									
										51
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										51
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							|  | @ -1,4 +1,4 @@ | ||||||
| # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. | # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "alabaster" | name = "alabaster" | ||||||
|  | @ -384,33 +384,33 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "flake8" | name = "flake8" | ||||||
| version = "7.3.0" | version = "6.1.0" | ||||||
| description = "the modular source code checker: pep8 pyflakes and co" | description = "the modular source code checker: pep8 pyflakes and co" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.9" | python-versions = ">=3.8.1" | ||||||
| files = [ | files = [ | ||||||
|     {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, |     {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, | ||||||
|     {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, |     {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| mccabe = ">=0.7.0,<0.8.0" | mccabe = ">=0.7.0,<0.8.0" | ||||||
| pycodestyle = ">=2.14.0,<2.15.0" | pycodestyle = ">=2.11.0,<2.12.0" | ||||||
| pyflakes = ">=3.4.0,<3.5.0" | pyflakes = ">=3.1.0,<3.2.0" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "flake8-commas" | name = "flake8-commas" | ||||||
| version = "4.0.0" | version = "2.1.0" | ||||||
| description = "Flake8 lint for trailing commas." | description = "Flake8 lint for trailing commas." | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.8" | python-versions = "*" | ||||||
| files = [ | files = [ | ||||||
|     {file = "flake8_commas-4.0.0-py3-none-any.whl", hash = "sha256:cad476d71ba72e8b941a8508d5b9ffb6b03e50f7102982474f085ad0d674b685"}, |     {file = "flake8-commas-2.1.0.tar.gz", hash = "sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263"}, | ||||||
|     {file = "flake8_commas-4.0.0.tar.gz", hash = "sha256:a68834b42a9a31c94ca790efe557a932c0eae21a3479c6b9a23c4dc077e3ea96"}, |     {file = "flake8_commas-2.1.0-py2.py3-none-any.whl", hash = "sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54"}, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| flake8 = ">=5" | flake8 = ">=2" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "furo" | name = "furo" | ||||||
|  | @ -500,13 +500,13 @@ files = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "invoke" | name = "invoke" | ||||||
| version = "2.2.1" | version = "1.7.3" | ||||||
| description = "Pythonic task execution" | description = "Pythonic task execution" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.6" | python-versions = "*" | ||||||
| files = [ | files = [ | ||||||
|     {file = "invoke-2.2.1-py3-none-any.whl", hash = "sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8"}, |     {file = "invoke-1.7.3-py3-none-any.whl", hash = "sha256:d9694a865764dd3fd91f25f7e9a97fb41666e822bbb00e670091e3f43933574d"}, | ||||||
|     {file = "invoke-2.2.1.tar.gz", hash = "sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707"}, |     {file = "invoke-1.7.3.tar.gz", hash = "sha256:41b428342d466a82135d5ab37119685a989713742be46e42a3a399d685579314"}, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1046,13 +1046,13 @@ tests = ["pytest"] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pycodestyle" | name = "pycodestyle" | ||||||
| version = "2.14.0" | version = "2.11.1" | ||||||
| description = "Python style guide checker" | description = "Python style guide checker" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.9" | python-versions = ">=3.8" | ||||||
| files = [ | files = [ | ||||||
|     {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, |     {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, | ||||||
|     {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, |     {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1068,13 +1068,13 @@ files = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pyflakes" | name = "pyflakes" | ||||||
| version = "3.4.0" | version = "3.1.0" | ||||||
| description = "passive checker of Python programs" | description = "passive checker of Python programs" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.9" | python-versions = ">=3.8" | ||||||
| files = [ | files = [ | ||||||
|     {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, |     {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, | ||||||
|     {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, |     {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1199,7 +1199,6 @@ files = [ | ||||||
|     {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, |     {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, | ||||||
|     {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, |     {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, | ||||||
|     {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, |     {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, | ||||||
|     {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, |  | ||||||
|     {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, |     {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, | ||||||
|     {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, |     {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, | ||||||
|     {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, |     {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, | ||||||
|  | @ -1715,4 +1714,4 @@ vault = ["hvac"] | ||||||
| [metadata] | [metadata] | ||||||
| lock-version = "2.0" | lock-version = "2.0" | ||||||
| python-versions = "^3.10" | python-versions = "^3.10" | ||||||
| content-hash = "1547e872ed842c8e1ea41e45841bf25a327cbaa907e075613c9b12077d2ffefc" | content-hash = "e4959d95b206c17013e548f015fbf288d9bc754584bfc128458cadead137a863" | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| [tool.poetry] | [tool.poetry] | ||||||
| name = "keep-it-secret" | name = "keep-it-secret" | ||||||
| version = "1.3.0" | version = "1.2.0" | ||||||
| description = "Keep It Secret by BTHLabs" | description = "Keep It Secret by BTHLabs" | ||||||
| authors = ["Tomek Wójcik <contact@bthlabs.pl>"] | authors = ["Tomek Wójcik <contact@bthlabs.pl>"] | ||||||
| maintainers = ["BTHLabs <contact@bthlabs.pl>"] | maintainers = ["BTHLabs <contact@bthlabs.pl>"] | ||||||
|  | @ -17,11 +17,11 @@ hvac = {version = ">=2.1.0", optional = true} | ||||||
| 
 | 
 | ||||||
| [tool.poetry.group.dev.dependencies] | [tool.poetry.group.dev.dependencies] | ||||||
| boto3 = "1.34.8" | boto3 = "1.34.8" | ||||||
| flake8 = "7.3.0" | flake8 = "6.1.0" | ||||||
| flake8-commas = "4.0.0" | flake8-commas = "2.1.0" | ||||||
| furo = "2023.9.10" | furo = "2023.9.10" | ||||||
| hvac = "2.1.0" | hvac = "2.1.0" | ||||||
| invoke = "2.2.1" | invoke = "1.7.3" | ||||||
| ipdb = "0.13.13" | ipdb = "0.13.13" | ||||||
| ipython = "8.19.0" | ipython = "8.19.0" | ||||||
| moto = "4.2.12" | moto = "4.2.12" | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ hang-closing = False | ||||||
| 
 | 
 | ||||||
| [tool:pytest] | [tool:pytest] | ||||||
| addopts = --disable-warnings | addopts = --disable-warnings | ||||||
| junit_suite_name = keep_it_secret |  | ||||||
| env = | env = | ||||||
|     KEEP_IT_SECRET_TESTS_SPAM=spam |     KEEP_IT_SECRET_TESTS_SPAM=spam | ||||||
|     AWS_ACCESS_KEY_ID=thisisntright |     AWS_ACCESS_KEY_ID=thisisntright | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								tasks.py
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								tasks.py
									
									
									
									
									
								
							|  | @ -1,7 +1,5 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| # type: ignore | # type: ignore | ||||||
| import os |  | ||||||
| 
 |  | ||||||
| from invoke import task | from invoke import task | ||||||
| 
 | 
 | ||||||
| try: | try: | ||||||
|  | @ -22,25 +20,13 @@ def mypy(ctx, warn=False): | ||||||
| 
 | 
 | ||||||
| @task | @task | ||||||
| def tests(ctx, warn=False): | def tests(ctx, warn=False): | ||||||
|     pytest_command_line = [ |     return ctx.run('pytest -v', warn=warn) | ||||||
|         'pytest', |  | ||||||
|         '-v', |  | ||||||
|     ] |  | ||||||
| 
 |  | ||||||
|     if 'KEEP_IT_SECRET_JUNIT_XML_PATH' in os.environ: |  | ||||||
|         pytest_command_line.append( |  | ||||||
|             f"--junit-xml={os.environ['KEEP_IT_SECRET_JUNIT_XML_PATH']}", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|     return ctx.run(' '.join(pytest_command_line), warn=warn) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @task | @task | ||||||
| def ci(ctx): | def ci(ctx): | ||||||
|     result = True |     result = True | ||||||
| 
 | 
 | ||||||
|     ctx.run('mkdir -p build') |  | ||||||
| 
 |  | ||||||
|     if flake8(ctx, warn=True).exited != 0: |     if flake8(ctx, warn=True).exited != 0: | ||||||
|         result = False |         result = False | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,30 +2,11 @@ | ||||||
| # type: ignore | # type: ignore | ||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
| 
 | 
 | ||||||
| from unittest import mock |  | ||||||
| 
 |  | ||||||
| import hvac |  | ||||||
| import pytest | import pytest | ||||||
| from pytest_mock import MockerFixture |  | ||||||
| 
 | 
 | ||||||
| from .fixtures import TestingVaultSecrets | from .fixtures import TestingVaultSecrets | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.fixture |  | ||||||
| def hvac_client() -> mock.Mock: |  | ||||||
|     return mock.Mock(spec=hvac.Client) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @pytest.fixture |  | ||||||
| def mock_hvac_client(mocker: MockerFixture, |  | ||||||
|                      hvac_client: mock.Mock, |  | ||||||
|                      ) -> mock.Mock: |  | ||||||
|     return mocker.patch( |  | ||||||
|         'keep_it_secret.ext.vault.hvac.Client', |  | ||||||
|         return_value=hvac_client, |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @pytest.fixture | @pytest.fixture | ||||||
| def testing_vault_secrets() -> TestingVaultSecrets: | def testing_vault_secrets() -> TestingVaultSecrets: | ||||||
|     return TestingVaultSecrets() |     return TestingVaultSecrets() | ||||||
|  |  | ||||||
|  | @ -1,124 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| # type: ignore |  | ||||||
| from __future__ import annotations |  | ||||||
| 
 |  | ||||||
| import os |  | ||||||
| from unittest import mock |  | ||||||
| 
 |  | ||||||
| from keep_it_secret.ext import vault |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @mock.patch.dict( |  | ||||||
|     os.environ, |  | ||||||
|     { |  | ||||||
|         'VAULT_URL': 'https://vault.work/', |  | ||||||
|         'VAULT_ROLE_ID': 'test_role_id', |  | ||||||
|         'VAULT_SECRET_ID': 'test_secret_id', |  | ||||||
|     }, |  | ||||||
| ) |  | ||||||
| def test_init(): |  | ||||||
|     # When |  | ||||||
|     result = vault.AppRoleVaultSecrets() |  | ||||||
| 
 |  | ||||||
|     # Then |  | ||||||
|     assert result.client is None |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @mock.patch.dict( |  | ||||||
|     os.environ, |  | ||||||
|     { |  | ||||||
|         'VAULT_URL': 'https://vault.work/', |  | ||||||
|         'VAULT_ROLE_ID': 'test_role_id', |  | ||||||
|         'VAULT_SECRET_ID': 'test_secret_id', |  | ||||||
|         'VAULT_CLIENT_CERT_PATH': '/tmp/vault_client_cert.pem', |  | ||||||
|         'VAULT_CLIENT_KEY_PATH': '/tmp/vault_client_key.pem', |  | ||||||
|         'VAULT_SERVER_CERT_PATH': '/tmp/vault_server_cert.pem', |  | ||||||
|     }, |  | ||||||
| ) |  | ||||||
| def test_as_hvac_client_kwargs(): |  | ||||||
|     # Given |  | ||||||
|     secrets = vault.AppRoleVaultSecrets() |  | ||||||
| 
 |  | ||||||
|     # When |  | ||||||
|     result = secrets.as_hvac_client_kwargs() |  | ||||||
| 
 |  | ||||||
|     # Then |  | ||||||
|     assert result == { |  | ||||||
|         'url': 'https://vault.work/', |  | ||||||
|         'cert': ('/tmp/vault_client_cert.pem', '/tmp/vault_client_key.pem'), |  | ||||||
|         'verify': '/tmp/vault_server_cert.pem', |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @mock.patch.dict( |  | ||||||
|     os.environ, |  | ||||||
|     { |  | ||||||
|         'VAULT_URL': 'https://vault.work/', |  | ||||||
|         'VAULT_ROLE_ID': 'test_role_id', |  | ||||||
|         'VAULT_SECRET_ID': 'test_secret_id', |  | ||||||
|     }, |  | ||||||
| ) |  | ||||||
| def test_as_hvac_client_kwargs_without_optional_fields(): |  | ||||||
|     # Given |  | ||||||
|     secrets = vault.AppRoleVaultSecrets() |  | ||||||
| 
 |  | ||||||
|     # When |  | ||||||
|     result = secrets.as_hvac_client_kwargs() |  | ||||||
| 
 |  | ||||||
|     # Then |  | ||||||
|     assert result == { |  | ||||||
|         'url': 'https://vault.work/', |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @mock.patch.dict( |  | ||||||
|     os.environ, |  | ||||||
|     { |  | ||||||
|         'VAULT_URL': 'https://vault.work/', |  | ||||||
|         'VAULT_ROLE_ID': 'test_role_id', |  | ||||||
|         'VAULT_SECRET_ID': 'test_secret_id', |  | ||||||
|     }, |  | ||||||
| ) |  | ||||||
| def test_get_client_cache_miss(mock_hvac_client: mock.Mock, |  | ||||||
|                                hvac_client: mock.Mock): |  | ||||||
|     # Given |  | ||||||
|     mock_hvac_client.return_value = hvac_client |  | ||||||
| 
 |  | ||||||
|     secrets = vault.AppRoleVaultSecrets() |  | ||||||
| 
 |  | ||||||
|     # When |  | ||||||
|     result = secrets.get_client() |  | ||||||
| 
 |  | ||||||
|     # Then |  | ||||||
|     assert result == hvac_client |  | ||||||
| 
 |  | ||||||
|     assert secrets.client == hvac_client |  | ||||||
| 
 |  | ||||||
|     mock_hvac_client.assert_called_once_with(**secrets.as_hvac_client_kwargs()) |  | ||||||
|     hvac_client.auth.approle.login.assert_called_once_with( |  | ||||||
|         role_id='test_role_id', |  | ||||||
|         secret_id='test_secret_id', |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @mock.patch.dict( |  | ||||||
|     os.environ, |  | ||||||
|     { |  | ||||||
|         'VAULT_URL': 'https://vault.work/', |  | ||||||
|         'VAULT_ROLE_ID': 'test_role_id', |  | ||||||
|         'VAULT_SECRET_ID': 'test_secret_id', |  | ||||||
|     }, |  | ||||||
| ) |  | ||||||
| def test_get_client_cache_hit(mock_hvac_client: mock.Mock, |  | ||||||
|                               hvac_client: mock.Mock): |  | ||||||
|     # Given |  | ||||||
|     secrets = vault.AppRoleVaultSecrets() |  | ||||||
|     secrets.client = hvac_client |  | ||||||
| 
 |  | ||||||
|     # When |  | ||||||
|     result = secrets.get_client() |  | ||||||
| 
 |  | ||||||
|     # Then |  | ||||||
|     assert result == hvac_client |  | ||||||
| 
 |  | ||||||
|     mock_hvac_client.assert_not_called() |  | ||||||
|  | @ -5,16 +5,22 @@ from __future__ import annotations | ||||||
| import os | import os | ||||||
| from unittest import mock | from unittest import mock | ||||||
| 
 | 
 | ||||||
|  | import pytest | ||||||
|  | from pytest_mock import MockerFixture | ||||||
|  | 
 | ||||||
| from keep_it_secret.ext import vault | from keep_it_secret.ext import vault | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @mock.patch.dict( | @pytest.fixture | ||||||
|     os.environ, | def mock_hvac_client(mocker: MockerFixture) -> mock.Mock: | ||||||
|     { |     return mocker.patch.object(vault.hvac, 'Client') | ||||||
|         'VAULT_URL': 'https://vault.work/', | 
 | ||||||
|         'VAULT_TOKEN': 'test_vault_token', | 
 | ||||||
|     }, | @pytest.fixture | ||||||
| ) | def hvac_client() -> mock.Mock: | ||||||
|  |     return mock.Mock() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def test_init(): | def test_init(): | ||||||
|     # When |     # When | ||||||
|     result = vault.VaultSecrets() |     result = vault.VaultSecrets() | ||||||
|  | @ -70,13 +76,6 @@ def test_as_hvac_client_kwargs_without_optional_fields(): | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @mock.patch.dict( |  | ||||||
|     os.environ, |  | ||||||
|     { |  | ||||||
|         'VAULT_URL': 'https://vault.work/', |  | ||||||
|         'VAULT_TOKEN': 'test_vault_token', |  | ||||||
|     }, |  | ||||||
| ) |  | ||||||
| def test_get_client_cache_miss(mock_hvac_client: mock.Mock, | def test_get_client_cache_miss(mock_hvac_client: mock.Mock, | ||||||
|                                hvac_client: mock.Mock): |                                hvac_client: mock.Mock): | ||||||
|     # Given |     # Given | ||||||
|  | @ -95,13 +94,6 @@ def test_get_client_cache_miss(mock_hvac_client: mock.Mock, | ||||||
|     mock_hvac_client.assert_called_once_with(**secrets.as_hvac_client_kwargs()) |     mock_hvac_client.assert_called_once_with(**secrets.as_hvac_client_kwargs()) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @mock.patch.dict( |  | ||||||
|     os.environ, |  | ||||||
|     { |  | ||||||
|         'VAULT_URL': 'https://vault.work/', |  | ||||||
|         'VAULT_TOKEN': 'test_vault_token', |  | ||||||
|     }, |  | ||||||
| ) |  | ||||||
| def test_get_client_cache_hit(mock_hvac_client: mock.Mock, | def test_get_client_cache_hit(mock_hvac_client: mock.Mock, | ||||||
|                               hvac_client: mock.Mock): |                               hvac_client: mock.Mock): | ||||||
|     # Given |     # Given | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user