From 0f7c2da81458bf324286523e161645e171037b10 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 16:42:05 -0400 Subject: [PATCH 01/44] Updated project README to include references to the new security policy. Moved the project's code of conduct out of the contributions guide and into the appropriate policy file. Updated the contribution guide to follow the NetBox project format. Added various issue templates based on the NetBox project formats but updated for PDA. Added additional GitHub workflows to handle stale and closed issue and PR management. Removed legacy stale issue workflow that was not in use. --- .github/ISSUE_TEMPLATE/bug_report.yaml | 78 ++++++++++ .github/ISSUE_TEMPLATE/config.yml | 12 ++ .../ISSUE_TEMPLATE/documentation_change.yaml | 40 +++++ .github/ISSUE_TEMPLATE/feature_request.yaml | 71 +++++++++ .github/ISSUE_TEMPLATE/housekeeping.yaml | 24 +++ .github/PULL_REQUEST_TEMPLATE.md | 14 ++ .github/stale.yml | 20 --- .github/workflows/lock.yml | 21 +++ .github/workflows/stale.yml | 45 ++++++ README.md | 8 +- docs/CODE_OF_CONDUCT.md | 74 +++++++++ docs/CONTRIBUTING.md | 145 ++++++++++-------- docs/SECURITY.md | 31 ++++ 13 files changed, 496 insertions(+), 87 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation_change.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yaml create mode 100644 .github/ISSUE_TEMPLATE/housekeeping.yaml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/stale.yml create mode 100644 .github/workflows/lock.yml create mode 100644 .github/workflows/stale.yml create mode 100644 docs/CODE_OF_CONDUCT.md create mode 100644 docs/SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..5bacd4e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,78 @@ +--- +name: 🐛 Bug Report +description: Report a reproducible bug in the current release of PDA +labels: ["type: bug"] +body: + - type: markdown + attributes: + value: > + **NOTE:** This form is only for reporting _reproducible bugs_ in a current PDA + installation. If you're having trouble with installation or just looking for + assistance with using PDA, please visit our + [discussion forum](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions) instead. + - type: input + attributes: + label: PDA version + description: What version of PDA are you currently running? + options: + - "0.4.0" + - "0.3.0" + - "0.2.5" + - "0.2.4" + - "0.2.3" + - "0.2.2" + - "0.2.1" + - "0.2" + - "0.1" + - "I'm Not Sure" + validations: + required: true + - type: dropdown + attributes: + label: Python version + description: What version of Python are you currently running? + options: + - "3.0" + - "3.1" + - "3.2" + - "3.3" + - "3.4" + - "3.5" + - "3.6" + - "3.7" + - "3.8" + - "3.9" + - "3.10" + - "3.11" + validations: + required: true + - type: textarea + attributes: + label: Steps to Reproduce + description: > + Describe in detail the exact steps that someone else can take to + reproduce this bug using the current stable release of PDA. Begin with the + creation of any necessary database objects and call out every operation being + performed explicitly. If reporting a bug in the REST API, be sure to reconstruct + the raw HTTP request(s) being made. Additionally, **do not rely on the demo instance** for reproducing + suspected bugs, as its data is prone to modification or deletion at any time. + placeholder: | + 1. Click on "create widget" + 2. Set foo to 12 and bar to G + 3. Click the "create" button + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: What did you expect to happen? + placeholder: A new zone record should have been created with the specified values + validations: + required: true + - type: textarea + attributes: + label: Observed Behavior + description: What happened instead? + placeholder: A TypeError exception was raised + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..98109af --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,12 @@ +# Reference: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +blank_issues_enabled: false +contact_links: + - name: 📖 Contributing Policy + url: https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/docs/CONTRIBUTING.md + about: "Please read through our contributing policy before opening an issue or pull request" + - name: ❓ Discussion + url: https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions + about: "If you're just looking for help, try starting a discussion instead" + - name: 💬 Project Chat + url: https://mattermost.powerdnsadmin.org/ + about: "Join our Mattermost chat to discuss the project with other users and developers" \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/documentation_change.yaml b/.github/ISSUE_TEMPLATE/documentation_change.yaml new file mode 100644 index 0000000..584d4b4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_change.yaml @@ -0,0 +1,40 @@ +--- +name: 📖 Documentation Change +description: Suggest an addition or modification to the PDA documentation +labels: ["type: documentation"] +body: + - type: dropdown + attributes: + label: Change Type + description: What type of change are you proposing? + options: + - Addition + - Correction + - Removal + - Cleanup (formatting, typos, etc.) + validations: + required: true + - type: dropdown + attributes: + label: Area + description: To what section of the documentation does this change primarily pertain? + options: + - Features + - Installation/upgrade + - Getting started + - Configuration + - Customization + - Database Setup + - Debug + - Integrations/API + - Administration + - Development + - Other + validations: + required: true + - type: textarea + attributes: + label: Proposed Changes + description: Describe the proposed changes and why they are necessary. + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 0000000..b2e1934 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,71 @@ +--- +name: ✨ Feature Request +description: Propose a new PDA feature or enhancement +labels: ["type: feature"] +body: + - type: markdown + attributes: + value: > + **NOTE:** This form is only for submitting well-formed proposals to extend or modify + PDA in some way. If you're trying to solve a problem but can't figure out how, or if + you still need time to work on the details of a proposed new feature, please start a + [discussion](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions) instead. + - type: input + attributes: + label: PDA version + description: What version of PDA are you currently running? + options: + - "0.4.0" + - "0.3.0" + - "0.2.5" + - "0.2.4" + - "0.2.3" + - "0.2.2" + - "0.2.1" + - "0.2" + - "0.1" + - "I'm Not Sure" + validations: + required: true + - type: dropdown + attributes: + label: Feature type + options: + - Data model modification + - App Setting Addition + - Default App Setting Change + - New functionality + - Change to existing functionality + validations: + required: true + - type: textarea + attributes: + label: Proposed functionality + description: > + Describe in detail the new feature or behavior you are proposing. Include any specific changes + to work flows, data models, and/or the user interface. The more detail you provide here, the + greater chance your proposal has of being discussed. Feature requests which don't include an + actionable implementation plan will be rejected. + validations: + required: true + - type: textarea + attributes: + label: Use case + description: > + Explain how adding this functionality would benefit PDA users. What need does it address? + validations: + required: true + - type: textarea + attributes: + label: Database changes + description: > + Note any changes to the database schema necessary to support the new feature. For example, + does the proposal require adding a new model or field? (Not all new features require database + changes.) + - type: textarea + attributes: + label: External dependencies + description: > + List any new dependencies on external libraries or services that this new feature would + introduce. For example, does the proposal require the installation of a new Python package? + (Not all new features introduce new dependencies.) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/housekeeping.yaml b/.github/ISSUE_TEMPLATE/housekeeping.yaml new file mode 100644 index 0000000..dba7e3c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/housekeeping.yaml @@ -0,0 +1,24 @@ +--- +name: 🏡 Housekeeping +description: A change pertaining to the codebase itself (developers only) +labels: ["type: housekeeping"] +body: + - type: markdown + attributes: + value: > + **NOTE:** This template is for use by maintainers only. Please do not submit + an issue using this template unless you have been specifically asked to do so. + - type: textarea + attributes: + label: Proposed Changes + description: > + Describe in detail the new feature or behavior you'd like to propose. + Include any specific changes to work flows, data models, or the user interface. + validations: + required: true + - type: textarea + attributes: + label: Justification + description: Please provide justification for the proposed change(s). + validations: + required: true \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..05a6611 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ + +### Fixes: #1234 + + \ No newline at end of file diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 0c0b1c3..0000000 --- a/.github/stale.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 60 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - bug / broken-feature - - bug / security-vulnerability - - feature / request - - mod / help-wanted -# Label to use when marking an issue as stale -staleLabel: mod / stale -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: true diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000..9385024 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,21 @@ +# lock-threads (https://github.com/marketplace/actions/lock-threads) +name: 'Lock threads' + +on: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v3 + with: + issue-inactive-days: 90 + pr-inactive-days: 30 + issue-lock-reason: 'resolved' \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..66fd367 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,45 @@ +# close-stale-issues (https://github.com/marketplace/actions/close-stale-issues) +name: 'Close stale issues/PRs' + +on: + schedule: + - cron: '0 4 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v6 + with: + close-issue-message: > + This issue has been automatically closed due to lack of activity. In an + effort to reduce noise, please do not comment any further. Note that the + core maintainers may elect to reopen this issue at a later date if deemed + necessary. + close-pr-message: > + This PR has been automatically closed due to lack of activity. + days-before-stale: 90 + days-before-close: 30 + exempt-issue-labels: 'status: accepted,status: blocked,status: needs milestone' + operations-per-run: 100 + remove-stale-when-updated: false + stale-issue-label: 'mod / stale' + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. PDA + is governed by a small group of core maintainers which means not all opened + issues may receive direct feedback. **Do not** attempt to circumvent this + process by "bumping" the issue; doing so will result in its immediate closure + and you may be barred from participating in any future discussions. Please see + our [contributing guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/docs/CONTRIBUTING.md). + stale-pr-label: 'mod / stale' + stale-pr-message: > + This PR has been automatically marked as stale because it has not had + recent activity. It will be closed automatically if no further action is + taken. \ No newline at end of file diff --git a/README.md b/README.md index 5d07f93..1cc644c 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,13 @@ You can then access PowerDNS-Admin by pointing your browser to http://localhost: ## Contributing -Please see our [contributing guidelines](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/docs/CONTRIBUTING.md). +Please see our [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/docs/CONTRIBUTING.md). + +## Code of Conduct + +Please see our [Code of Conduct Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/docs/CODE_OF_CONDUCT.md). ## License This project is released under the MIT license. For additional -information, [see here](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/LICENSE) +information, [see the full license](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/LICENSE). diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..54b10d7 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Code of Conduct + +# Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [admin@powerdnsadmin.org](mailto:admin@powerdnsadmin.org). 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. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index df50ba2..8c82a80 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,88 +1,103 @@ -# Contributing +# Contribution Guide -Before submitting new contributions to this repository, it is a good idea to start a discussion with the repository -maintainers on GitHub through the use of issues or discussions. This will help to ensure that your efforts don't get -wasted if the submission is not desirable for the project. +**Looking for help?** PDA has a somewhat active community of fellow users that may be able to provide assistance. Just [start a discussion](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions/new) right here on GitHub! -This is not to say that all contributions that have been discussed will be accepted either. As part of an ongoing -effort to clean up the codebase, some contributions may be rejected if they do not meet the standards of the project -which have not been fully defined yet. This is a work in progress. +
+

