diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index cc59d93..676646f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,2 +1,3 @@
# Contributing
+
The repository is released under the GNU AFFERO GENERAL PUBLIC LICENSE license, and follows a standard Github development process, using Github tracker for issues and merging pull requests into master.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 9700237..72e44eb 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,7 +1,6 @@
---
name: Bug report
about: Create a report to help us improve
-
---
**Describe the bug**
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 066b2d9..a09db44 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,7 +1,6 @@
---
name: Feature request
about: Suggest an idea for this project
-
---
**Is your feature request related to a problem? Please describe.**
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 6ff7146..80c4aed 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,5 +1,7 @@
### Type of Change
+
+
- [ ] New feature
- [ ] Bug fix
- [ ] Documentation
@@ -7,8 +9,11 @@
- [ ] Chore
### Resolves
+
- Fixes #[Add issue number here.]
### Describe Changes
+
+
_Describe what this Pull Request does_
diff --git a/.github/actions/tests/python/action.yml b/.github/actions/tests/python/action.yml
index 7416c43..4b9231c 100644
--- a/.github/actions/tests/python/action.yml
+++ b/.github/actions/tests/python/action.yml
@@ -12,7 +12,6 @@ inputs:
required: true
type: string
-
env:
REQUIREMENTS_PATH: "requirements/local.txt"
diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml
index 8079ccf..9f5b78b 100644
--- a/.github/workflows/auto-assign.yml
+++ b/.github/workflows/auto-assign.yml
@@ -11,9 +11,9 @@ jobs:
issues: write
pull-requests: write
steps:
- - name: 'Auto-assign issue'
- uses: pozil/auto-assign-issue@v1
- with:
+ - name: "Auto-assign issue"
+ uses: pozil/auto-assign-issue@v1
+ with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
assignees: lpm0073
numOfAssignee: 1
diff --git a/.github/workflows/semanticVersionBump.yml b/.github/workflows/semanticVersionBump.yml
index b1a8c14..9337dbf 100644
--- a/.github/workflows/semanticVersionBump.yml
+++ b/.github/workflows/semanticVersionBump.yml
@@ -110,6 +110,8 @@ jobs:
id: update_version
run: |
echo "# -*- coding: utf-8 -*-" > ${{ env.VERSION_FILE }}
+ echo "# DO NOT EDIT." > ${{ env.VERSION_FILE }}
+ echo "# Managed via automated CI/CD in .github/workflows/semanticVersionBump.yml." > ${{ env.VERSION_FILE }}
echo "__version__ = \"${{ env.NEXT_VERSION }}\"" >> ${{ env.VERSION_FILE }}
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
diff --git a/.gitignore b/.gitignore
index e07699a..2abbdd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,10 @@
+# Build artifacts
+build/
+dist/
+*.egg-info/
+*.egg
+
+
# Jupyter Notebook
.ipynb_checkpoints
data
diff --git a/.mergify.yml b/.mergify.yml
index 4516041..f938da6 100644
--- a/.mergify.yml
+++ b/.mergify.yml
@@ -3,16 +3,16 @@
pull_request_rules:
- name: automatic approve dependabot pull requests
conditions:
- - 'author~=dependabot[bot]|dependabot-preview[bot]|dependabot'
+ - "author~=dependabot[bot]|dependabot-preview[bot]|dependabot"
actions:
review:
type: APPROVE
- name: automatic merge dependabot pull requests
conditions:
- - 'author~=dependabot[bot]|dependabot-preview[bot]|dependabot'
- - '#approved-reviews-by>=1'
- - 'base=main' # replace 'main' with the name of the branch you want to auto-merge into
+ - "author~=dependabot[bot]|dependabot-preview[bot]|dependabot"
+ - "#approved-reviews-by>=1"
+ - "base=main" # replace 'main' with the name of the branch you want to auto-merge into
actions:
merge:
method: merge
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index dfb1f87..cd56513 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,6 +2,10 @@ default_language_version:
# default language version for each language
python: python3.11
repos:
+ - repo: https://github.com/pre-commit/mirrors-prettier
+ rev: v4.0.0-alpha.3-1
+ hooks:
+ - id: prettier
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
diff --git a/.pylintrc b/.pylintrc
index f74e19c..14b2358 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -8,4 +8,4 @@ ignore =
max-line-length=120
[MESSAGES CONTROL]
-disable=C0103,W0102,E0401
+disable=too-few-public-methods,invalid-name
diff --git a/.vscode/settings.json b/.vscode/settings.json
index cdb6be9..8194751 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,6 @@
{
- "cornflakes.linter.executablePath": "./venv/bin/flake8",
- "[yaml]": {
- "editor.autoIndent": "none"
- }
+ "cornflakes.linter.executablePath": "./venv/bin/flake8",
+ "[yaml]": {
+ "editor.autoIndent": "none"
+ }
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae005c9..de84bc4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,101 +1,122 @@
-## [0.1.18](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.17...v0.1.18) (2023-12-05)
+# Change Log
+
+## 0.2.0 Draft Release Notes
+
+The primary objective of this release is to make the package configurable at run-time using standard environment variables and/or a .env file. While adding this feature we also took the step of creating a config.Settings class with significant data validation capability, mostly thanks to Pydantic.
+
+Secondarily, we took steps to make this project conform to [12-factor methodology](./doc/Twelve_Factor_Methodology.md). We want to make this project more accessible to students and learners as an instructional tool while not adding undue code review workloads to anyone with merge authority for this project. To this end we've added several pre-commit code linting and code style tools as well as a quasi-standardized set of GitHub Actions CI/CD automations that manage pull requests and semantic releases.
+
+### Commit and Release Details
+
+- Add a [config.Settings](./secure_logger/conf.py) class with Pydantic validations and ability to configure at run-time via bash environment variable and/or a .env file.
+- Add [SecureLoggerConfigurationError](./secure_logger/exceptions.py) exception class to raise exception in the event of any Pydantic and/or package data validation errors during configuration.
+- add [log_level](./secure_logger/decorators.py) input parameter to decorator to allow customization of the log level on individual log entries.
+- Refactored [setup.py](./setup.py) to remove deprecated macOS functions.
+- Add a [security policy](./SECURITY.md)
+- Add a [contributor policy](.github/CONTRIBUTING.md)
+- Added README badges to report live status of unit tests and CI/CD. Converted README to markdown.
+- Add [Dependabot](.github/dependabot.yml) and [Mergify](./.mergify.yml) to periodically monitor and update PyPi and NPM requirements
+- Add [pre-commit](./.pre-commit-config.yml) with codespell, black, flake8, isort, pylint, bandit, tox, prettier. Add built-in pre-commit hooks for code style and security.
+- Added the following Github Actions:
+ - Automated unit testing on push
+ - Auto-assign new Issues
+ - Periodic automated patch releases after Dependabot runs
+ - Pull request automation
+ - Semantic release
+ - Automated merge of main to dev branches
+- Add GitHub templates for [Issue](./.github/ISSUE_TEMPLATE/), [Contributing](./.github/CONTRIBUTING.md), [Funding](./.github/FUNDING.yml), [Pull Request](./.github/PULL_REQUEST_TEMPLATE.md)
+- Add the following to the [Makefile](./Makefile)
+ - Recognition of .env file
+ - Scaffold multi platform support
+ - Make lint
+ - Make help
+## [0.1.18](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.17...v0.1.18) (2023-12-05)
### Bug Fixes
-* add missing actions ([d2ea137](https://github.com/FullStackWithLawrence/secure-logger/commit/d2ea1376034fcd33b47a53da9cb15ad893448314))
+- add missing actions ([d2ea137](https://github.com/FullStackWithLawrence/secure-logger/commit/d2ea1376034fcd33b47a53da9cb15ad893448314))
## [0.1.17](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.16...v0.1.17) (2023-11-14)
-
### Bug Fixes
-* add regex expressions to correctly evaluate all possible values of __version__.py ([1ac6246](https://github.com/FullStackWithLawrence/secure-logger/commit/1ac6246f4840457a62d6a7a6ccf03065c28643c7))
-* restore npx semantic-release logic in next branch ([e8e4b1d](https://github.com/FullStackWithLawrence/secure-logger/commit/e8e4b1db87d3e04e8e263080ca8adc4fda989d86))
+- add regex expressions to correctly evaluate all possible values of **version**.py ([1ac6246](https://github.com/FullStackWithLawrence/secure-logger/commit/1ac6246f4840457a62d6a7a6ccf03065c28643c7))
+- restore npx semantic-release logic in next branch ([e8e4b1d](https://github.com/FullStackWithLawrence/secure-logger/commit/e8e4b1db87d3e04e8e263080ca8adc4fda989d86))
## [0.1.16](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.15...v0.1.16) (2023-11-14)
-
### Bug Fixes
-* convert prerelease version to strict semantic equivalent ([f463db5](https://github.com/FullStackWithLawrence/secure-logger/commit/f463db5d53499166968fe0d9a36a36e3327d36d4))
-* remove pre-commit ([12c3da0](https://github.com/FullStackWithLawrence/secure-logger/commit/12c3da0042cd7ff03783c278fddf1c3bb6b4f69a))
-* set prerelease to true for next and nexst-major ([b830102](https://github.com/FullStackWithLawrence/secure-logger/commit/b8301020b57a5e1bc09517100d3d2724e9c7d716))
+- convert prerelease version to strict semantic equivalent ([f463db5](https://github.com/FullStackWithLawrence/secure-logger/commit/f463db5d53499166968fe0d9a36a36e3327d36d4))
+- remove pre-commit ([12c3da0](https://github.com/FullStackWithLawrence/secure-logger/commit/12c3da0042cd7ff03783c278fddf1c3bb6b4f69a))
+- set prerelease to true for next and nexst-major ([b830102](https://github.com/FullStackWithLawrence/secure-logger/commit/b8301020b57a5e1bc09517100d3d2724e9c7d716))
## [0.1.15](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.14...v0.1.15) (2023-11-14)
-
### Bug Fixes
-* add npx semantic-release --dry-run --no-ci ([d0133ab](https://github.com/FullStackWithLawrence/secure-logger/commit/d0133ab6d966370b5eabf44e3543c81d3fe6b850))
+- add npx semantic-release --dry-run --no-ci ([d0133ab](https://github.com/FullStackWithLawrence/secure-logger/commit/d0133ab6d966370b5eabf44e3543c81d3fe6b850))
## [0.1.14](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.13...v0.1.14) (2023-11-14)
-
### Bug Fixes
-* fix requirements path for semantic release ([f2ad049](https://github.com/FullStackWithLawrence/secure-logger/commit/f2ad049e7a146a3eadc75606db87651c0e89e59a))
-* must add next and next-major ([7434ecf](https://github.com/FullStackWithLawrence/secure-logger/commit/7434ecfc7e82cf3c6c9b2e771ea410883a722343))
+- fix requirements path for semantic release ([f2ad049](https://github.com/FullStackWithLawrence/secure-logger/commit/f2ad049e7a146a3eadc75606db87651c0e89e59a))
+- must add next and next-major ([7434ecf](https://github.com/FullStackWithLawrence/secure-logger/commit/7434ecfc7e82cf3c6c9b2e771ea410883a722343))
## [0.1.13](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.12...v0.1.13) (2023-11-14)
-
### Bug Fixes
-* long_description_content_type='text/x-rst' ([d551597](https://github.com/FullStackWithLawrence/secure-logger/commit/d551597c208b3de6d635947ae2247c755928816e))
+- long_description_content_type='text/x-rst' ([d551597](https://github.com/FullStackWithLawrence/secure-logger/commit/d551597c208b3de6d635947ae2247c755928816e))
## [0.1.12](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.11...v0.1.12) (2023-11-14)
-
### Bug Fixes
-* refactor python code blocks using readme_renderer compliant directives ([49a3423](https://github.com/FullStackWithLawrence/secure-logger/commit/49a34234bb95ed37676f0620eb481a221302b73a))
+- refactor python code blocks using readme_renderer compliant directives ([49a3423](https://github.com/FullStackWithLawrence/secure-logger/commit/49a34234bb95ed37676f0620eb481a221302b73a))
## [0.1.11](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.10...v0.1.11) (2023-11-14)
-
### Bug Fixes
-* CURRENT_VERSION was unassigned ([528fa71](https://github.com/FullStackWithLawrence/secure-logger/commit/528fa71dc0fc008bd7098686cab94198a961f5c3))
+- CURRENT_VERSION was unassigned ([528fa71](https://github.com/FullStackWithLawrence/secure-logger/commit/528fa71dc0fc008bd7098686cab94198a961f5c3))
## [0.1.10](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.9...v0.1.10) (2023-11-14)
-
### Bug Fixes
-* add a dedicated version bump workflow ([8c59680](https://github.com/FullStackWithLawrence/secure-logger/commit/8c596807fda14a7d1ec927267d68db776ef1d822))
-* need to git pull ([24f177d](https://github.com/FullStackWithLawrence/secure-logger/commit/24f177da2ef4b70ba73c69b9c1c51133a6d29c3c))
-* need to git pull ([2e60dbb](https://github.com/FullStackWithLawrence/secure-logger/commit/2e60dbbd5922649e0dc5b0177f145d49f7b476dd))
-* need to git pull ([e173922](https://github.com/FullStackWithLawrence/secure-logger/commit/e173922d290eba66addda88f408aa0d576382214))
-* remove version bump ([7b71ae9](https://github.com/FullStackWithLawrence/secure-logger/commit/7b71ae9c484486cd124b751bc201f16cc2d627bf))
+- add a dedicated version bump workflow ([8c59680](https://github.com/FullStackWithLawrence/secure-logger/commit/8c596807fda14a7d1ec927267d68db776ef1d822))
+- need to git pull ([24f177d](https://github.com/FullStackWithLawrence/secure-logger/commit/24f177da2ef4b70ba73c69b9c1c51133a6d29c3c))
+- need to git pull ([2e60dbb](https://github.com/FullStackWithLawrence/secure-logger/commit/2e60dbbd5922649e0dc5b0177f145d49f7b476dd))
+- need to git pull ([e173922](https://github.com/FullStackWithLawrence/secure-logger/commit/e173922d290eba66addda88f408aa0d576382214))
+- remove version bump ([7b71ae9](https://github.com/FullStackWithLawrence/secure-logger/commit/7b71ae9c484486cd124b751bc201f16cc2d627bf))
## [0.1.9](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.8...v0.1.9) (2023-11-13)
-
### Bug Fixes
-* have to skip CI automated unit tests when commit __version__.py ([0e939c8](https://github.com/FullStackWithLawrence/secure-logger/commit/0e939c87eb3ab0aafe6475691f1380127ea1860b))
-* refactor call to npx semantic-release ([2c7d768](https://github.com/FullStackWithLawrence/secure-logger/commit/2c7d768c8875a11f68ff47700e4c8a107a98c89b))
+- have to skip CI automated unit tests when commit **version**.py ([0e939c8](https://github.com/FullStackWithLawrence/secure-logger/commit/0e939c87eb3ab0aafe6475691f1380127ea1860b))
+- refactor call to npx semantic-release ([2c7d768](https://github.com/FullStackWithLawrence/secure-logger/commit/2c7d768c8875a11f68ff47700e4c8a107a98c89b))
## [0.1.8](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.7...v0.1.8) (2023-11-13)
-
### Bug Fixes
-* required upgrade to node 20.9.0 ([756ea63](https://github.com/FullStackWithLawrence/secure-logger/commit/756ea6366bfe828783be17f4d581e8b4cd7f6574))
-* required upgrade to node 20.9.0 ([9bc8871](https://github.com/FullStackWithLawrence/secure-logger/commit/9bc88718b3ffbfdd002d9156dcbad3db974b723f))
+- required upgrade to node 20.9.0 ([756ea63](https://github.com/FullStackWithLawrence/secure-logger/commit/756ea6366bfe828783be17f4d581e8b4cd7f6574))
+- required upgrade to node 20.9.0 ([9bc8871](https://github.com/FullStackWithLawrence/secure-logger/commit/9bc88718b3ffbfdd002d9156dcbad3db974b723f))
## [0.1.7](https://github.com/FullStackWithLawrence/secure-logger/compare/v0.1.6...v0.1.7) (2023-11-13)
-
### Bug Fixes
-* add missing package descriptors ([72bc5b3](https://github.com/FullStackWithLawrence/secure-logger/commit/72bc5b3c296e3f5d809ecad894c8b25aa1e0e0fa))
-* correct path to python requirements ([cdc0557](https://github.com/FullStackWithLawrence/secure-logger/commit/cdc05577f5b6e5c1bb46b141ff61ff00a0eca899))
-* fix calls to unit tests ([9b98d18](https://github.com/FullStackWithLawrence/secure-logger/commit/9b98d18016cfe99b38298bd910831c1de3988db0))
-* fix path to secure_logger ([1d6a1d9](https://github.com/FullStackWithLawrence/secure-logger/commit/1d6a1d90c233464746a85dcb0add3ba6e0f8297a))
-* move __version__.py to the root ([da0beac](https://github.com/FullStackWithLawrence/secure-logger/commit/da0beac04ec3144936aa92fb5deca8b97945b439))
-* remove reference to django migrations ([e0a62be](https://github.com/FullStackWithLawrence/secure-logger/commit/e0a62be13deb323f683d39134f42b203e413ff49))
+- add missing package descriptors ([72bc5b3](https://github.com/FullStackWithLawrence/secure-logger/commit/72bc5b3c296e3f5d809ecad894c8b25aa1e0e0fa))
+- correct path to python requirements ([cdc0557](https://github.com/FullStackWithLawrence/secure-logger/commit/cdc05577f5b6e5c1bb46b141ff61ff00a0eca899))
+- fix calls to unit tests ([9b98d18](https://github.com/FullStackWithLawrence/secure-logger/commit/9b98d18016cfe99b38298bd910831c1de3988db0))
+- fix path to secure_logger ([1d6a1d9](https://github.com/FullStackWithLawrence/secure-logger/commit/1d6a1d90c233464746a85dcb0add3ba6e0f8297a))
+- move **version**.py to the root ([da0beac](https://github.com/FullStackWithLawrence/secure-logger/commit/da0beac04ec3144936aa92fb5deca8b97945b439))
+- remove reference to django migrations ([e0a62be](https://github.com/FullStackWithLawrence/secure-logger/commit/e0a62be13deb323f683d39134f42b203e413ff49))
# CHANGE LOG
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index e6947b9..9c4918e 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -2,11 +2,11 @@
## 1. Purpose
-A primary goal of aws_openai is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
+A primary goal of secure_logger is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
-We invite all those who participate in aws_openai to help us create safe and positive experiences for everyone.
+We invite all those who participate in secure_logger to help us create safe and positive experiences for everyone.
## 2. Open Source Citizenship
@@ -39,7 +39,7 @@ The following behaviors are considered harassment and are unacceptable within ou
## 5. Enforcement
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [FullStackWithLawrence@gmail.com](mailto:FullStackWithLawrence@gmail.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [lpm0073@gmail.com](mailto:lpm0073@gmail.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
diff --git a/Makefile b/Makefile
index 784f0b8..def05e0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,93 +1,145 @@
+SHELL := /bin/bash
PYTHON = python3
PIP = $(PYTHON) -m pip
-.PHONY: pre-commit requirements init clean report test build release-test release-prod help
+
+ifeq ($(OS),Windows_NT)
+ ACTIVATE_VENV = venv\Scripts\activate
+else
+ ACTIVATE_VENV = source venv/bin/activate
+endif
+
+ifneq ("$(wildcard .env)","")
+ include .env
+endif
+
+.PHONY: analyze init pre-commit requirements lint clean test build force-release publish-test publish-prod help
# Default target executed when no arguments are given to make.
all: help
+analyze:
+ cloc . --exclude-ext=svg,json,zip --vcs=git
+
+# -------------------------------------------------------------------------
+# Initialize. create virtual environment and install requirements
+# -------------------------------------------------------------------------
init:
make clean && \
npm install && \
python3.11 -m venv venv && \
- source venv/bin/activate && \
+ $(ACTIVATE_VENV) && \
pip install --upgrade pip && \
- make requirements && \
- pre-commit install
+ make requirements
+# -------------------------------------------------------------------------
+# Install and run pre-commit hooks
+# -------------------------------------------------------------------------
pre-commit:
pre-commit install
pre-commit run --all-files
+# -------------------------------------------------------------------------
+# Install requirements: Python, npm and pre-commit
+# -------------------------------------------------------------------------
requirements:
rm -rf .tox
$(PIP) install --upgrade pip wheel
$(PIP) install -r requirements/local.txt && \
npm install && \
- pre-commit install && \
+ make pre-commit
pre-commit autoupdate
+# -------------------------------------------------------------------------
+# Run black and pre-commit hooks.
+# includes prettier, isort, flake8, pylint, etc.
+# -------------------------------------------------------------------------
lint:
pre-commit run --all-files && \
black .
+# -------------------------------------------------------------------------
+# Destroy all build artifacts and Python temporary files
+# -------------------------------------------------------------------------
clean:
rm -rf venv .pytest_cache __pycache__ .pytest_cache node_modules && \
rm -rf build dist secure_logger.egg-info
-report:
- cloc . --exclude-ext=svg,json,zip --vcs=git
-
+# -------------------------------------------------------------------------
+# Run Python unit tests
+# -------------------------------------------------------------------------
test:
python -m unittest discover -s secure_logger/tests/ && \
python -m setup_test
+# -------------------------------------------------------------------------
+# Build the project
+# -------------------------------------------------------------------------
build:
+ @echo "-------------------------------------------------------------------------"
+ @echo " I. Unit tests"
+ @echo "-------------------------------------------------------------------------"
+ make test
+ @echo "-------------------------------------------------------------------------"
+ @echo " II. Check version"
+ @echo "-------------------------------------------------------------------------"
npx semantic-release --doctor --dry-run
-
+ @echo "-------------------------------------------------------------------------"
+ @echo " III. Initializing the project,"
+ @echo " Linting and running pre-commit hooks"
+ @echo "-------------------------------------------------------------------------"
+ make init
+ . venv/bin/activate
$(PIP) install --upgrade setuptools wheel twine
$(PIP) install --upgrade build
-
- make clean
+ @echo "-------------------------------------------------------------------------"
+ @echo " IV. Building the project"
+ @echo "-------------------------------------------------------------------------"
$(PYTHON) -m build --sdist ./
$(PYTHON) -m build --wheel ./
- $(PYTHON) -m pip install --upgrade twine
+ @echo "-------------------------------------------------------------------------"
+ @echo " V. Verifying the build"
+ @echo "-------------------------------------------------------------------------"
twine check dist/*
-release:
+# -------------------------------------------------------------------------
+# Force a new semantic release to be created in GitHub
+# -------------------------------------------------------------------------
+force-release:
git commit -m "fix: force a new release" --allow-empty && git push
# -------------------------------------------------------------------------
-# upload to PyPi Test
+# Publish to PyPi Test
# https://test.pypi.org/project/secure-logger/
# -------------------------------------------------------------------------
-release-test:
+publish-test:
git rev-parse --abbrev-ref HEAD | grep '^main$' || (echo 'Not on main branch, aborting' && exit 1)
make build
twine upload --verbose --skip-existing --repository testpypi dist/*
# -------------------------------------------------------------------------
-# upload to PyPi
+# Publish to PyPi
# https://pypi.org/project/secure-logger/
# -------------------------------------------------------------------------
-release-prod:
+publish-prod:
git rev-parse --abbrev-ref HEAD | grep '^main$' || (echo 'Not on main branch, aborting' && exit 1)
make build
twine upload --verbose --skip-existing dist/*
-######################
-# HELP
-######################
-
+# -------------------------------------------------------------------------
+# Generate help menu
+# -------------------------------------------------------------------------
help:
@echo '===================================================================='
+ @echo 'init - build virtual environment and install requirements'
+ @echo 'analyze - runs cloc report'
@echo 'pre-commit - install and configure pre-commit hooks'
@echo 'requirements - install Python, npm and pre-commit requirements'
@echo 'lint - run black and pre-commit hooks'
- @echo 'init - build virtual environment and install requirements'
@echo 'clean - destroy all build artifacts'
- @echo 'repository - runs cloc report'
+ @echo 'test - run Python unit tests'
@echo 'build - build the project'
- @echo 'release-test - test deployment to PyPi'
- @echo 'release-prod - production deployment to PyPi'
+ @echo 'force-release - force a new release to be created in GitHub'
+ @echo 'publish-test - test deployment to PyPi'
+ @echo 'publish-prod - production deployment to PyPi'
diff --git a/README.md b/README.md
index 1824d69..781149c 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,8 @@
[](https://www.youtube.com/@FullStackWithLawrence)
[](https://www.python.org/)
-[](https://github.com/FullStackWithLawrence/secure-logger/actions)
+[](./doc/Twelve_Factor_Methodology.md)
+[](https://github.com/FullStackWithLawrence/secure-logger/actions)

[](https://github.com/FullStackWithLawrence/secure-logger)
@@ -23,7 +24,7 @@ application logs.
## Installation
-``` bash
+```bash
pip install secure-logger
```
@@ -31,30 +32,33 @@ pip install secure-logger
### As a decorator
-``` python
+```python
from secure_logger.decorators import secure_logger
+import logging
-class Foo(object):
+logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
- @secure_logger()
+class Foo(object):
+ @secure_logger(log_level='INFO')
def bar(self, dict_data, list_data):
pass
# call your method, passing some sensitive data
dict_data = {
- 'not_a_sensitive_key': 'you-can-see-me',
- 'aws-access-key_id': conf.AWS_ACCESS_KEY_ID,
- 'aws-secret-access-key': conf.AWS_SECRET_ACCESS_KEY
+ "not_a_sensitive_key": "you-can-see-me",
+ "aws-access-key-id": "i-am-hidden",
+ "aws-secret-access-key": "so-am-i",
}
-list_data = ['foo', 'bar']
+list_data = ["foo", "bar"]
foo = Foo()
foo.bar(dict_data=dict_data, list_data=list_data)
```
Log output:
-``` log
-INFO:secure_logger: __main__.Foo().bar() keyword args: {
+```console
+INFO:secure_logger: __main__.bar() ['<__main__.Foo object at 0x103474ac0>'] keyword args: {
"dict_data": {
"not_a_sensitive_key": "you-can-see-me",
"aws-access-key-id": "*** -- secure_logger() -- ***",
@@ -64,12 +68,11 @@ INFO:secure_logger: __main__.Foo().bar() keyword args: {
"foo",
"bar"
]
-}
```
### As library functions
-``` python
+```python
from secure_logger.masked_dict import masked_dict, masked_dict2str
dict_data = {
@@ -82,7 +85,7 @@ print(masked_dict2str(dict_data))
Output:
-``` bash
+```bash
{
"not_a_sensitive_key": "you-can-see-me",
"aws-access-key-id": "*** -- secure_logger() -- ***",
@@ -92,28 +95,29 @@ Output:
## Configuration
-secure_logger accepts optional parameters.
+secure_logger accepts optional parameters which you can configure as either bash environment variables or with a .env file placed in the root of your project
+
+- **SECURE_LOGGER_SENSITIVE_KEYS**: a Python list of dictionary keys. Not case sensitive.
+- **SECURE_LOGGER_REDACTION_MESSAGE**: a string value that will replace the sensitive key values
+- **SECURE_LOGGER_INDENTATION**: number of characters to indent JSON string output when logging output
+- **SECURE_LOGGER_LOG_LEVEL**: the level at which secure_logger generates log entries. One of: 'CRITICAL', 'FATAL', 'ERROR', 'WARN', 'WARNING', 'INFO', 'DEBUG'
-- sensitive_keys: a Python list of dictionary keys. Not case
- sensitive.
-- message: a string value that will replace the sensitive key values
-- indent: number of characters to indent JSON string output when
- logging output
+Additionally, you can override individual invocations of the decorator with custom parameters:
-``` python
+```python
class MyClass():
- @secure_logger(sensitive_keys=["password", "token", "crown_jewels"], message="***", indent=4)
- def another_def(self):
+ @secure_logger(log_level='DEBUG', sensitive_keys=["password", "apikey", "crown_jewels"], message="*** -- TOP SECRET -- ***", indent=4)
+ def another_function(self, password: str, apikey: str, crown_jewels: List(dict)):
pass
```
## Configuration Defaults
-``` python
-DEFAULT_REDACTION_MESSAGE = "*** -- secure_logger() -- ***"
-DEFAULT_INDENT = 4
-DEFAULT_SENSITIVE_KEYS = [
+```python
+SECURE_LOGGER_REDACTION_MESSAGE = "*** -- secure_logger() -- ***"
+SECURE_LOGGER_INDENTATION = 4
+SECURE_LOGGER_SENSITIVE_KEYS = [
"password",
"token",
"client_id",
@@ -129,9 +133,15 @@ DEFAULT_SENSITIVE_KEYS = [
"aws-access-key-id",
"aws-secret-access-key",
]
+SECURE_LOGGER_LOG_LEVEL = 'DEBUG'
```
-### Contributing
+## Contributing
+
+Pull requests are welcomed and encouraged!
+
+- This project uses an automated [Pull Request](.github/workflows/pullRequestController.yml) CI/CD process.
+- This project conforms to [12-Factor Methodology](./doc/Twelve_Factor_Methodology.md).
+- This project uses [Semantic Versioning](./doc/SEMANTIC_VERSIONING.md) which requires that git commit messages follow strict (but easy to learn) formatting rules.
-Pull requests are welcome, and you can also contact [Lawrence
-McDaniel](https://lawrencemcdaniel.com/contact) directly.
+Contact: [Lawrence McDaniel](https://lawrencemcdaniel.com/contact).
diff --git a/SECURITY.md b/SECURITY.md
index 101ea9e..0dde0f4 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -4,14 +4,14 @@
Only the latest version of this project is currently being supported with security updates.
-| Version | Supported |
-| ------- | ------------------ |
-| latest | :white_check_mark: |
-| < latest| :x: |
+| Version | Supported |
+| -------- | ------------------ |
+| latest | :white_check_mark: |
+| < latest | :x: |
## Reporting a Vulnerability
-If you discover a security vulnerability within this project, please send an email to [FullStackWithLawrence@gmail.com](mailto:FullStackWithLawrence@gmail.com). All security vulnerabilities will be promptly addressed.
+If you discover a security vulnerability within this project, please send an email to [lpm0073@gmail.com](mailto:lpm0073@gmail.com). All security vulnerabilities will be promptly addressed.
Please do not publicly disclose the issue until it has been addressed by the team.
diff --git a/commitlint.config.js b/commitlint.config.js
index d872c08..b58fa47 100644
--- a/commitlint.config.js
+++ b/commitlint.config.js
@@ -3,22 +3,21 @@ const Configuration = {
* Resolve and load @commitlint/config-conventional from node_modules.
* Referenced packages must be installed
*/
- extends: ['@commitlint/config-conventional', '@commitlint/config-angular'],
+ extends: ["@commitlint/config-conventional", "@commitlint/config-angular"],
/*
* Resolve and load conventional-changelog-atom from node_modules.
* Referenced packages must be installed
*/
- parserPreset: 'conventional-changelog-atom',
+ parserPreset: "conventional-changelog-atom",
/*
* Resolve and load @commitlint/format from node_modules.
* Referenced package must be installed
*/
- formatter: '@commitlint/format',
+ formatter: "@commitlint/format",
/*
* Any rules defined here will override rules from @commitlint/config-conventional
*/
- rules: {
- },
+ rules: {},
/*
* Array of functions that return true if commitlint should ignore the given message.
* Given array is merged with predefined functions, which consist of matchers like:
@@ -40,7 +39,7 @@ const Configuration = {
* Custom URL to show upon failure
*/
helpUrl:
- 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint',
+ "https://github.com/conventional-changelog/commitlint/#what-is-commitlint",
/*
* Custom prompt configs
*/
@@ -48,7 +47,7 @@ const Configuration = {
messages: {},
questions: {
type: {
- description: 'please input type:',
+ description: "please input type:",
},
},
},
diff --git a/SEMANTIC_VERSIONING.md b/doc/SEMANTIC_VERSIONING.md
similarity index 100%
rename from SEMANTIC_VERSIONING.md
rename to doc/SEMANTIC_VERSIONING.md
diff --git a/doc/Twelve_Factor_Methodology.md b/doc/Twelve_Factor_Methodology.md
new file mode 100644
index 0000000..279da79
--- /dev/null
+++ b/doc/Twelve_Factor_Methodology.md
@@ -0,0 +1,16 @@
+# 12-Factor Methodology
+
+This project conforms to [12-factor methodology](https://12factor.net/). We want to make this project more accessible to students and learners as an instructional tool while not adding undue code review workloads to anyone with merge authority for this project. To this end we've added several pre-commit code linting and code style tools as well as a quasi-standardized set of GitHub Actions CI/CD automations that manage pull requests and semantic releases.
+
+- 1. **Codebase**: [✅] One codebase tracked in revision control, many deploys
+- 2. **Dependencies**: [✅] Explicitly declare and isolate dependencies. We're using setup.py, requirements.txt, and package.json to identify dependencies.
+- 3. **Config**: [✅] Store config in the environment. This release implements config.Settings, which stores all configuration information for the package.
+- 4. **Backing services**: [-] Treat backing services as attached resources. Not applicable to this project.
+- 5. **Build, release, run**: [✅] Strictly separate build and run stages. `Build` is implemented in Makefile, `release` is implemented as a GitHub Action, and `run` is deferred to the projects that include this package.
+- 6. **Processes**: [✅] Execute the app as one or more stateless processes
+- 7. **Port binding**: [-] Export services via port binding. Not Applicable. This package does not implement any services.
+- 8. **Concurrency**: Scale out via the process model
+- 9. **Disposability**: [✅] Maximize robustness with fast startup and graceful shutdown
+- 10. **Dev/prod parity**: [✅] Keep development, staging, and production as similar as possible. The GitHub Action [pushMain.yml](.github/workflows/pushMain.yml) executes a forced merge from main to dev branches. This ensure that all dev branches are synced to main immediately after pull requests are merged to main.
+- 11. **Logs**: [✅] Treat logs as event streams. Obviously 😉
+- 12. **Admin processes**: [✅] Run admin/management tasks as one-off processes. All admin processes are implemented with GitHub Actions and other GitHub management features.
diff --git a/pyproject.toml b/pyproject.toml
index 2b766c7..c8e5e38 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,5 @@
[build-system]
-requires = ["flake8", "gitlint", "bump2version"]
+requires = ["setuptools", "wheel"]
[tool.isort]
profile = "black"
diff --git a/release.config.js b/release.config.js
index 56490a2..1029b22 100644
--- a/release.config.js
+++ b/release.config.js
@@ -1,37 +1,38 @@
module.exports = {
- "dryRun": false,
- "branches": [
+ dryRun: false,
+ branches: [
+ {
+ name: "next",
+ prerelease: true,
+ },
+ {
+ name: "next-major",
+ prerelease: true,
+ },
+ "main",
+ ],
+ plugins: [
+ "@semantic-release/commit-analyzer",
+ "@semantic-release/release-notes-generator",
+ [
+ "@semantic-release/changelog",
{
- "name": "next",
- "prerelease": true
+ changelogFile: "CHANGELOG.md",
},
+ ],
+ "@semantic-release/github",
+ [
+ "@semantic-release/git",
{
- "name": "next-major",
- "prerelease": true
+ assets: [
+ "CHANGELOG.md",
+ "client/package.json",
+ "client/package-lock.json",
+ "requirements/local.txt",
+ ],
+ message:
+ "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}",
},
- "main"
],
- "plugins": [
- "@semantic-release/commit-analyzer",
- "@semantic-release/release-notes-generator",
- [
- "@semantic-release/changelog",
- {
- "changelogFile": "CHANGELOG.md"
- }
- ],
- "@semantic-release/github",
- [
- "@semantic-release/git",
- {
- "assets": [
- "CHANGELOG.md",
- "client/package.json",
- "client/package-lock.json",
- "requirements/local.txt",
- ],
- "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
- }
- ]
- ]
- };
+ ],
+};
diff --git a/requirements/local.txt b/requirements/local.txt
index 6e45234..9d82980 100644
--- a/requirements/local.txt
+++ b/requirements/local.txt
@@ -16,3 +16,7 @@ bandit==1.7.5
pydocstringformatter==0.7.3
tox==4.11.4
codespell==2.2.6
+
+# project dependencies
+# ------------
+pydantic==2.5.2
diff --git a/secure_logger/__version__.py b/secure_logger/__version__.py
index 7ac0976..55359f0 100644
--- a/secure_logger/__version__.py
+++ b/secure_logger/__version__.py
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
-__version__ = "0.1.18-next.1"
+# DO NOT EDIT.
+# Managed via automated CI/CD in .github/workflows/semanticVersionBump.yml.
+__version__ = "0.2.0-next.1"
diff --git a/secure_logger/conf.py b/secure_logger/conf.py
new file mode 100644
index 0000000..8101dd0
--- /dev/null
+++ b/secure_logger/conf.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+# pylint: disable=no-member
+"""Configuration for secure_logger"""
+
+import logging
+
+from pydantic import BaseModel, Field, ValidationError, validator
+
+from secure_logger.exceptions import SecureLoggerConfigurationError
+
+
+LOGGER_NAME = "decorator_logger"
+DEFAULT_SECURE_LOGGER_SENSITIVE_KEYS = [
+ "password",
+ "token",
+ "client_id",
+ "client_secret",
+ "Authorization",
+ "secret",
+ "access_key_id",
+ "secret_access_key",
+ "access-key-id",
+ "secret-access-key",
+ "aws_access_key_id",
+ "aws_secret_access_key",
+ "aws-access-key-id",
+ "aws-secret-access-key",
+]
+DEFAULT_SECURE_LOGGER_REDACTION_MESSAGE = "*** -- secure_logger() -- ***"
+DEFAULT_SECURE_LOGGER_INDENTATION = 4
+DEFAULT_SECURE_LOGGER_LOG_LEVEL = "DEBUG"
+
+# pylint: disable=protected-access
+DEFAULT_SECURE_LOGGER_LOG_LEVELS = [level.upper() for level in logging._nameToLevel if level != "NOTSET"]
+
+
+class Settings(BaseModel):
+ """Settings for secure_logger"""
+
+ class Config:
+ """Pydantic configuration"""
+
+ # necessary to allow logging.Logger
+ arbitrary_types_allowed = True
+
+ logger_name: str = Field(LOGGER_NAME.lower())
+ logging_logger: logging.Logger = Field(default_factory=lambda: logging.getLogger(LOGGER_NAME.lower()))
+ sensitive_keys: list = Field(DEFAULT_SECURE_LOGGER_SENSITIVE_KEYS, env="SECURE_LOGGER_SENSITIVE_KEYS")
+ redaction_message: str = Field(DEFAULT_SECURE_LOGGER_REDACTION_MESSAGE, env="SECURE_LOGGER_REDACTION_MESSAGE")
+ indentation: int = Field(DEFAULT_SECURE_LOGGER_INDENTATION, gt=0, env="SECURE_LOGGER_INDENTATION")
+ logging_level: str = Field(DEFAULT_SECURE_LOGGER_LOG_LEVEL, env="SECURE_LOGGER_LOG_LEVEL")
+ logging_levels: list = Field(DEFAULT_SECURE_LOGGER_LOG_LEVELS)
+
+ @validator("logging_level")
+ @classmethod
+ def must_be_valid_log_level(cls, v):
+ """Validate the log level"""
+ if v not in DEFAULT_SECURE_LOGGER_LOG_LEVELS:
+ raise ValueError("invalid log level")
+ return v
+
+ @property
+ def logger_function(self):
+ """Returns the logger function for the specified logging level"""
+ return self.get_logger_function(self.logging_level)
+
+ @property
+ def logging_level_int(self) -> int:
+ """Returns the logger level for the specified logging level"""
+ # pylint: disable=protected-access
+ return logging._nameToLevel.get(self.logging_level, logging.DEBUG)
+
+ def get_logger_function(self, log_level):
+ """Returns the logger function for the specified logging level"""
+ log_levels = {
+ "DEBUG": self.logging_logger.debug,
+ "INFO": self.logging_logger.info,
+ "WARNING": self.logging_logger.warning,
+ "ERROR": self.logging_logger.error,
+ "CRITICAL": self.logging_logger.critical,
+ }
+ return log_levels.get(log_level, logging.debug)
+
+
+settings = None
+try:
+ settings = Settings()
+except ValidationError as e:
+ raise SecureLoggerConfigurationError("Invalid configuration: " + str(e)) from e
+
+logger = logging.getLogger(LOGGER_NAME)
+logger.debug("SECURE_LOGGER_SENSITIVE_KEYS: %s", settings.sensitive_keys)
+logger.debug("SECURE_LOGGER_REDACTION_MESSAGE: %s", settings.redaction_message)
+logger.debug("SECURE_LOGGER_INDENTATION: %s", settings.indentation)
+logger.debug("SECURE_LOGGER_LOG_LEVEL: %s", settings.logging_level)
diff --git a/secure_logger/decorators.py b/secure_logger/decorators.py
index fd8ab8d..a31241e 100644
--- a/secure_logger/decorators.py
+++ b/secure_logger/decorators.py
@@ -2,29 +2,30 @@
"""Python Secure Logger."""
# python stuff
import inspect
-import logging
from functools import wraps
# our stuff
-from .masked_dict import (
- DEFAULT_INDENT,
- DEFAULT_REDACTION_MESSAGE,
- DEFAULT_SENSITIVE_KEYS,
- masked_dict2str,
-)
-
-
-# module initializations
-logger = logging.getLogger(__name__)
+from secure_logger.conf import settings
+from secure_logger.exceptions import SecureLoggerConfigurationError
+from secure_logger.masked_dict import masked_dict2str
def secure_logger(
- sensitive_keys: list = DEFAULT_SENSITIVE_KEYS,
- indent: int = DEFAULT_INDENT,
- message: str = DEFAULT_REDACTION_MESSAGE,
+ log_level: str = settings.logging_level,
+ sensitive_keys: list = settings.sensitive_keys,
+ indent: int = settings.indentation,
+ message: str = settings.redaction_message,
):
"""Top level decorator, for defining input parameters."""
+ logging_levels = list(settings.logging_levels)
+ if log_level not in logging_levels:
+ raise SecureLoggerConfigurationError(f"Invalid logging level: {log_level}. Valid values are: {logging_levels}")
+ if indent < 0:
+ raise SecureLoggerConfigurationError(f"Invalid indentation value: {indent}. Valid values are: 0 or greater.")
+ if message == "":
+ raise SecureLoggerConfigurationError(f"Invalid redaction message: {message}. Must be a non-empty string.")
+
def decorate(func):
"""
Decorate a Python a class, a class method, or a function.
@@ -32,8 +33,7 @@ def decorate(func):
Adds a log entry with the module name, class name and method/function name,
its positional arguments, and keyword pairs presented as a formatted dict.
- Sample output:
- 2022-07-08 19:40:51,085 INFO secure_logger: courses.views.CourseListingView().get_queryset()
+ Sample output: see README.md
"""
@wraps(func)
@@ -69,14 +69,21 @@ def wrapper(*args, **kwargs):
name_spec = name_of_module + "." if name_of_module else ""
name_spec += name_of_class + "." if name_of_class else ""
name_spec += name_of_def + "()" if name_of_def else ""
-
if len(kwargs.keys()) > 0:
kwargs_dict_repr = "keyword args: "
kwargs_dict_repr += masked_dict2str(
kwargs, sensitive_keys=sensitive_keys, indent=indent, message=message
)
- logger.info(
+ # get the logger for the specified logging level taking into consideration
+ # that the user may have changed the logging level in the decorator parameters
+ logger = (
+ settings.logger_function
+ if log_level == settings.logging_level
+ else settings.get_logger_function(log_level)
+ )
+
+ logger(
"secure_logger: %s %s %s",
name_spec,
positional_args if positional_args else "",
diff --git a/secure_logger/exceptions.py b/secure_logger/exceptions.py
new file mode 100644
index 0000000..5f3dcb8
--- /dev/null
+++ b/secure_logger/exceptions.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""Module exceptions.py"""
+
+
+class SecureLoggerConfigurationError(Exception):
+ """Exception raised for errors in the configuration."""
+
+ def __init__(self, message):
+ self.message = message
+ super().__init__(self.message)
diff --git a/secure_logger/masked_dict.py b/secure_logger/masked_dict.py
index 15593e6..17802dd 100644
--- a/secure_logger/masked_dict.py
+++ b/secure_logger/masked_dict.py
@@ -5,26 +5,7 @@
import json
from unittest.mock import MagicMock
-
-# our stuff
-DEFAULT_SENSITIVE_KEYS = [
- "password",
- "token",
- "client_id",
- "client_secret",
- "Authorization",
- "secret",
- "access_key_id",
- "secret_access_key",
- "access-key-id",
- "secret-access-key",
- "aws_access_key_id",
- "aws_secret_access_key",
- "aws-access-key-id",
- "aws-secret-access-key",
-]
-DEFAULT_REDACTION_MESSAGE = "*** -- secure_logger() -- ***"
-DEFAULT_INDENT = 4
+from secure_logger.conf import settings
class _JSONEncoder(json.JSONEncoder):
@@ -44,7 +25,9 @@ def default(self, o):
def masked_dict(
- source_dict, sensitive_keys: list = DEFAULT_SENSITIVE_KEYS, message: str = DEFAULT_REDACTION_MESSAGE
+ source_dict,
+ sensitive_keys: list = settings.sensitive_keys,
+ message: str = settings.redaction_message,
) -> dict:
"""
Mask sensitive key / value in log entries.
@@ -75,16 +58,16 @@ def masked_dict(
def masked_dict2str(
obj: dict,
- sensitive_keys: list = DEFAULT_SENSITIVE_KEYS,
- indent: int = DEFAULT_INDENT,
- message: str = DEFAULT_REDACTION_MESSAGE,
+ sensitive_keys: list = settings.sensitive_keys,
+ indent: int = settings.indentation,
+ message: str = settings.redaction_message,
) -> str:
"""Return a JSON encoded string representation of a masked dict."""
return json.dumps(masked_dict(obj, sensitive_keys, message=message), cls=_JSONEncoder, indent=indent)
def serialized_masked_dict(
- obj: dict, sensitive_keys: list = DEFAULT_SENSITIVE_KEYS, indent: int = DEFAULT_INDENT
+ obj: dict, sensitive_keys: list = settings.sensitive_keys, indent: int = settings.indentation
) -> str:
"""Backwards compatibility to 0.1.2 and prior."""
return masked_dict2str(obj, sensitive_keys=sensitive_keys, indent=indent)
diff --git a/secure_logger/tests/__init__.py b/secure_logger/tests/__init__.py
index e69de29..f654774 100644
--- a/secure_logger/tests/__init__.py
+++ b/secure_logger/tests/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+"""Init file for tests module."""
+import sys
+
+
+sys.path.append("../") # noqa: E402
diff --git a/secure_logger/tests/test_class_decorator.py b/secure_logger/tests/test_class_decorator.py
new file mode 100644
index 0000000..dee7c83
--- /dev/null
+++ b/secure_logger/tests/test_class_decorator.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""Simple test bank."""
+import unittest
+
+from secure_logger.conf import settings
+from secure_logger.decorators import secure_logger
+
+
+class TestClassDecorator(unittest.TestCase):
+ """Test class logging."""
+
+ def test_class_with_default_params(self):
+ """Test class with default parameters."""
+
+ @secure_logger()
+ class MockDecoratedClass:
+ """Test 3: decorate a class."""
+
+ expected_output = (
+ settings.logging_level + ":decorator_logger:secure_logger: test_class_decorator.MockDecoratedClass. "
+ )
+
+ with self.assertLogs(level=settings.logging_level_int) as cm:
+ MockDecoratedClass()
+
+ self.assertEqual(cm.output[0][0:100], expected_output[0:100])
diff --git a/secure_logger/tests/test_class_method_decorator.py b/secure_logger/tests/test_class_method_decorator.py
new file mode 100644
index 0000000..bc88493
--- /dev/null
+++ b/secure_logger/tests/test_class_method_decorator.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+"""Simple test bank."""
+import unittest
+
+from secure_logger.conf import settings
+from secure_logger.decorators import secure_logger
+from secure_logger.exceptions import SecureLoggerConfigurationError
+
+
+class TestClassMethodDecorator(unittest.TestCase):
+ """Test class method logging."""
+
+ class MockClass:
+ """Test class method logging."""
+
+ @secure_logger()
+ def decorator_with_defaults(self, test_dict, test_list):
+ """Test class input parameter as objects."""
+
+ @secure_logger(
+ log_level="INFO",
+ sensitive_keys=["aws_secret_access_key"],
+ indent=10,
+ message="-- Forbidden! --",
+ )
+ def decorator_with_custom_params(self, test_dict, test_list):
+ """Test class input parameter as objects."""
+
+ test_dict = {
+ "insensitive_key": "you-can-see-me",
+ "aws_access_key_id": "i-am-hidden",
+ "aws_secret_access_key": "so-am-i",
+ }
+ test_list = ["foo", "bar"]
+ mock_class = MockClass()
+
+ def test_class_method_with_default_params(self):
+ """Test class method with default parameters."""
+ expected_output = (
+ settings.logging_level
+ + ":decorator_logger:secure_logger: test_class_method_decorator.decorator_with_defaults() "
+ "['', " + hello_world
+ )
+ with self.assertLogs(logger=settings.logger_name, level=settings.logging_level_int) as cm:
+ self.mock_decorated_def("hello world")
+
+ self.assertEqual(cm.output[0][0:125], expected_output[0:125])
diff --git a/secure_logger/tests/test_masked_dict.py b/secure_logger/tests/test_masked_dict.py
new file mode 100644
index 0000000..4f0c966
--- /dev/null
+++ b/secure_logger/tests/test_masked_dict.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+"""Simple test bank."""
+import json
+import unittest
+
+from secure_logger.conf import settings
+from secure_logger.masked_dict import masked_dict, masked_dict2str
+
+
+class TestMaskedDict(unittest.TestCase):
+ """Test the masked_dict function."""
+
+ test_dict = {
+ "insensitive_key": "you-can-see-me",
+ "aws_access_key_id": "i-am-hidden",
+ "aws_secret_access_key": "so-am-i",
+ }
+ expected_dict = {
+ "insensitive_key": "you-can-see-me",
+ "aws_access_key_id": settings.redaction_message,
+ "aws_secret_access_key": settings.redaction_message,
+ }
+
+ def test_masked_dict(self):
+ """Test the masked_dict function."""
+ md = masked_dict(self.test_dict)
+ self.assertDictEqual(md, self.expected_dict)
+
+ def test_masked_dict2str(self):
+ """Test the masked_dict2str function."""
+ md2s = masked_dict2str(self.test_dict)
+ md2s_to_json = json.loads(md2s)
+ self.assertDictEqual(md2s_to_json, self.expected_dict)
diff --git a/secure_logger/tests/test_masked_dict_sensitivity.py b/secure_logger/tests/test_masked_dict_sensitivity.py
new file mode 100644
index 0000000..141fd31
--- /dev/null
+++ b/secure_logger/tests/test_masked_dict_sensitivity.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+"""Simple test bank."""
+import json
+import unittest
+
+from secure_logger.conf import settings
+from secure_logger.masked_dict import masked_dict, masked_dict2str
+
+
+class TestMaskedDictCaseSensitivity(unittest.TestCase):
+ """Test the masked_dict function with case sensitivity."""
+
+ test_dict = {
+ "insensitive_key": "you-can-see-me",
+ "AWs_AcCEss_KeY_iD": "i-am-very-hidden",
+ "AWS_SECRET_ACCESS_KEY": "so-am-i",
+ }
+ expected_dict = {
+ "insensitive_key": "you-can-see-me",
+ "AWs_AcCEss_KeY_iD": settings.redaction_message,
+ "AWS_SECRET_ACCESS_KEY": settings.redaction_message,
+ }
+
+ def test_masked_dict(self):
+ """Test the masked_dict function."""
+ md = masked_dict(self.test_dict)
+ self.assertDictEqual(md, self.expected_dict)
+
+ def test_masked_dict2str(self):
+ """Test the masked_dict2str function."""
+ md2s = masked_dict2str(self.test_dict)
+ md2s_to_json = json.loads(md2s)
+ self.assertDictEqual(md2s_to_json, self.expected_dict)
diff --git a/secure_logger/tests/tests.py b/secure_logger/tests/tests.py
deleted file mode 100644
index 25ff9da..0000000
--- a/secure_logger/tests/tests.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# -*- coding: utf-8 -*-
-# pylint: disable=too-few-public-methods
-"""Simple test bank."""
-import json
-import logging
-import sys
-import unittest
-
-
-sys.path.append("../") # noqa: E402
-
-from secure_logger.decorators import ( # noqa: E402, pylint: disable=wrong-import-position
- secure_logger,
-)
-from secure_logger.masked_dict import ( # noqa: E402, pylint: disable=wrong-import-position
- DEFAULT_REDACTION_MESSAGE,
- masked_dict,
- masked_dict2str,
-)
-
-
-###############################################################################
-# TEST BANK
-###############################################################################
-class TestMaskedDict(unittest.TestCase):
- """Test the masked_dict function."""
-
- test_dict = {
- "insensitive_key": "you-can-see-me",
- "aws_access_key_id": "i-am-hidden",
- "aws_secret_access_key": "so-am-i",
- }
- expected_dict = {
- "insensitive_key": "you-can-see-me",
- "aws_access_key_id": DEFAULT_REDACTION_MESSAGE,
- "aws_secret_access_key": DEFAULT_REDACTION_MESSAGE,
- }
-
- def test_masked_dict(self):
- """Test the masked_dict function."""
- md = masked_dict(self.test_dict)
- self.assertDictEqual(md, self.expected_dict)
-
- def test_masked_dict2str(self):
- """Test the masked_dict2str function."""
- md2s = masked_dict2str(self.test_dict)
- md2s_to_json = json.loads(md2s)
- self.assertDictEqual(md2s_to_json, self.expected_dict)
-
-
-class TestMaskedDictCaseSensitivity(unittest.TestCase):
- """Test the masked_dict function with case sensitivity."""
-
- test_dict = {
- "insensitive_key": "you-can-see-me",
- "AWs_AcCEss_KeY_iD": "i-am-very-hidden",
- "AWS_SECRET_ACCESS_KEY": "so-am-i",
- }
- expected_dict = {
- "insensitive_key": "you-can-see-me",
- "AWs_AcCEss_KeY_iD": DEFAULT_REDACTION_MESSAGE,
- "AWS_SECRET_ACCESS_KEY": DEFAULT_REDACTION_MESSAGE,
- }
-
- def test_masked_dict(self):
- """Test the masked_dict function."""
- md = masked_dict(self.test_dict)
- self.assertDictEqual(md, self.expected_dict)
-
- def test_masked_dict2str(self):
- """Test the masked_dict2str function."""
- md2s = masked_dict2str(self.test_dict)
- md2s_to_json = json.loads(md2s)
- self.assertDictEqual(md2s_to_json, self.expected_dict)
-
-
-class TestCustomParams(unittest.TestCase):
- """Test the masked_dict function with custom parameters."""
-
- visible_value = "i should be visible"
- custom_keys = ["foo", "bar"]
- custom_message = "--REDACTED--"
- test_dict = {"foo": "i should be hidden", "bar": "me too", "visible_key": visible_value}
-
- def test_custom_keys(self):
- """Test the masked_dict function with custom keys."""
- expected_result = {
- "foo": DEFAULT_REDACTION_MESSAGE,
- "bar": DEFAULT_REDACTION_MESSAGE,
- "visible_key": self.visible_value,
- }
- masked_test_dict = masked_dict(self.test_dict, self.custom_keys)
- self.assertDictEqual(masked_test_dict, expected_result)
-
- def test_custom_keys_and_message(self):
- """Test the masked_dict function with custom keys and message."""
- expected_result = {"foo": self.custom_message, "bar": self.custom_message, "visible_key": self.visible_value}
- masked_test_dict = masked_dict(self.test_dict, self.custom_keys, self.custom_message)
- self.assertDictEqual(masked_test_dict, expected_result)
-
-
-class TestModuleDefDecorator(unittest.TestCase):
- """Test module function logging."""
-
- @secure_logger()
- def mock_decorated_def(self, msg):
- """Test 1: a simple module function."""
-
- def test_decorator_output(self):
- """Test 1: a simple module function."""
- hello_world = json.dumps(["'hello world'"])
- hello_world = "'hello world'"
-
- # noqa: C0301
- expected_output = (
- "INFO:secure_logger.decorators:secure_logger: tests.mock_decorated_def() "
- "['', " + hello_world
- )
- with self.assertLogs(level=logging.DEBUG) as cm:
- self.mock_decorated_def("hello world")
-
- self.assertEqual(cm.output[0][0:100], expected_output[0:100])
-
-
-class TestClassMethodDecorator(unittest.TestCase):
- """Test class method logging."""
-
- class MockClass:
- """Test class method logging."""
-
- @secure_logger()
- def decorator_with_defaults(self, test_dict, test_list):
- """Test class input parameter as objects."""
-
- @secure_logger(sensitive_keys=["aws_secret_access_key"], indent=10, message="-- Forbidden! --")
- def decorator_with_custom_params(self, test_dict, test_list):
- """Test class input parameter as objects."""
-
- test_dict = {
- "insensitive_key": "you-can-see-me",
- "aws_access_key_id": "i-am-hidden",
- "aws_secret_access_key": "so-am-i",
- }
- test_list = ["foo", "bar"]
- mock_class = MockClass()
-
- def test_class_method_with_default_params(self):
- """Test class method with default parameters."""
- expected_output = (
- "INFO:secure_logger.decorators:secure_logger: tests.decorator_with_defaults() "
- "['=2.5.0"],
extras_require={},
classifiers=[ # https://pypi.org/classifiers/
"Development Status :: 4 - Beta",