+ :bug: Report a bug · + :bulb: Suggest a feature · + :arrow_heading_up: Submit a pull request +

+

+ :rescue_worker_helmet: Become a maintainer · + :heart: Other ideas +

+
+

-Please note we have a code of conduct, please follow it in all your interactions with the project. +Some general tips for engaging here on GitHub: -All pull requests should be based on the `dev` branch of this repository and not the `master` branch! +* Register for a free [GitHub account](https://github.com/signup) if you haven't already. +* You can use [GitHub Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for formatting text and adding images. +* To help mitigate notification spam, please avoid "bumping" issues with no activity. (To vote an issue up or down, use a :thumbsup: or :thumbsdown: reaction.) +* Please avoid pinging members with `@` unless they've previously expressed interest or involvement with that particular issue. -## Code of Conduct +## :bug: Reporting Bugs -### Our Pledge +* First, ensure that you're running the [latest stable version](https://github.com/PowerDNS-Admin/PowerDNS-Admin/releases) of PDA. If you're running an older version, there's a chance that the bug has already been fixed. -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +* Next, search our [issues list](https://github.com/PowerDNS-Admin/PowerDNS-Admin/issues?q=is%3Aissue) to see if the bug you've found has already been reported. If you come across a bug report that seems to match, please click "add a reaction" in the top right corner of the issue and add a thumbs up (:thumbsup:). This will help draw more attention to it. Any comments you can add to provide additional information or context would also be much appreciated. -### Our Standards +* If you can't find any existing issues (open or closed) that seem to match yours, you're welcome to [submit a new bug report](https://github.com/PowerDNS-Admin/PowerDNS-Admin/issues/new?label=type%3A+bug&template=bug_report.yaml). Be sure to complete the entire report template, including detailed steps that someone triaging your issue can follow to confirm the reported behavior. (If we're not able to replicate the bug based on the information provided, we'll ask for additional detail.) -Examples of behavior that contributes to creating a positive environment -include: +* Some other tips to keep in mind: + * Error messages and screenshots are especially helpful. + * Don't prepend your issue title with a label like `[Bug]`; the proper label will be assigned automatically. + * Verify that you have GitHub notifications enabled and are subscribed to your issue after submitting. + * We appreciate your patience as bugs are prioritized by their severity, impact, and difficulty to resolve. -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +## :bulb: Feature Requests -Examples of unacceptable behavior by participants include: +* First, check the GitHub [issues list](https://github.com/PowerDNS-Admin/PowerDNS-Admin/issues?q=is%3Aissue) to see if the feature you have in mind has already been proposed. If you happen to find an open feature request that matches your idea, click "add a reaction" in the top right corner of the issue and add a thumbs up (:thumbsup:). This ensures that the issue has a better chance of receiving attention. Also feel free to add a comment with any additional justification for the feature. -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +* If you have a rough idea that's not quite ready for formal submission yet, start a [GitHub discussion](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions) instead. This is a great way to test the viability and narrow down the scope of a new feature prior to submitting a formal proposal, and can serve to generate interest in your idea from other community members. -### Our Responsibilities +* Once you're ready, submit a feature request [using this template](https://github.com/PowerDNS-Admin/PowerDNS-Admin/issues/new?label=type%3A+feature&template=feature_request.yaml). Be sure to provide sufficient context and detail to convey exactly what you're proposing and why. The stronger your use case, the better chance your proposal has of being accepted. -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +* Some other tips to keep in mind: + * Don't prepend your issue title with a label like `[Feature]`; the proper label will be assigned automatically. + * Try to anticipate any likely questions about your proposal and provide that information proactively. + * Verify that you have GitHub notifications enabled and are subscribed to your issue after submitting. + * You're welcome to volunteer to implement your FR, but don't submit a pull request until it has been approved. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +## :arrow_heading_up: Submitting Pull Requests -### Scope +* [Pull requests](https://docs.github.com/en/pull-requests) (a feature of GitHub) are used to propose changes to NetBox's code base. Our process generally goes like this: + * A user opens a new issue (bug report or feature request) + * A maintainer triages the issue and may mark it as needing an owner + * The issue's author can volunteer to own it, or someone else can + * A maintainer assigns the issue to whomever volunteers + * The issue owner submits a pull request that will resolve the issue + * A maintainer reviews and merges the pull request, closing the issue -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +* It's very important that you not submit a pull request until a relevant issue has been opened **and** assigned to you. Otherwise, you risk wasting time on work that may ultimately not be needed. -### Enforcement +* New pull requests should generally be based off of the `dev` branch, rather than `master`. The `dev` branch is used for ongoing development, while `master` is used for tracking stable releases. -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [admin@powerdnsadmin.org](mailto:admin@powerdnsadmin.org). 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. +* In most cases, it is not necessary to add a changelog entry: A maintainer will take care of this when the PR is merged. (This helps avoid merge conflicts resulting from multiple PRs being submitted simultaneously.) -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. +* All code submissions should meet the following criteria (CI will eventually enforce these checks): + * Python syntax is valid + * PEP 8 compliance is enforced, with the exception that lines may be + greater than 80 characters in length -### Attribution +* Some other tips to keep in mind: + * If you'd like to volunteer for someone else's issue, please post a comment on that issue letting us know. (This will allow the maintainers to assign it to you.) + * All new functionality must include relevant tests where applicable. -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +## :rescue_worker_helmet: Become a Maintainer -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +We're always looking for motivated individuals to join the maintainers team and help drive PDA's long-term development. Some of our most sought-after skills include: + +* Python development with a strong focus on the [Flask](https://flask.palletsprojects.com/) and [Django](https://www.djangoproject.com/) frameworks +* Expertise working with SQLite, MySQL, and/or PostgreSQL databases +* Javascript & TypeScript proficiency +* A knack for web application design (HTML & CSS) +* Familiarity with git and software development best practices +* Excellent attention to detail +* Working experience in the field of network operations as it relates to the use of DNS (Domain Name System) servers. + +We generally ask that maintainers dedicate around four hours of work to the project each week on average, which includes both hands-on development and project management tasks such as issue triage. + +We do maintain an active Mattermost instance for internal communication, but we also use GitHub issues for project management. + +Some maintainers petition their employer to grant some of their paid time to work on PDA. + +Interested? You can contact our lead maintainer, Matt Scott, at admin@powerdnsadmin.org. We'd love to have you on the team! + +## :heart: Other Ways to Contribute + +You don't have to be a developer to contribute to PDA: There are plenty of other ways you can add value to the community! Below are just a few examples: + +* Help answer questions and provide feedback in our [GitHub discussions](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions). +* Write a blog article or record a YouTube video demonstrating how PDA is used at your organization. diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 0000000..bd91d36 --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,31 @@ +# Security Policy + +## No Warranty + +Per the terms of the MIT license, PDA is offered "as is" and without any guarantee or warranty pertaining to its operation. While every reasonable effort is made by its maintainers to ensure the product remains free of security vulnerabilities, users are ultimately responsible for conducting their own evaluations of each software release. + +## Recommendations + +Administrators are encouraged to adhere to industry best practices concerning the secure operation of software, such as: + +* Do not expose your PDA installation to the public Internet +* Do not permit multiple users to share an account +* Enforce minimum password complexity requirements for local accounts +* Prohibit access to your database from clients other than the PDA application +* Keep your deployment updated to the most recent stable release + +## Reporting a Suspected Vulnerability + +If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so via email. Please note that any reported vulnerabilities **MUST** meet all the following conditions: + +* Affects the most recent stable release of PDA, or a current beta release +* Affects a PDA instance installed and configured per the official documentation +* Is reproducible following a prescribed set of instructions + +Please note that we **DO NOT** accept reports generated by automated tooling which merely suggest that a file or file(s) _may_ be vulnerable under certain conditions, as these are most often innocuous. + +If you believe that you've found a vulnerability which meets all of these conditions, please [submit a draft security advisory](https://github.com/PowerDNS-Admin/PowerDNS-Admin/security/advisories/new) on GitHub, or email a brief description of the suspected bug and instructions for reproduction to **admin@powerdnsadmin.org**. + +### Bug Bounties + +As PDA is provided as free open source software, we do not offer any monetary compensation for vulnerability or bug reports, however your contributions are greatly appreciated. \ No newline at end of file From 6681d0f5b034a223bef8408f9b2cec03ccd296d1 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 16:46:34 -0400 Subject: [PATCH 02/44] Relocated new security policy to the project root to meet GitHub feature expectations. --- docs/SECURITY.md => SECURITY.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/SECURITY.md => SECURITY.md (100%) diff --git a/docs/SECURITY.md b/SECURITY.md similarity index 100% rename from docs/SECURITY.md rename to SECURITY.md From 31c8577409f191189e9550a8fc79d3890ee07e70 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 16:48:11 -0400 Subject: [PATCH 03/44] Updated project README to include reference to new security policy. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1cc644c..ea2c1a1 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,10 @@ You can then access PowerDNS-Admin by pointing your browser to http://localhost: ![dashboard](docs/screenshots/dashboard.png) +## Security Issues / Reports + +Please see our [Security Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/SECURITY.md). + ## Contributing Please see our [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/docs/CONTRIBUTING.md). From a2e5c7d5bced8f3466a4a1f00a8d538194435b69 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 17:29:05 -0400 Subject: [PATCH 04/44] Corrected an input type mistake in the bug report and feature request templates. Corrected URL mistake in the issue template config.yml file. Updated project README policy reference URLs to use master branch. --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- README.md | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 5bacd4e..640cff8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -10,7 +10,7 @@ body: installation. If you're having trouble with installation or just looking for assistance with using PDA, please visit our [discussion forum](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions) instead. - - type: input + - type: dropdown attributes: label: PDA version description: What version of PDA are you currently running? diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 98109af..1ecb2e9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,7 @@ blank_issues_enabled: false contact_links: - name: 📖 Contributing Policy - url: https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/docs/CONTRIBUTING.md + url: https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md about: "Please read through our contributing policy before opening an issue or pull request" - name: ❓ Discussion url: https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index b2e1934..fa1db00 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -10,7 +10,7 @@ body: PDA in some way. If you're trying to solve a problem but can't figure out how, or if you still need time to work on the details of a proposed new feature, please start a [discussion](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions) instead. - - type: input + - type: dropdown attributes: label: PDA version description: What version of PDA are you currently running? diff --git a/README.md b/README.md index ea2c1a1..c16bbca 100644 --- a/README.md +++ b/README.md @@ -74,15 +74,15 @@ You can then access PowerDNS-Admin by pointing your browser to http://localhost: ## Security Issues / Reports -Please see our [Security Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/SECURITY.md). +Please see our [Security Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/SECURITY.md). ## Contributing -Please see our [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/docs/CONTRIBUTING.md). +Please see our [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md). ## Code of Conduct -Please see our [Code of Conduct Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/dev/docs/CODE_OF_CONDUCT.md). +Please see our [Code of Conduct Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CODE_OF_CONDUCT.md). ## License From 1358e47b5be6da1380a9dfb8440c0e4f9f31a79a Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 17:48:07 -0400 Subject: [PATCH 05/44] Corrected project name reference mistake in contribution guide. --- docs/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 8c82a80..8acf50c 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -52,7 +52,7 @@ Some general tips for engaging here on GitHub: ## :arrow_heading_up: Submitting Pull Requests -* [Pull requests](https://docs.github.com/en/pull-requests) (a feature of GitHub) are used to propose changes to NetBox's code base. Our process generally goes like this: +* [Pull requests](https://docs.github.com/en/pull-requests) (a feature of GitHub) are used to propose changes to PDA's code base. Our process generally goes like this: * A user opens a new issue (bug report or feature request) * A maintainer triages the issue and may mark it as needing an owner * The issue's author can volunteer to own it, or someone else can From 1bfb5429a1553a8eef45a529fcd4694934ae510e Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 17:56:59 -0400 Subject: [PATCH 06/44] Updated stale issue / PR workflow to include proper exceptions. --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 66fd367..d0a3e0d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -26,7 +26,7 @@ jobs: This PR has been automatically closed due to lack of activity. days-before-stale: 90 days-before-close: 30 - exempt-issue-labels: 'status: accepted,status: blocked,status: needs milestone' + exempt-issue-labels: 'mod / announcement, mod / accepted, mod / reviewing, mod / testing' operations-per-run: 100 remove-stale-when-updated: false stale-issue-label: 'mod / stale' From 98e6b8946f08a41bd224265fbdee46834ccefafc Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 18:03:18 -0400 Subject: [PATCH 07/44] Updated labels for the issue templates. --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/documentation_change.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- .github/ISSUE_TEMPLATE/housekeeping.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 640cff8..0c5a2d7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,7 +1,7 @@ --- name: 🐛 Bug Report description: Report a reproducible bug in the current release of PDA -labels: ["type: bug"] +labels: ["bug / broken-feature"] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/documentation_change.yaml b/.github/ISSUE_TEMPLATE/documentation_change.yaml index 584d4b4..0b34991 100644 --- a/.github/ISSUE_TEMPLATE/documentation_change.yaml +++ b/.github/ISSUE_TEMPLATE/documentation_change.yaml @@ -1,7 +1,7 @@ --- name: 📖 Documentation Change description: Suggest an addition or modification to the PDA documentation -labels: ["type: documentation"] +labels: ["docs / request"] body: - type: dropdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index fa1db00..e649c61 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -1,7 +1,7 @@ --- name: ✨ Feature Request description: Propose a new PDA feature or enhancement -labels: ["type: feature"] +labels: ["feature / request"] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/housekeeping.yaml b/.github/ISSUE_TEMPLATE/housekeeping.yaml index dba7e3c..2d8e5df 100644 --- a/.github/ISSUE_TEMPLATE/housekeeping.yaml +++ b/.github/ISSUE_TEMPLATE/housekeeping.yaml @@ -1,7 +1,7 @@ --- name: 🏡 Housekeeping description: A change pertaining to the codebase itself (developers only) -labels: ["type: housekeeping"] +labels: ["mod / change-request"] body: - type: markdown attributes: From 92f5071a84135e2058fc6dfc1cd3401cc66b16c5 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 18:16:06 -0400 Subject: [PATCH 08/44] Corrected URL mistake in stale issue / PR workflow. --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d0a3e0d..2c6284c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -37,7 +37,7 @@ jobs: issues may receive direct feedback. **Do not** attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see - our [contributing guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/docs/CONTRIBUTING.md). + our [contributing guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md). stale-pr-label: 'mod / stale' stale-pr-message: > This PR has been automatically marked as stale because it has not had From 1aac3c0f0d2fdca15e03fcbe9cd5870c123bd31a Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 17 Mar 2023 18:25:05 -0400 Subject: [PATCH 09/44] Updated the stale issue / PR workflow to include better verbiage for the contribution guide. Also updated the stale issue / PR workflow to exclude security vulnerabilities. --- .github/workflows/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 2c6284c..666cab7 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -26,7 +26,7 @@ jobs: This PR has been automatically closed due to lack of activity. days-before-stale: 90 days-before-close: 30 - exempt-issue-labels: 'mod / announcement, mod / accepted, mod / reviewing, mod / testing' + exempt-issue-labels: 'bug / security-vulnerability, mod / announcement, mod / accepted, mod / reviewing, mod / testing' operations-per-run: 100 remove-stale-when-updated: false stale-issue-label: 'mod / stale' @@ -37,7 +37,7 @@ jobs: issues may receive direct feedback. **Do not** attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see - our [contributing guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md). + our [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md). stale-pr-label: 'mod / stale' stale-pr-message: > This PR has been automatically marked as stale because it has not had From 2606ad0395a6c08ce9761c6ca4769bb39a53712b Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 18 Mar 2023 08:48:07 -0400 Subject: [PATCH 10/44] Updated various yaml files to include proper opening lines. Tweaked the name of the stale threads workflow. --- .github/ISSUE_TEMPLATE/config.yml | 1 + .github/dependabot.yml | 1 + .github/labels.yml | 4 ++++ .github/workflows/build-and-publish.yml | 2 +- .github/workflows/codeql-analysis.yml | 1 + .github/workflows/lock.yml | 1 + .github/workflows/stale.yml | 2 +- 7 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1ecb2e9..6aba80c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,3 +1,4 @@ +--- # Reference: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser blank_issues_enabled: false contact_links: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 68dd932..898c594 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,4 @@ +--- version: 2 updates: - package-ecosystem: npm diff --git a/.github/labels.yml b/.github/labels.yml index c113abb..e17cd97 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -1,3 +1,4 @@ +--- labels: - name: bug / broken-feature description: Existing feature malfunctioning or broken @@ -38,6 +39,9 @@ labels: - name: mod / announcement description: This is an admin announcement color: 'e5ef23' + - name: mod / change-request + description: Used by internal developers to indicate a change-request. + color: 'e5ef23' - name: mod / changes-requested description: Changes have been requested before proceeding color: 'e5ef23' diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index f76ed98..74085f0 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -1,3 +1,4 @@ +--- name: 'Docker Image' on: @@ -42,7 +43,6 @@ jobs: - name: Docker Image Build uses: docker/build-push-action@v2 - #if: github.ref == 'refs/heads/master' with: context: ./ file: ./docker/Dockerfile diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7f2f148..b54abf1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,3 +1,4 @@ +--- # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 9385024..2005b45 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -1,3 +1,4 @@ +--- # lock-threads (https://github.com/marketplace/actions/lock-threads) name: 'Lock threads' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 666cab7..b14dc66 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,5 +1,5 @@ # close-stale-issues (https://github.com/marketplace/actions/close-stale-issues) -name: 'Close stale issues/PRs' +name: 'Close Stale Threads' on: schedule: From ca4bf18f6734326c5091f145760d4f37245bfcc3 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 18 Mar 2023 19:20:36 -0400 Subject: [PATCH 11/44] Updated invalid value in dependabot workflow. --- .github/dependabot.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 898c594..29095aa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,7 +8,8 @@ updates: ignore: - dependency-name: "*" update-types: [ "version-update:semver-major" ] - labels: feature / dependency + labels: + - 'feature / dependency' - package-ecosystem: pip directory: / schedule: @@ -16,4 +17,5 @@ updates: ignore: - dependency-name: "*" update-types: [ "version-update:semver-major" ] - labels: feature / dependency + labels: + - 'feature / dependency' From 0a66089cad7f54a5bdf650dcef4f82cfa2b36e1e Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 18 Mar 2023 20:49:01 -0400 Subject: [PATCH 12/44] Updated dependabot configuration to target the dev branch. --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 29095aa..482207e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,6 +2,7 @@ version: 2 updates: - package-ecosystem: npm + target-branch: dev directory: / schedule: interval: daily @@ -11,6 +12,7 @@ updates: labels: - 'feature / dependency' - package-ecosystem: pip + target-branch: dev directory: / schedule: interval: daily From 80b191bc0d2cfac1dbbfe1a9fd0c31e275569200 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 18 Mar 2023 20:55:20 -0400 Subject: [PATCH 13/44] Updated project README to include donation section. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c16bbca..94d0133 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,9 @@ Please see our [Code of Conduct Policy](https://github.com/PowerDNS-Admin/PowerD This project is released under the MIT license. For additional information, [see the full license](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/LICENSE). + +## Donate + +Like my work? + +Buy Me A Coffee \ No newline at end of file From 4e54b5ae0a0e60470c8ed940d3645767dc6b554c Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 18 Mar 2023 21:45:28 -0400 Subject: [PATCH 14/44] Added GitHub sponsors configuration. --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..18e85f0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [AzorianSolutions] \ No newline at end of file From 976f52ce7afacb3de39c440dca44cd5348ea34a7 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sun, 19 Mar 2023 12:36:44 -0400 Subject: [PATCH 15/44] Corrected minor formatting issue with project's Code of Conduct policy. --- docs/CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index 54b10d7..ed3cb47 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -1,6 +1,6 @@ # Code of Conduct -# Our Pledge +## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and From 236487eada0122c25929a9dff1e736e863376472 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sun, 19 Mar 2023 12:39:44 -0400 Subject: [PATCH 16/44] Updated Security section header of the project README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94d0133..b9f9da5 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ You can then access PowerDNS-Admin by pointing your browser to http://localhost: ![dashboard](docs/screenshots/dashboard.png) -## Security Issues / Reports +## Security Policy Please see our [Security Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/SECURITY.md). From 55faefeedc3a0fa7818a2ad766f40601a8b92ce4 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sun, 19 Mar 2023 15:09:52 -0400 Subject: [PATCH 17/44] Updated stale thread workflow with updated message verbiage. Updated lock thread workflow to properly exclude threads with specific labels. --- .github/workflows/lock.yml | 4 +++- .github/workflows/stale.yml | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 2005b45..cf6f0b3 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -19,4 +19,6 @@ jobs: with: issue-inactive-days: 90 pr-inactive-days: 30 - issue-lock-reason: 'resolved' \ No newline at end of file + issue-lock-reason: 'resolved' + exclude-any-issue-labels: 'bug / security-vulnerability, mod / announcement, mod / accepted, mod / reviewing, mod / testing' + exclude-any-pr-labels: 'bug / security-vulnerability, mod / announcement, mod / accepted, mod / reviewing, mod / testing' \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index b14dc66..9b565ec 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -36,10 +36,11 @@ jobs: is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. **Do not** attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure - and you may be barred from participating in any future discussions. Please see - our [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md). + and you may be barred from participating in any future discussions. Please see our + [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md). stale-pr-label: 'mod / stale' stale-pr-message: > This PR has been automatically marked as stale because it has not had recent activity. It will be closed automatically if no further action is - taken. \ No newline at end of file + taken. Please see our + [Contribution Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md). \ No newline at end of file From a187d70470daffda44ff34c0f7534c8e9a92dcab Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sun, 19 Mar 2023 17:02:45 -0400 Subject: [PATCH 18/44] Updated CodeQL workflow to exclude non-relevant project paths. --- .github/workflows/codeql-analysis.yml | 64 ++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b54abf1..9f4b66f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -15,10 +15,70 @@ name: "CodeQL" on: workflow_dispatch: push: - branches: [ dev, master ] + branches: + - 'dev' + - 'main' + - 'master' + - 'dependabot/**' + - 'feature/**' + - 'issue/**' + paths-ignore: + - .github/** + - deploy/** + - docker/** + - docker-test/** + - docs/** + - powerdnsadmin/static/assets/** + - powerdnsadmin/static/custom/css/** + - powerdnsadmin/static/img/** + - powerdnsadmin/swagger-spec.yaml + - .dockerignore + - .gitattributes + - .gitignore + - .lgtm.yml + - .whitesource + - .yarnrc + - docker-compose.yml + - docker-compose-test.yml + - LICENSE + - package.json + - README.md + - requirements.txt + - SECURITY.md + - yarn.lock pull_request: # The branches below must be a subset of the branches above - branches: [ dev, master ] + branches: + - 'dev' + - 'main' + - 'master' + - 'dependabot/**' + - 'feature/**' + - 'issue/**' + paths-ignore: + - .github/** + - deploy/** + - docker/** + - docker-test/** + - docs/** + - powerdnsadmin/static/assets/** + - powerdnsadmin/static/custom/css/** + - powerdnsadmin/static/img/** + - powerdnsadmin/swagger-spec.yaml + - .dockerignore + - .gitattributes + - .gitignore + - .lgtm.yml + - .whitesource + - .yarnrc + - docker-compose.yml + - docker-compose-test.yml + - LICENSE + - package.json + - README.md + - requirements.txt + - SECURITY.md + - yarn.lock schedule: - cron: '45 2 * * 2' From 1762a5481b8ad8ae05a65a9a21608bfebe6e569b Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sun, 19 Mar 2023 17:05:30 -0400 Subject: [PATCH 19/44] Updated build-and-publish workflow to exclude non-relevant project paths. --- .github/workflows/build-and-publish.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 74085f0..9eb0fff 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -9,6 +9,22 @@ on: - 'master' tags: - 'v*.*.*' + paths-ignore: + - .github/** + - deploy/** + - docker-test/** + - docs/** + - .dockerignore + - .gitattributes + - .gitignore + - .lgtm.yml + - .whitesource + - .yarnrc + - docker-compose.yml + - docker-compose-test.yml + - LICENSE + - README.md + - SECURITY.md jobs: build-and-push-docker-image: From 92033aa109afb30e5be00a64805ed29e90a37bfa Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Tue, 21 Mar 2023 19:09:48 -0400 Subject: [PATCH 20/44] Updated project README to include organization sponsorship reference. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b9f9da5..cd0c3dd 100644 --- a/README.md +++ b/README.md @@ -89,8 +89,10 @@ Please see our [Code of Conduct Policy](https://github.com/PowerDNS-Admin/PowerD This project is released under the MIT license. For additional information, [see the full license](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/LICENSE). -## Donate +## [Donate](https://www.buymeacoffee.com/AzorianMatt) Like my work? -Buy Me A Coffee \ No newline at end of file +Buy Me A Coffee + +**Want to sponsor me?** Please visit my organization's [sponsorship page](https://github.com/sponsors/AzorianSolutions). From b86282b44271dbacf631df7631e69f09fd0e46c9 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Fri, 24 Mar 2023 19:42:35 -0400 Subject: [PATCH 21/44] Added references to the project's discord server. --- .github/SUPPORT.md | 15 +++++++++++++++ README.md | 6 ++++++ docs/CONTRIBUTING.md | 4 +++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .github/SUPPORT.md diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..e0df5a6 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,15 @@ +# PowerDNS Admin + +## Project Support + +**Looking for help?** PDA has a somewhat active community of fellow users that may be able to provide assistance. +Just [start a discussion](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions/new) right here on GitHub! + +Looking to chat with someone? Join our [Discord Server](https://discord.powerdnsadmin.org). + +Some general tips for engaging here on GitHub: + +* Register for a free [GitHub account](https://github.com/signup) if you haven't already. +* You can use [GitHub Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for formatting text and adding images. +* To help mitigate notification spam, please avoid "bumping" issues with no activity. (To vote an issue up or down, use a :thumbsup: or :thumbsdown: reaction.) +* Please avoid pinging members with `@` unless they've previously expressed interest or involvement with that particular issue. diff --git a/README.md b/README.md index cd0c3dd..6070d5f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,12 @@ You can then access PowerDNS-Admin by pointing your browser to http://localhost: ![dashboard](docs/screenshots/dashboard.png) +## Support + +**Looking for help?** Try taking a look at the project's +[Support Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/.github/SUPPORT.md) or joining +our [Discord Server](https://discord.powerdnsadmin.org). + ## Security Policy Please see our [Security Policy](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/SECURITY.md). diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 8acf50c..d4fb25f 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,6 +1,8 @@ # Contribution Guide -**Looking for help?** PDA has a somewhat active community of fellow users that may be able to provide assistance. Just [start a discussion](https://github.com/PowerDNS-Admin/PowerDNS-Admin/discussions/new) right here on GitHub! +**Looking for help?** Try taking a look at the project's +[Support Guide](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/.github/SUPPORT.md) or joining +our [Discord Server](https://discord.powerdnsadmin.org).

From 0d0339a3166409d6f84c677e4453f80332a137b2 Mon Sep 17 00:00:00 2001 From: Jan Koppe Date: Wed, 29 Mar 2023 14:52:00 +0200 Subject: [PATCH 22/44] fix #1485: allow more than 100 rows default in dashboard The dashboard.domains_custom route was hardcoded to either return all the domains, or at most 100, regardless of default_domain_table_size setting. Make this limit be dependent on default_domain_table_size instead. The API will now limit to 100 or default_domain_table_size, whichever one is higher. This is done to not break any seconday use-cases that might depend on the hardcoded setting. --- powerdnsadmin/routes/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerdnsadmin/routes/dashboard.py b/powerdnsadmin/routes/dashboard.py index 14a8ae3..e517207 100644 --- a/powerdnsadmin/routes/dashboard.py +++ b/powerdnsadmin/routes/dashboard.py @@ -141,7 +141,7 @@ def domains_custom(tab_id): filtered_count = domains.count() start = int(request.args.get("start", 0)) - length = min(int(request.args.get("length", 0)), 100) + length = min(int(request.args.get("length", 0)), max(100, int(Setting().get('default_domain_table_size')))) if length != -1: domains = domains[start:start + length] From bae746cffe2bbb8333d69c2cd5e7099bfea39e8e Mon Sep 17 00:00:00 2001 From: Stefan Ubbink Date: Sun, 2 Apr 2023 15:47:34 +0200 Subject: [PATCH 23/44] Show the current zone type and soa-edit-api settings on the zone settings page --- powerdnsadmin/routes/domain.py | 6 +++++- powerdnsadmin/templates/domain_setting.html | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/powerdnsadmin/routes/domain.py b/powerdnsadmin/routes/domain.py index bcf91cc..bee1250 100644 --- a/powerdnsadmin/routes/domain.py +++ b/powerdnsadmin/routes/domain.py @@ -560,13 +560,17 @@ def setting(domain_name): d = Domain(name=domain_name) domain_user_ids = d.get_user() account = d.get_account() + domain_info = d.get_domain_info(domain_name) return render_template('domain_setting.html', domain=domain, users=users, domain_user_ids=domain_user_ids, accounts=accounts, - domain_account=account) + domain_account=account, + zone_type=domain_info["kind"].lower(), + masters=','.join(domain_info["masters"]), + soa_edit_api=domain_info["soa_edit_api"].upper()) if request.method == 'POST': # username in right column diff --git a/powerdnsadmin/templates/domain_setting.html b/powerdnsadmin/templates/domain_setting.html index f59f4a9..ba11354 100644 --- a/powerdnsadmin/templates/domain_setting.html +++ b/powerdnsadmin/templates/domain_setting.html @@ -218,15 +218,16 @@

-
+ Secret
+
+ + + +
+
+ + + +
@@ -785,16 +804,6 @@ value="{{ SETTING.get('google_token_url') }}">
-
- - - -
@@ -806,15 +815,6 @@ value="{{ SETTING.get('google_authorize_url') }}">
-
- - - -

@@ -870,26 +870,26 @@ -
- +
+ Secret
@@ -1311,21 +1311,21 @@ OAuth
- +
- +
From 9168dd99e074709e2d1cc7f6dd357f25c4aa9e8b Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 8 Apr 2023 18:11:55 -0400 Subject: [PATCH 27/44] Updated the OAuth login handlers to utilize uniform user naming variables. Updated the GitHub login process to split the user's full name based on spaces so that first and last name are filled in on PDA profile. --- powerdnsadmin/routes/index.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index 4351a54..706d0b9 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -189,17 +189,25 @@ def login(): if 'github_token' in session: me = json.loads(github.get('user').text) github_username = me['login'] - github_name = me['name'] + github_first_name = me['name'] + github_last_name = '' github_email = me['email'] + # If the user's full name from GitHub contains at least two words, use the first word as the first name and + # the rest as the last name. + github_name_parts = github_first_name.split(' ') + if len(github_name_parts) > 1: + github_first_name = github_name_parts[0] + github_last_name = ' '.join(github_name_parts[1:]) + user = User.query.filter_by(username=github_username).first() if user is None: user = User.query.filter_by(email=github_email).first() if not user: user = User(username=github_username, plain_text_password=None, - firstname=github_name, - lastname='', + firstname=github_first_name, + lastname=github_last_name, email=github_email) result = user.create_local_user() @@ -227,8 +235,8 @@ def login(): mygroups = [] azure_username = me["userPrincipalName"] - azure_givenname = me["givenName"] - azure_familyname = me["surname"] + azure_first_name = me["givenName"] + azure_last_name = me["surname"] if "mail" in me: azure_email = me["mail"] else: @@ -244,8 +252,8 @@ def login(): if not user: user = User(username=azure_username, plain_text_password=None, - firstname=azure_givenname, - lastname=azure_familyname, + firstname=azure_first_name, + lastname=azure_last_name, email=azure_email) result = user.create_local_user() @@ -386,21 +394,21 @@ def login(): if 'oidc_token' in session: me = json.loads(oidc.get('userinfo').text) oidc_username = me[Setting().get('oidc_oauth_username')] - oidc_givenname = me[Setting().get('oidc_oauth_firstname')] - oidc_familyname = me[Setting().get('oidc_oauth_last_name')] + oidc_first_name = me[Setting().get('oidc_oauth_firstname')] + oidc_last_name = me[Setting().get('oidc_oauth_last_name')] oidc_email = me[Setting().get('oidc_oauth_email')] user = User.query.filter_by(username=oidc_username).first() if not user: user = User(username=oidc_username, plain_text_password=None, - firstname=oidc_givenname, - lastname=oidc_familyname, + firstname=oidc_first_name, + lastname=oidc_last_name, email=oidc_email) result = user.create_local_user() else: - user.firstname = oidc_givenname - user.lastname = oidc_familyname + user.firstname = oidc_first_name + user.lastname = oidc_last_name user.email = oidc_email user.plain_text_password = None result = user.update_local_user() From ece96262124985143467534394c64a35b7b35cda Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 8 Apr 2023 18:14:40 -0400 Subject: [PATCH 28/44] Updated the OAuth login handlers to utilize uniform user naming variables. Updated the GitHub login process to split the user's full name based on spaces so that first and last name are filled in on PDA profile. --- powerdnsadmin/routes/index.py | 59 ++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index 706d0b9..2636cf1 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -164,18 +164,18 @@ def login(): if 'google_token' in session: user_data = json.loads(google.get('userinfo').text) - first_name = user_data['given_name'] - surname = user_data['family_name'] - email = user_data['email'] - user = User.query.filter_by(username=email).first() + google_first_name = user_data['given_name'] + google_last_name = user_data['family_name'] + google_email = user_data['email'] + user = User.query.filter_by(username=google_email).first() if user is None: - user = User.query.filter_by(email=email).first() + user = User.query.filter_by(email=google_email).first() if not user: - user = User(username=email, - firstname=first_name, - lastname=surname, + user = User(username=google_email, + firstname=google_first_name, + lastname=google_last_name, plain_text_password=None, - email=email) + email=google_email) result = user.create_local_user() if not result['status']: @@ -187,11 +187,11 @@ def login(): return authenticate_user(user, 'Google OAuth') if 'github_token' in session: - me = json.loads(github.get('user').text) - github_username = me['login'] - github_first_name = me['name'] + user_data = json.loads(github.get('user').text) + github_username = user_data['login'] + github_first_name = user_data['name'] github_last_name = '' - github_email = me['email'] + github_email = user_data['email'] # If the user's full name from GitHub contains at least two words, use the first word as the first name and # the rest as the last name. @@ -222,7 +222,7 @@ def login(): if 'azure_token' in session: azure_info = azure.get('me?$select=displayName,givenName,id,mail,surname,userPrincipalName').text current_app.logger.info('Azure login returned: ' + azure_info) - me = json.loads(azure_info) + user_data = json.loads(azure_info) azure_info = azure.post('me/getMemberGroups', json={'securityEnabledOnly': False}).text @@ -234,15 +234,15 @@ def login(): else: mygroups = [] - azure_username = me["userPrincipalName"] - azure_first_name = me["givenName"] - azure_last_name = me["surname"] - if "mail" in me: - azure_email = me["mail"] + azure_username = user_data["userPrincipalName"] + azure_first_name = user_data["givenName"] + azure_last_name = user_data["surname"] + if "mail" in user_data: + azure_email = user_data["mail"] else: azure_email = "" if not azure_email: - azure_email = me["userPrincipalName"] + azure_email = user_data["userPrincipalName"] # Handle foreign principals such as guest users azure_email = re.sub(r"#.*$", "", azure_email) @@ -392,11 +392,11 @@ def login(): return authenticate_user(user, 'Azure OAuth') if 'oidc_token' in session: - me = json.loads(oidc.get('userinfo').text) - oidc_username = me[Setting().get('oidc_oauth_username')] - oidc_first_name = me[Setting().get('oidc_oauth_firstname')] - oidc_last_name = me[Setting().get('oidc_oauth_last_name')] - oidc_email = me[Setting().get('oidc_oauth_email')] + user_data = json.loads(oidc.get('userinfo').text) + oidc_username = user_data[Setting().get('oidc_oauth_username')] + oidc_first_name = user_data[Setting().get('oidc_oauth_firstname')] + oidc_last_name = user_data[Setting().get('oidc_oauth_last_name')] + oidc_email = user_data[Setting().get('oidc_oauth_email')] user = User.query.filter_by(username=oidc_username).first() if not user: @@ -426,10 +426,11 @@ def login(): desc_prop = Setting().get('oidc_oauth_account_description_property') account_to_add = [] - # If the name_property and desc_property exist in me (A variable that contains all the userinfo from the IdP). - if name_prop in me and desc_prop in me: - accounts_name_prop = [me[name_prop]] if type(me[name_prop]) is not list else me[name_prop] - accounts_desc_prop = [me[desc_prop]] if type(me[desc_prop]) is not list else me[desc_prop] + # If the name_property and desc_property exist in me (A variable that contains all the userinfo from the + # IdP). + if name_prop in user_data and desc_prop in user_data: + accounts_name_prop = [user_data[name_prop]] if type(user_data[name_prop]) is not list else user_data[name_prop] + accounts_desc_prop = [user_data[desc_prop]] if type(user_data[desc_prop]) is not list else user_data[desc_prop] # Run on all groups the user is in by the index num. for i in range(len(accounts_name_prop)): From 737e104912af07d07bdf2daf7242fae94adcba45 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sun, 9 Apr 2023 10:11:00 -0400 Subject: [PATCH 29/44] Added KnockoutJS NPM package. Re-formatted and re-organized settings model. Working on Knockout model integration into existing authentication settings editor view. --- package.json | 1 + powerdnsadmin/assets.py | 2 + powerdnsadmin/models/setting.py | 95 +++-- powerdnsadmin/routes/admin.py | 50 ++- .../js/app-authentication-settings-editor.js | 273 +++++++++++++ .../admin_setting_authentication.html | 381 ++++++++---------- yarn.lock | 7 +- 7 files changed, 541 insertions(+), 268 deletions(-) create mode 100644 powerdnsadmin/static/custom/js/app-authentication-settings-editor.js diff --git a/package.json b/package.json index 42f866d..3aaac1d 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "jquery-ui-dist": "^1.13.2", "jquery.quicksearch": "^2.4.0", "jtimeout": "^3.2.0", + "knockout": "^3.5.1", "multiselect": "^0.9.12" }, "resolutions": { diff --git a/powerdnsadmin/assets.py b/powerdnsadmin/assets.py index 5e17d7f..8f9192f 100644 --- a/powerdnsadmin/assets.py +++ b/powerdnsadmin/assets.py @@ -20,6 +20,7 @@ js_login = Bundle( 'node_modules/jquery/dist/jquery.js', 'node_modules/bootstrap/dist/js/bootstrap.js', 'node_modules/icheck/icheck.js', + 'node_modules/knockout/build/output/knockout-latest.js', 'custom/js/custom.js', filters=(ConcatFilter, 'rjsmin'), output='generated/login.js') @@ -55,6 +56,7 @@ js_main = Bundle( 'node_modules/datatables.net-plugins/sorting/natural.js', 'node_modules/jtimeout/src/jTimeout.js', 'node_modules/jquery.quicksearch/src/jquery.quicksearch.js', + 'node_modules/knockout/build/output/knockout-latest.js', 'custom/js/custom.js', 'node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js', filters=(ConcatFilter, 'rjsmin'), diff --git a/powerdnsadmin/models/setting.py b/powerdnsadmin/models/setting.py index dedaaab..1ef3166 100644 --- a/powerdnsadmin/models/setting.py +++ b/powerdnsadmin/models/setting.py @@ -15,6 +15,7 @@ class Setting(db.Model): value = db.Column(db.Text()) defaults = { + # General Settings 'maintenance': False, 'fullscreen_layout': True, 'record_helper': True, @@ -42,56 +43,79 @@ class Setting(db.Model): 'pdns_api_timeout': 30, 'pdns_version': '4.1.1', 'verify_ssl_connections': True, + 'verify_user_email': False, + 'enforce_api_ttl': False, + 'ttl_options': '1 minute,5 minutes,30 minutes,60 minutes,24 hours', + 'otp_field_enabled': True, + 'custom_css': '', + 'otp_force': False, + 'max_history_records': 1000, + 'deny_domain_override': False, + 'account_name_extra_chars': False, + 'gravatar_enabled': False, + + # Local Authentication Settings 'local_db_enabled': True, 'signup_enabled': True, - 'autoprovisioning': False, - 'urn_value': '', - 'autoprovisioning_attribute': '', - 'purge': False, - 'verify_user_email': False, + 'pwd_enforce_characters': False, + 'pwd_min_len': 10, + 'pwd_min_lowercase': 3, + 'pwd_min_uppercase': 2, + 'pwd_min_digits': 2, + 'pwd_min_special': 1, + 'pwd_enforce_complexity': False, + 'pwd_min_complexity': 11, + + # LDAP Authentication Settings 'ldap_enabled': False, 'ldap_type': 'ldap', 'ldap_uri': '', 'ldap_base_dn': '', 'ldap_admin_username': '', 'ldap_admin_password': '', + 'ldap_domain': '', 'ldap_filter_basic': '', - 'ldap_filter_group': '', 'ldap_filter_username': '', + 'ldap_filter_group': '', 'ldap_filter_groupname': '', 'ldap_sg_enabled': False, 'ldap_admin_group': '', 'ldap_operator_group': '', 'ldap_user_group': '', - 'ldap_domain': '', + 'autoprovisioning': False, + 'autoprovisioning_attribute': '', + 'urn_value': '', + 'purge': False, + + # Google OAuth2 Settings + 'google_oauth_enabled': False, + 'google_oauth_client_id': '', + 'google_oauth_client_secret': '', + 'google_oauth_scope': 'openid email profile', + 'google_base_url': 'https://www.googleapis.com/oauth2/v3/', + 'google_oauth_metadata_url': 'https://accounts.google.com/.well-known/openid-configuration', + 'google_token_url': 'https://oauth2.googleapis.com/token', + 'google_authorize_url': 'https://accounts.google.com/o/oauth2/v2/auth', + + # GitHub OAuth2 Settings 'github_oauth_enabled': False, 'github_oauth_key': '', 'github_oauth_secret': '', 'github_oauth_scope': 'email', 'github_oauth_api_url': 'https://api.github.com/user', - 'github_oauth_token_url': - 'https://github.com/login/oauth/access_token', - 'github_oauth_authorize_url': - 'https://github.com/login/oauth/authorize', 'github_oauth_metadata_url': '', - 'google_oauth_enabled': False, - 'google_oauth_client_id': '', - 'google_oauth_client_secret': '', - 'google_token_url': 'https://oauth2.googleapis.com/token', - 'google_oauth_scope': 'openid email profile', - 'google_authorize_url': 'https://accounts.google.com/o/oauth2/v2/auth', - 'google_oauth_metadata_url': '', - 'google_base_url': 'https://www.googleapis.com/oauth2/v3/', + 'github_oauth_token_url': 'https://github.com/login/oauth/access_token', + 'github_oauth_authorize_url': 'https://github.com/login/oauth/authorize', + + # Azure OAuth2 Settings 'azure_oauth_enabled': False, 'azure_oauth_key': '', 'azure_oauth_secret': '', 'azure_oauth_scope': 'User.Read openid email profile', 'azure_oauth_api_url': 'https://graph.microsoft.com/v1.0/', - 'azure_oauth_token_url': - 'https://login.microsoftonline.com/[tenancy]/oauth2/v2.0/token', - 'azure_oauth_authorize_url': - 'https://login.microsoftonline.com/[tenancy]/oauth2/v2.0/authorize', 'azure_oauth_metadata_url': '', + 'azure_oauth_token_url': '', + 'azure_oauth_authorize_url': '', 'azure_sg_enabled': False, 'azure_admin_group': '', 'azure_operator_group': '', @@ -101,22 +125,25 @@ class Setting(db.Model): 'azure_group_accounts_name_re': '', 'azure_group_accounts_description': 'description', 'azure_group_accounts_description_re': '', + + # OIDC OAuth2 Settings 'oidc_oauth_enabled': False, 'oidc_oauth_key': '', 'oidc_oauth_secret': '', 'oidc_oauth_scope': 'email', 'oidc_oauth_api_url': '', + 'oidc_oauth_metadata_url': '', 'oidc_oauth_token_url': '', 'oidc_oauth_authorize_url': '', - 'oidc_oauth_metadata_url': '', 'oidc_oauth_logout_url': '', 'oidc_oauth_username': 'preferred_username', + 'oidc_oauth_email': 'email', 'oidc_oauth_firstname': 'given_name', 'oidc_oauth_last_name': 'family_name', - 'oidc_oauth_email': 'email', 'oidc_oauth_account_name_property': '', 'oidc_oauth_account_description_property': '', - 'enforce_api_ttl': False, + + # Zone Record Settings 'forward_records_allow_edit': { 'A': True, 'AAAA': True, @@ -193,22 +220,6 @@ class Setting(db.Model): 'TXT': True, 'URI': False }, - 'ttl_options': '1 minute,5 minutes,30 minutes,60 minutes,24 hours', - 'otp_field_enabled': True, - 'custom_css': '', - 'otp_force': False, - 'max_history_records': 1000, - 'deny_domain_override': False, - 'account_name_extra_chars': False, - 'gravatar_enabled': False, - 'pwd_enforce_characters': False, - 'pwd_min_len': 10, - 'pwd_min_lowercase': 3, - 'pwd_min_uppercase': 2, - 'pwd_min_digits': 2, - 'pwd_min_special': 1, - 'pwd_enforce_complexity': False, - 'pwd_min_complexity': 11 } def __init__(self, id=None, name=None, value=None): diff --git a/powerdnsadmin/routes/admin.py b/powerdnsadmin/routes/admin.py index eedabdc..7ec669d 100644 --- a/powerdnsadmin/routes/admin.py +++ b/powerdnsadmin/routes/admin.py @@ -72,8 +72,8 @@ def get_record_changes(del_rrset, add_rrset): """For the given record, return the state dict.""" return { "disabled": record['disabled'], - "content": record['content'], - "comment": record.get('comment', ''), + "content": record['content'], + "comment": record.get('comment', ''), } add_records = get_records(add_rrset) @@ -149,8 +149,8 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n # Sort them by the record name if change_num in out_changes: out_changes[change_num].sort(key=lambda change: - change.del_rrset['name'] if change.del_rrset else change.add_rrset['name'] - ) + change.del_rrset['name'] if change.del_rrset else change.add_rrset['name'] + ) # only used for changelog per record if record_name != None and record_type != None: # then get only the records with the specific (record_name, record_type) tuple @@ -897,7 +897,8 @@ class DetailedHistory(): description=DetailedHistory.get_key_val(detail_dict, "description")) - elif any(msg in history.msg for msg in ['Change zone','Change domain']) and 'access control' in history.msg: # added or removed a user from a zone + elif any(msg in history.msg for msg in ['Change zone', + 'Change domain']) and 'access control' in history.msg: # added or removed a user from a zone users_with_access = DetailedHistory.get_key_val(detail_dict, "user_has_access") self.detailed_msg = render_template_string(""" @@ -942,7 +943,7 @@ class DetailedHistory(): linked_domains=DetailedHistory.get_key_val(detail_dict, "domains")) - elif any(msg in history.msg for msg in ['Update type for zone','Update type for domain']): + elif any(msg in history.msg for msg in ['Update type for zone', 'Update type for domain']): self.detailed_msg = render_template_string("""
@@ -977,7 +978,8 @@ class DetailedHistory(): 'status'), history_msg=DetailedHistory.get_key_val(detail_dict, 'msg')) - elif any(msg in history.msg for msg in ['Update zone','Update domain']) and 'associate account' in history.msg: # When an account gets associated or dissociate with zones + elif any(msg in history.msg for msg in ['Update zone', + 'Update domain']) and 'associate account' in history.msg: # When an account gets associated or dissociate with zones self.detailed_msg = render_template_string('''
Zone: {{ domain }}
@@ -1231,8 +1233,10 @@ def history_table(): # ajax call data .filter( db.and_( db.or_( - History.msg.like("%domain " + domain_name) if domain_name != "*" else History.msg.like("%domain%"), - History.msg.like("%zone " + domain_name) if domain_name != "*" else History.msg.like("%zone%"), + History.msg.like("%domain " + domain_name) if domain_name != "*" else History.msg.like( + "%domain%"), + History.msg.like("%zone " + domain_name) if domain_name != "*" else History.msg.like( + "%zone%"), History.msg.like( "%domain " + domain_name + " access control") if domain_name != "*" else History.msg.like( "%domain%access control"), @@ -1540,7 +1544,8 @@ def has_an_auth_method(local_db_enabled=None, oidc_oauth_enabled = Setting().get('oidc_oauth_enabled') if azure_oauth_enabled is None: azure_oauth_enabled = Setting().get('azure_oauth_enabled') - return local_db_enabled or ldap_enabled or google_oauth_enabled or github_oauth_enabled or oidc_oauth_enabled or azure_oauth_enabled + return local_db_enabled or ldap_enabled or google_oauth_enabled or github_oauth_enabled or oidc_oauth_enabled \ + or azure_oauth_enabled @admin_bp.route('/setting/authentication', methods=['GET', 'POST']) @@ -1562,17 +1567,20 @@ def setting_authentication(): pwd_enforce_characters = True if request.form.get('pwd_enforce_characters') else False pwd_min_len = safe_cast(request.form.get('pwd_min_len', Setting().defaults["pwd_min_len"]), int, Setting().defaults["pwd_min_len"]) - pwd_min_lowercase = safe_cast(request.form.get('pwd_min_lowercase', Setting().defaults["pwd_min_lowercase"]), int, - Setting().defaults["pwd_min_lowercase"]) - pwd_min_uppercase = safe_cast(request.form.get('pwd_min_uppercase', Setting().defaults["pwd_min_uppercase"]), int, - Setting().defaults["pwd_min_uppercase"]) + pwd_min_lowercase = safe_cast( + request.form.get('pwd_min_lowercase', Setting().defaults["pwd_min_lowercase"]), int, + Setting().defaults["pwd_min_lowercase"]) + pwd_min_uppercase = safe_cast( + request.form.get('pwd_min_uppercase', Setting().defaults["pwd_min_uppercase"]), int, + Setting().defaults["pwd_min_uppercase"]) pwd_min_digits = safe_cast(request.form.get('pwd_min_digits', Setting().defaults["pwd_min_digits"]), int, Setting().defaults["pwd_min_digits"]) pwd_min_special = safe_cast(request.form.get('pwd_min_special', Setting().defaults["pwd_min_special"]), int, Setting().defaults["pwd_min_special"]) pwd_enforce_complexity = True if request.form.get('pwd_enforce_complexity') else False - pwd_min_complexity = safe_cast(request.form.get('pwd_min_complexity', Setting().defaults["pwd_min_complexity"]), int, + pwd_min_complexity = safe_cast(request.form.get('pwd_min_complexity', + Setting().defaults["pwd_min_complexity"]), int, Setting().defaults["pwd_min_complexity"]) if not has_an_auth_method(local_db_enabled=local_db_enabled): @@ -1585,14 +1593,12 @@ def setting_authentication(): else: Setting().set('local_db_enabled', local_db_enabled) Setting().set('signup_enabled', signup_enabled) - Setting().set('pwd_enforce_characters', pwd_enforce_characters) Setting().set('pwd_min_len', pwd_min_len) Setting().set('pwd_min_lowercase', pwd_min_lowercase) Setting().set('pwd_min_uppercase', pwd_min_uppercase) Setting().set('pwd_min_digits', pwd_min_digits) Setting().set('pwd_min_special', pwd_min_special) - Setting().set('pwd_enforce_complexity', pwd_enforce_complexity) Setting().set('pwd_min_complexity', pwd_min_complexity) @@ -2097,16 +2103,16 @@ def global_search(): results = server.global_search(object_type='all', query=query) # Filter results to domains to which the user has access permission - if current_user.role.name not in [ 'Administrator', 'Operator' ]: + if current_user.role.name not in ['Administrator', 'Operator']: allowed_domains = db.session.query(Domain) \ .outerjoin(DomainUser, Domain.id == DomainUser.domain_id) \ .outerjoin(Account, Domain.account_id == Account.id) \ .outerjoin(AccountUser, Account.id == AccountUser.account_id) \ .filter( - db.or_( - DomainUser.user_id == current_user.id, - AccountUser.user_id == current_user.id - )) \ + db.or_( + DomainUser.user_id == current_user.id, + AccountUser.user_id == current_user.id + )) \ .with_entities(Domain.name) \ .all() allowed_domains = [value for value, in allowed_domains] diff --git a/powerdnsadmin/static/custom/js/app-authentication-settings-editor.js b/powerdnsadmin/static/custom/js/app-authentication-settings-editor.js new file mode 100644 index 0000000..104b3e9 --- /dev/null +++ b/powerdnsadmin/static/custom/js/app-authentication-settings-editor.js @@ -0,0 +1,273 @@ +let model; + +let AuthenticationSettingsModel = function (user_data, csrf_token, selector) { + let self = this; + + let defaults = { + tab_active: '', + tab_default: 'local', + + // Local Authentication Settings + local_db_enabled: true, + signup_enabled: true, + pwd_enforce_characters: false, + pwd_min_len: 10, + pwd_min_lowercase: 3, + pwd_min_uppercase: 2, + pwd_min_digits: 2, + pwd_min_special: 1, + pwd_enforce_complexity: false, + pwd_min_complexity: 11, + + // LDAP Authentication Settings + ldap_enabled: false, + ldap_type: 'ldap', + ldap_uri: '', + ldap_base_dn: '', + ldap_admin_username: '', + ldap_admin_password: '', + ldap_domain: '', + ldap_filter_basic: '', + ldap_filter_username: '', + ldap_filter_group: '', + ldap_filter_groupname: '', + ldap_sg_enabled: false, + ldap_admin_group: '', + ldap_operator_group: '', + ldap_user_group: '', + autoprovisioning: false, + autoprovisioning_attribute: '', + urn_value: '', + purge: false, + + // Google OAuth2 Settings + google_oauth_enabled: false, + google_oauth_client_id: '', + google_oauth_client_secret: '', + google_oauth_scope: '', + google_base_url: '', + google_oauth_auto_configure: false, + google_oauth_metadata_url: '', + google_token_url: '', + google_authorize_url: '', + + // GitHub OAuth2 Settings + github_oauth_enabled: false, + github_oauth_key: '', + github_oauth_secret: '', + github_oauth_scope: '', + github_oauth_api_url: '', + github_oauth_auto_configure: false, + github_oauth_metadata_url: '', + github_oauth_token_url: '', + github_oauth_authorize_url: '', + + // Azure AD OAuth2 Settings + azure_oauth_enabled: false, + azure_oauth_key: '', + azure_oauth_secret: '', + azure_oauth_scope: '', + azure_oauth_api_url: '', + azure_oauth_auto_configure: false, + azure_oauth_metadata_url: '', + azure_oauth_token_url: '', + azure_oauth_authorize_url: '', + azure_sg_enabled: false, + azure_admin_group: '', + azure_operator_group: '', + azure_user_group: '', + azure_group_accounts_enabled: false, + azure_group_accounts_name: '', + azure_group_accounts_name_re: '', + azure_group_accounts_description: '', + azure_group_accounts_description_re: '', + + // OIDC OAuth2 Settings + oidc_oauth_enabled: false, + oidc_oauth_key: '', + oidc_oauth_secret: '', + oidc_oauth_scope: '', + oidc_oauth_api_url: '', + oidc_oauth_auto_configure: false, + oidc_oauth_metadata_url: '', + oidc_oauth_token_url: '', + oidc_oauth_authorize_url: '', + oidc_oauth_logout_url: '', + oidc_oauth_username: '', + oidc_oauth_email: '', + oidc_oauth_firstname: '', + oidc_oauth_last_name: '', + oidc_oauth_account_name_property: '', + oidc_oauth_account_description_property: '', + } + + self.data = {}; + + self.setupObservables = function () { + self.tab_active = ko.observable(self.data.tab_active); + self.tab_default = ko.observable(self.data.tab_default); + + // Local Authentication Settings + self.local_db_enabled = ko.observable(self.data.local_db_enabled); + self.signup_enabled = ko.observable(self.data.signup_enabled); + self.pwd_enforce_characters = ko.observable(self.data.pwd_enforce_characters); + self.pwd_min_len = ko.observable(self.data.pwd_min_len); + self.pwd_min_lowercase = ko.observable(self.data.pwd_min_lowercase); + self.pwd_min_uppercase = ko.observable(self.data.pwd_min_uppercase); + self.pwd_min_digits = ko.observable(self.data.pwd_min_digits); + self.pwd_min_special = ko.observable(self.data.pwd_min_special); + self.pwd_enforce_complexity = ko.observable(self.data.pwd_enforce_complexity); + self.pwd_min_complexity = ko.observable(self.data.pwd_min_complexity); + + // LDAP Authentication Settings + self.ldap_enabled = ko.observable(self.data.ldap_enabled); + self.ldap_type = ko.observable(self.data.ldap_type); + self.ldap_uri = ko.observable(self.data.ldap_uri); + self.ldap_base_dn = ko.observable(self.data.ldap_base_dn); + self.ldap_admin_username = ko.observable(self.data.ldap_admin_username); + self.ldap_admin_password = ko.observable(self.data.ldap_admin_password); + self.ldap_domain = ko.observable(self.data.ldap_domain); + self.ldap_filter_basic = ko.observable(self.data.ldap_filter_basic); + self.ldap_filter_username = ko.observable(self.data.ldap_filter_username); + self.ldap_filter_group = ko.observable(self.data.ldap_filter_group); + self.ldap_filter_groupname = ko.observable(self.data.ldap_filter_groupname); + self.ldap_sg_enabled = ko.observable(self.data.ldap_sg_enabled); + self.ldap_admin_group = ko.observable(self.data.ldap_admin_group); + self.ldap_operator_group = ko.observable(self.data.ldap_operator_group); + self.ldap_user_group = ko.observable(self.data.ldap_user_group); + self.autoprovisioning = ko.observable(self.data.autoprovisioning); + self.autoprovisioning_attribute = ko.observable(self.data.autoprovisioning_attribute); + self.urn_value = ko.observable(self.data.urn_value); + self.purge = ko.observable(self.data.purge); + + // Google OAuth2 Settings + self.google_oauth_enabled = ko.observable(self.data.google_oauth_enabled); + self.google_oauth_client_id = ko.observable(self.data.google_oauth_client_id); + self.google_oauth_client_secret = ko.observable(self.data.google_oauth_client_secret); + self.google_oauth_scope = ko.observable(self.data.google_oauth_scope); + self.google_base_url = ko.observable(self.data.google_base_url); + self.google_oauth_auto_configure = ko.observable(self.data.google_oauth_auto_configure); + self.google_oauth_metadata_url = ko.observable(self.data.google_oauth_metadata_url); + self.google_token_url = ko.observable(self.data.google_token_url); + self.google_authorize_url = ko.observable(self.data.google_authorize_url); + + // GitHub OAuth2 Settings + self.github_oauth_enabled = ko.observable(self.data.github_oauth_enabled); + self.github_oauth_key = ko.observable(self.data.github_oauth_key); + self.github_oauth_secret = ko.observable(self.data.github_oauth_secret); + self.github_oauth_scope = ko.observable(self.data.github_oauth_scope); + self.github_oauth_api_url = ko.observable(self.data.github_oauth_api_url); + self.github_oauth_auto_configure = ko.observable(self.data.github_oauth_auto_configure); + self.github_oauth_metadata_url = ko.observable(self.data.github_oauth_metadata_url); + self.github_oauth_token_url = ko.observable(self.data.github_oauth_token_url); + self.github_oauth_authorize_url = ko.observable(self.data.github_oauth_authorize_url); + + // Azure AD OAuth2 Settings + self.azure_oauth_enabled = ko.observable(self.data.azure_oauth_enabled); + self.azure_oauth_key = ko.observable(self.data.azure_oauth_key); + self.azure_oauth_secret = ko.observable(self.data.azure_oauth_secret); + self.azure_oauth_scope = ko.observable(self.data.azure_oauth_scope); + self.azure_oauth_api_url = ko.observable(self.data.azure_oauth_api_url); + self.azure_oauth_auto_configure = ko.observable(self.data.azure_oauth_auto_configure); + self.azure_oauth_metadata_url = ko.observable(self.data.azure_oauth_metadata_url); + self.azure_oauth_token_url = ko.observable(self.data.azure_oauth_token_url); + self.azure_oauth_authorize_url = ko.observable(self.data.azure_oauth_authorize_url); + self.azure_sg_enabled = ko.observable(self.data.azure_sg_enabled); + self.azure_admin_group = ko.observable(self.data.azure_admin_group); + self.azure_operator_group = ko.observable(self.data.azure_operator_group); + self.azure_user_group = ko.observable(self.data.azure_user_group); + self.azure_group_accounts_enabled = ko.observable(self.data.azure_group_accounts_enabled); + self.azure_group_accounts_name = ko.observable(self.data.azure_group_accounts_name); + self.azure_group_accounts_name_re = ko.observable(self.data.azure_group_accounts_name_re); + self.azure_group_accounts_description = ko.observable(self.data.azure_group_accounts_description); + self.azure_group_accounts_description_re = ko.observable(self.data.azure_group_accounts_description_re); + + // OIDC OAuth2 Settings + self.oidc_oauth_enabled = ko.observable(self.data.oidc_oauth_enabled); + self.oidc_oauth_key = ko.observable(self.data.oidc_oauth_key); + self.oidc_oauth_secret = ko.observable(self.data.oidc_oauth_secret); + self.oidc_oauth_scope = ko.observable(self.data.oidc_oauth_scope); + self.oidc_oauth_api_url = ko.observable(self.data.oidc_oauth_api_url); + self.oidc_oauth_auto_configure = ko.observable(self.data.oidc_oauth_auto_configure); + self.oidc_oauth_metadata_url = ko.observable(self.data.oidc_oauth_metadata_url); + self.oidc_oauth_token_url = ko.observable(self.data.oidc_oauth_token_url); + self.oidc_oauth_authorize_url = ko.observable(self.data.oidc_oauth_authorize_url); + self.oidc_oauth_logout_url = ko.observable(self.data.oidc_oauth_logout_url); + self.oidc_oauth_username = ko.observable(self.data.oidc_oauth_username); + self.oidc_oauth_email = ko.observable(self.data.oidc_oauth_email); + self.oidc_oauth_firstname = ko.observable(self.data.oidc_oauth_firstname); + self.oidc_oauth_last_name = ko.observable(self.data.oidc_oauth_last_name); + self.oidc_oauth_account_name_property = ko.observable(self.data.oidc_oauth_account_name_property); + self.oidc_oauth_account_description_property = ko.observable(self.data.oidc_oauth_account_description_property); + } + + self.updateWithDefaults = function (instance) { + self.data = $.extend(defaults, instance) + } + + self.activateTab = function (tab) { + $('[role="tablist"] a.nav-link').blur(); + self.tab_active(tab); + window.location.hash = tab; + } + + self.activateDefaultTab = function () { + self.activateTab(self.tab_default()); + } + + self.initTabs = function() { + if (self.hasHash()) { + self.activateTab(self.getHash()); + } else { + self.activateDefaultTab(); + } + } + + self.getHash = function () { + return window.location.hash.substring(1); + } + + self.hasHash = function () { + return window.location.hash.length > 1; + } + + self.setupListeners = function () { + if ('onhashchange' in window) { + $(window).bind('hashchange', self.onHashChange); + } + } + + self.destroyListeners = function () { + if ('onhashchange' in window) { + $(window).unbind('hashchange', self.onHashChange); + } + } + + self.onTabClick = function (model, event) { + self.activateTab($(event.target).data('tab')); + return false; + } + + self.onHashChange = function (event) { + let hash = window.location.hash.trim(); + if (hash.length > 1) { + self.activateTab(hash.substring(1)); + } else { + self.activateDefaultTab(); + } + } + + self.updateWithDefaults(user_data); + self.setupObservables(); + + ko.applyBindings(self); + + self.initTabs(); + self.setupListeners(); +} + +$(function () { + // TODO: Load the data from the server and pass it to the model instantiation + loaded_data = {}; + model = new AuthenticationSettingsModel(loaded_data, CSRF_TOKEN, '#settings-editor'); +}) \ No newline at end of file diff --git a/powerdnsadmin/templates/admin_setting_authentication.html b/powerdnsadmin/templates/admin_setting_authentication.html index ca8baa5..73d305d 100644 --- a/powerdnsadmin/templates/admin_setting_authentication.html +++ b/powerdnsadmin/templates/admin_setting_authentication.html @@ -25,7 +25,7 @@
-
+

Settings Editor

@@ -43,31 +43,43 @@
Associate: {{ history_assoc_account }}