Park-Manager Documentation¶
Park-Manager is a full-stack hosting-management system.
In the documentation, you can find how to install Park-Manager, customize the platform, create your own Modules, and how to contribute to the project.
Caution
Park-Manager is still in active development, the current status is pre-alpha, and not recommended for production usage.
Major code changes should be expected. Some documentation sections may be incomplete or missing.
Getting Help¶
TBD.
Getting Started¶
To install the Park-Manager Application you need at least a system that is able to run Docker.
Development and Customizing¶
Park-Manager is build-up from multiple modules, that already provide a ridge functionality for your hosting solution, but what Park-Manager prides itself for is the extendability of the system.
In this chapter you learn how to fully customize Park-Manager to your needs.
Park-Manager System¶
Park-Manager is build-up from multiple modules, including the CoreModule that forms the basis for booting the Application.
In this chapter you can find more information about the system architecture, and how to work with the SystemConfig.
Note
Park-Manager is build on the Symfony Framework, this chapter assumes you have advanced experience with this framework.
The architecture follows a Domain Driven Design approach, some basic
knowledge is required. If you are completely new to this, checkout
Introduction to DDD
first.
Topics¶
The The Module System chapter explains in great detail how the Modules are constructed, integrated, interact and how to achieve a strict separation of concerns.
The
runtime_system
is responsible for booting the application and background services.The SystemConfig gives a detailed overview and explanation of communicating with external system configurations like Email storage, FTP, and web-server virtual-hosts.
The
/architecture/user_system
explains how to work with the different user types (User and Administrator, Reseller) and how to link ownership of a “product”.The application is separated into tree Application Sections: Client, Administrator, and ResellerSpace 0 that gives Administrators a private section for company brand websites.
In chapter you can find more information about the routing schema’s, access rules separation.
SystemConfig¶
The SystemConfig Component manages the configuration for external systems including:
Managing OS system users
;Mail storage (and content filtering)
;FTP Users and IP-address access listing
;Web server virtual-hosts configuration (including TLS)
;Cron jobs and scheduled tasks
.
Note
The SystemConfig is a separate boundary, communication with this system is handled through a ServiceBus. A Module cannot directly access the information.
“Ownership” of an entity is managed by the communicating Modules,
the Module uses a RootEntityOwner
object with the (uuid) id of
either an Webhosting Account or System Administrator.
The SystemConfig Component doesn’t enforce it’s own access control as this is handled by a ServiceBus MessageGuard.
Cron jobs and Scheduled Tasks¶
TODO.
System ServiceBus¶
Access to protected system services is handled using a dedicated ServiceBus operating outside the application. Access is heavily guarded and operations are explicit.
TBD.
The System ServiceBus works outside of the Application itself and therefor, the service configurations need to be set-up properly in-advance.
While a source-code is a good reference for understanding how an application or library works, a system set-up however is more complex and can be applied in various forms.
To allow full customizing, all service set-ups have a fully documented implementation schema (called a Set-up Schema).
In a Set-up Schema you can find all details regarding a chosen set-up, including the motivation for this chosen set-up, how to test the set-up (after changing) and what things you can and can not change.
The System ServiceBus works outside of the Application itself and therefor, the service configurations need to be set-up properly in-advance.
This chapter explains the process for managing SystemConfig set-ups. All configuration set-ups provided by the Park-Manager project are expected to follow this process.
First start with a proper plan or requirements list for what the configuration must support and allows in terms of managing by Park-Manager.
Note
If a set-up doesn’t provide enough flexibility the systems administrator might need to apply some manual changes, which could destabilize the systems configuration. Which is why good documentation is critical!
Implement the planned configuration set-up and test if it works properly and follows the original specification.
Document how the system is set-up, which configurations must remain intact for Park-Manager to function properly and what can be safely changed.
Also explain why something was done in a specific way if this is not directly clear or diverges from the “recommended” configuration of the vendor.
Explain pros and cons of the chosen implementation.
Also document how the tests were performed, and which are attention points when performing the tests.
Tip
If possible an automated script also suffices for a testing documentation.
Application Sections¶
TBD.
Contributing¶
Park-Manager is a community driven project, we value good coding quality, and easy to use software for users.
To contribute to Park-Manager please read this chapter carefully to understand how we build and maintain the Park-Manager project.
The Contribution Guide¶
Contributing Code¶
Contributing Code¶
When proposing action, small or large, try always to start by identifying the problems you want to solve. Only when you have a clear and real problem on which everyone can agree, move to discussing solutions.
Discuss first, then start coding.
Each new feature should be discussed before any patches are provided;
Tickets are not assigned to anyone. If you want a feature, either provide a pull request or if possible consider offering someone money to work on the feature.
New feature implementations should start out with as minimal code possible, work towards a Minimum Viable Product (MVP).
If it were a car, you should be able to drive it. Maybe it doesn’t have a radio or A/C, but it can drive.
Caution
If you think you’ve found a security issue, please use the special procedure.
Backward Compatibility Promise¶
Park-Manager follows a versioning strategy called Semantic Versioning. It means that only major releases include BC breaks, whereas minor releases include new features without breaking backwards compatibility.
Warning
Park-Manager is the pre-alpha development stage.
We release minor version before every larger change, but be prepared for Backward Compatibility breaks to happen until a v1.0.0 release.
Since Park-Manager is based on Symfony, our BC promise extends Symfony’s Backward Compatibility Promise with a few new rules and exceptions stated in this document.
Patch releases (such as 1.0.1, 1.0.2, etc.) do not require any additional work apart from cleaning the Symfony cache.
Minor releases (such as 1.1.0, 1.2.0, etc.) require to run database migrations.
This BC promise applies to all of Park-Manager’ PHP code except for:
- code tagged with
@internal
tags- event listeners
- model and repository interfaces
- PHPUnit tests (located at
src/**/Tests/
)- Behat tests (located at
src/**/Behat/
)
In order to fulfill the constant need to evolve, models are excluded from this BC promise. Methods may be added to models.
Following the reasoning same as above and due to technological constraints, repository interfaces are also excluded from this BC promise.
They are excluded from this BC promise, but they should be as simple as possible and always call another service. Behaviour they’re providing (the end result) is still included in BC promise.
The currently present routes cannot have their name changed, but optional
parameters might be added to them. All the new routes will start with
park-manager.
prefix in order to avoid conflicts.
Services names cannot change, but new services might be added with park_manager.
prefix.
Note
For autowiring make sure to reference the interface (if any) and not a solid implementation.
Neither template events, block or templates themselves cannot be deleted or renamed.
From time to time, some classes and/or methods are deprecated in Park-Manager; that happens when a feature implementation cannot be changed because of backward compatibility issues, but we still want to propose a “better” alternative. In that case, the old implementation can simply be deprecated.
A feature is marked as deprecated by adding a @deprecated
phpdoc to
relevant classes, methods, properties, ...:
/**
* @deprecated since version 1.8, to be removed in 2.0. Use XXX instead.
*/
The deprecation message should indicate the version when the class/method was deprecated, the version when it will be removed, and whenever possible, how the feature was replaced.
A PHP E_USER_DEPRECATED
error must also be triggered to help people with
the migration starting one or two minor versions before the version where the
feature will be removed (depending on the criticality of the removal).
See Symfony’s Deprecations Convention on how to apply this deprecation logic.
Security Policy¶
This document explains how Park-Manager security issues are handled by the Park-Manager
core team (Park-Manager being the code hosted on the main park-manager/park-manager
Git
repository).
If you think that you have found a security issue in Park-Manager, don’t use the bug tracker and don’t publish it publicly. Instead, all security issues must be sent to security [at] rollerscapes.net. Emails sent to this address are forwarded to the Park-Manager core-team private mailing-list.
Note
While we are working on a patch, please do not reveal the issue publicly. The resolution can take anywhere between a couple of days, a month or an indefinite amount of time depending on its complexity.
For each report, we first try to confirm the vulnerability. When it is confirmed, the core-team works on a solution following these steps:
- Send an acknowledgement to the reporter;
- Work on a patch;
- Get a CVE identifier from mitre.org;
- Write a security announcement for the official Park-Manager blog about the
vulnerability. This post should contain the following information:
- a title that always include the “Security release” string;
- a description of the vulnerability;
- the affected versions;
- the possible exploits;
- how to patch/upgrade/workaround affected applications;
- the CVE identifier;
- credits.
- Send the patch and the announcement to the reporter for review;
- Apply the patch to all maintained versions of Park-Manager;
- Package new versions for all affected versions;
- Publish the post on the official Park-Manager blog (it must also be added to the “Security Advisories” category);
- Update the security advisory list (see below).
- Update the public security advisories database maintained by the
FriendsOfPHP organization and which is used by the
security:check
command.
This section explains how we classify security issues that are discovered in Park-Manager.
A critical rating applies to vulnerabilities that allow remote, unauthenticated access and code execution, with no user interaction required.
These would allow complete system compromise and can easily be exploited by automated scripts such as worms.
An important rating applies to vulnerabilities that allow system authentication levels to be compromised.
These include allowing local users to elevate their privilege levels, unauthenticated remote users to see resources that should require authentication to view, the execution of arbitrary code by remote users, or any local or remote attack that could result in an denial of service.
A moderate rating applies to vulnerabilities that rely on unlikely scenarios in order to compromise the system.
These usually require that a flawed or unlikely configuration of the system be in place, and only occur in rare situations.
A trivial rating applies to vulnerabilities that do not fit into the higher categories.
These vulnerabilities occur in very unlikely situations and configurations, often requiring extremely tight timing of execution and/or for events to occur that are out of the attacker’s control.
This rating may also be given to vulnerabilities that, even if successful, impose few or no consequences on the system.
Experimental Features¶
All Park-Manager features benefit from our Backward Compatibility Promise to give developers the confidence to upgrade to new versions safely and more often.
But sometimes, a new feature is controversial. Or finding a good API is not easy. In such cases, we prefer to gather feedback from real-world usage, adapt the API, or remove it altogether. Doing so is not possible with a no BC-break approach.
To avoid being bound to our backward compatibility promise, such features can
be marked as experimental and their classes and methods must be marked with
the @experimental
tag.
A feature can be marked as being experimental for only one minor version, and can never be introduced in a last major release. The core team can decide to extend the experimental period for another minor version on a case by case basis.
To ease upgrading projects using experimental features, the changelog must explain backward incompatible changes and explain how to upgrade code.
Reporting a Bug¶
Whenever you find a bug in Park-Manager, we kindly ask you to report it. It helps us make a better hosting system.
Caution
If you think you’ve found a security issue, please use the special procedure instead.
Before submitting a bug:
- Double-check the official documentation to see if you’re not misusing the system;
- Check if your problem relates to Park-Manager or a third-party library, if your problem doesn’t directly relate to Park-Manager, report it to the external project instead;
- Ask for assistance on Stack Overflow, on the #support channel of the Park-Manager Slack if you’re not sure if your issue really is a bug.
If your problem definitely looks like a bug, report it using the official bug tracker and follow some basic rules:
Use the title field to clearly describe the issue;
Describe the steps needed to reproduce the bug with short code examples (providing a unit test that illustrates the bug is best);
Give as much detail as possible about your environment (OS, Docker version, PHP version, Symfony version, Park-Manager version, enabled extensions, used modules, ...);
If you want to provide a stack trace you got on an HTML page, be sure to provide the plain text version, which should appear at the bottom of the page. Do not provide it as a screenshot, since search engines will not be able to index the text inside them. Same goes for errors encountered in a terminal, do not take a screenshot, but copy/paste the contents. If the stack trace is long, consider enclosing it in a <details> HTML tag.
Be wary that stack traces may contain sensitive information, and if it is the case, be sure to redact them prior to posting your stack trace.
(optional) Attach a patch.
In the issue tracker there are two types of bug reports:
- Bug - Confirmed bugs or bug fixes.
- Potential Bug - Bug reports, should become a Bug after confirming it.
A “Potential Bug” needs to be confirmed by the Core Team or another Contributor (preferably by providing a minimal reproduction of the reported issue), before becoming a confirmed bug.
Note
Spelling/grammar corrections and styling violations are not marked as a Bug. These are marked as an Enhancement.
Any Potential Bug that cannot be reproduced (with the provided information) is marked as Help Wanted, if no new information is provided within a reasonable time period the issue is marked as Stale. And thereafter closed when no activity is reported after two weeks.
Caution
Except for syntax error fixes; a proposed patch should always have a test-case to proof the patch fixes the reported bug and to prevent future regressions.
Bug-fix pull requests without a test-case are not be merged unless a test is technically impossible or other measures are taken to prevent future regressions.
Submitting a Patch¶
Patches are the best way to provide a bug fix or to propose enhancements to Park-Manager.
Before working on Park-Manager, setup a friendly environment with the following software:
- Git;
- Docker 17 (and DockerCompose) or above.
- Makefile
Tip
The tests are run using Docker so you don’t have to worry about additional software requirements or side affects.
Make sure you have the latest Docker and Docker-compose installed (The old Docker toolbox is not officially supported and requires manual configuring).
If you don’t have Makefile installed obtain a copy for your system using the systems software repository. macOS can install the last version of xCode, Windows users can either use the Linux subsystem (Windows 10) or use GnuWin.
Set up your user information with your real name and a working email address:
$ git config --global user.name "Your Name"
$ git config --global user.email you@example.com
Tip
If you are new to Git, you are highly recommended to read the excellent and free ProGit book.
Tip
If your IDE creates configuration files inside the project’s directory,
you can use global .gitignore
file (for all projects) or
.git/info/exclude
file (per project) to ignore them. See
GitHub’s documentation.
Tip
Windows users: when installing Git, the installer will ask what to do with line endings, and suggests replacing all LF with CRLF. This is the wrong setting if you wish to contribute to Park-Manager! Selecting the as-is method is your best choice, as Git will convert your line feeds to the ones in the repository. If you have already installed Git, you can check the value of this setting by typing:
$ git config core.autocrlf
This will return either “false”, “input” or “true”; “true” and “false” being the wrong values. Change it to “input” by typing:
$ git config --global core.autocrlf input
Replace --global
by --local
if you want to set it only for the active
repository.
Get the Park-Manager source code:
- Create a GitHub account and sign in;
- Fork the Park-Manager repository (click on the “Fork” button);
- After the “forking action” has completed, clone your fork locally
(this will create a
park-manager
directory):
$ git clone git@github.com:USERNAME/park-manager.git
- Add the upstream repository as a remote:
$ cd park-manager
$ git remote add upstream git://github.com/park-manager/park-manager.git
- Adjust your branch to track the Park-Manager master remote branch, by default it’ll track your origin remote’s master:
$ git config branch.master.remote upstream
Note
If you plan to only contribute documentation you may skip this step,
and run make doc
instead.
Now that Park-Manager is installed, check that all unit tests pass for your environment:
$ make install # This uses Docker to install dependencies, but files are stored locally
$ make test
If all went well you are now ready to start contributing, see also the related sections about the projects coding standards and used conventions.
Before you start, you must know that all the patches you are going to submit must be released under the MPL-v2.0. license, unless explicitly specified in your commits.
All patches must be targeted against the master
branch,
including bug fixes and minor corrections like typo’s.
Keep in mind that your changes will be cherry-picked to lower branches by maintainers after the merge if they are applicable.
Each time you want to work on a patch for a bug or on an enhancement, create a topic branch:
$ git checkout -b BRANCH_NAME upstream/master
Tip
Use a descriptive name for your branch (like ticket_XXX
where XXX
is the ticket number is a good convention for bug fixes).
The above checkout commands automatically switch the code to the newly created
branch (check the branch you are working on with git branch
).
If you want to test your code in an existing project that uses park-manager/park-manager
,
you can use the link
utility provided in the Git repository you cloned previously.
This tool scans the vendor/
directory of your project, finds Park-Manager packages
it uses, and replaces them by symbolic links to the ones in the Git repository.
$ php link /path/to/your/project
Before running the link
command, be sure that the dependencies of the project you
want to debug are installed by running composer install
inside it.
Work on the code as much as you want and commit as much as you want; but keep in mind the following:
- Follow the coding
standards
(usegit diff --check
to check for trailing spaces – also read the tip below); - Add (unit) tests to prove that the bug is fixed or that the new feature actually works;
- Try hard to not break backward compatibility (if you must do so, try to provide a compatibility layer to support the old way) – patches that break backward compatibility have less chance to be merged;
- Do atomic and logically separate commits (use the power of
git rebase
to have a clean and logical history); - Squash irrelevant commits that are just about fixing coding standards or fixing typos in your own code;
- Never fix coding standards in some existing code as it makes the code review more difficult;
- In addition to this “code” pull request, you must also update the documentation when appropriate. See more in contributing documentation section.
- Each patch defines one clear and agreed problem, and one clear, minimal, plausible solution. If done properly using Conventional Commits will automatically help you to make your commits atomic and clear;
- Write good commit messages (see the tip below).
Tip
When submitting pull requests, Travis CI checks your code
for common typos and verifies that you are using the coding standards
as defined other chapters.
A status is posted below the pull request description with a summary of any problems it detects or any build failures.
A good commit message uses the Conventional Commits guide lines, with the following additions:
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood (
add
/fix
notadded
/fixed
) - Wrap the body at 72 characters
- Use the body to explain what and why vs. how
For <type>
use the following values:
- build: Changes that affect the build system or external dependencies (example docker, webpack, travis)
- ci: Changes to our CI configuration files and scripts (example scopes: Travis CI, BrowserStack, SauceLabs)
- docs: Documentation only changes
- feat: A new feature
- fix: A bug fix
- perf: A code change that improves performance
- refactor: A code change that neither fixes a bug nor adds a feature
- style: Changes that do not affect the meaning of the code (white-space, formatting, phpdoc comments, etc)
- test: Adding missing tests or correcting existing tests
For scope
use the name of the Module (in lowercase), either:
core
, webhosting
, customer
, domainreg
, etc.
When your patch is not about a bug fix (when you add a new feature or change an existing one for instance), it must also include the following:
- An explanation of the changes in the relevant
CHANGELOG
file(s) (the[BC BREAK]
or the[DEPRECATION]
prefix must be used when relevant); - An explanation on how to upgrade an existing application in the relevant
UPGRADE
file(s) if the changes break backward compatibility or if you deprecate something that will ultimately break backward compatibility. - A
BREAKING CHANGE:
entry the commit message.
Whenever you feel that your patch is ready for submission, follow the following steps.
Before submitting your patch, update your branch (needed if it takes you a while to finish your changes):
$ git checkout BRANCH_NAME
$ git rebase --pull upstream/master
When doing the rebase
command, you might have to fix merge conflicts.
git status
will show you the unmerged files. Resolve all the conflicts,
then continue the rebase:
$ git add ... # add resolved files
$ git rebase --continue
Check that all tests still pass and push your branch remotely:
$ git push --force origin BRANCH_NAME
You can now make a pull request on the park-manager/park-manager
GitHub repository.
To ease the core team work, always include the modified components in your pull request message, like in:
[Core] fix something
[Webhosting] [Core add something
The default pull request description contains a table which you must fill in with the appropriate answers. This ensures that contributions may be reviewed without needless feedback loops and that your contributions can be included into Park-Manager as quickly as possible.
Some answers to the questions trigger some more requirements:
- If you answer yes to “Bug fix?”, check if the bug is already listed in the Park-Manager issues and reference it/them in “Fixed tickets”;
- If you answer yes to “New feature?”, you must update the documentation when appropriate;
- If you answer yes to “BC breaks?”, the patch must contain updates to the
relevant
CHANGELOG
andUPGRADE
files; - If you answer yes to “Deprecations?”, the patch must contain updates to the
relevant
CHANGELOG
andUPGRADE
files; - If you answer no to “Tests pass”, you must add an item to a todo-list with the actions that must be done to fix the tests;
- If the “license” is not MPL-v2.0, just don’t submit the pull request as it won’t be accepted anyway.
If some of the previous requirements are not met, create a todo-list and add relevant items:
- [ ] fix the tests as they have not been updated yet
- [ ] submit changes to the documentation
- [ ] document the BC breaks
If the code is not finished yet because you don’t have time to finish it or because you want early feedback on your work, add an item to todo-list:
- [ ] finish the code
- [ ] gather feedback for my changes
As long as you have items in the todo-list, please prefix the pull request title with “[WIP]”.
In the pull request description, give as much details as possible about your changes (don’t hesitate to give code examples to illustrate your points). If your pull request is about adding a new feature or modifying an existing one, explain the rationale for the changes. The pull request description helps the code review and it serves as a reference when the code is merged (the pull request description and all its associated comments are part of the merge commit message).
Based on the feedback on the pull request, you might need to rework your
patch. Before re-submitting the patch, rebase with upstream/master
or
the branch your pull request is targeting, don’t merge; and force the push
to the origin:
$ git rebase -f upstream/master
$ git push --force-with-lease origin BRANCH_NAME
Note
When doing a push --force-with-lease
, always specify the branch name explicitly
to avoid messing other branches in the repo (--force-with-lease
tells Git that
you really want to mess with things so do it carefully).
Often, Park-Manager team members will ask you to “squash” your commits. This means you will convert many commits to one commit.
To do this, use the rebase command:
$ git rebase -i upstream/master
$ git push --force-with-lease origin BRANCH_NAME
After you type this command, an editor will popup showing a list of commits:
pick 1a31be6 first commit
pick 7fc64b4 second commit
pick 7d33018 third commit
To squash all commits into the first one, remove the word pick
before the
second and the last commits, and replace it by the word squash
or just
s
. When you save, Git will start rebasing, and if successful, will ask
you to edit the commit message, which by default is a listing of the commit
messages of all the commits. When you are finished, execute the push command.
Testing Methodology¶
Park-Manager has fully adapted test driven development.
Because one testing framework doesn’t necessarily work as good as the other for a specific situation Park-Manager uses a number of testing frameworks.
This sections explains the basic principles of test driven development and standards that are followed within Park-Manager.
In short “test driven development” or TTD means you write your tests before you write the implementation, but why?
First you need to understand that the only reason why software exists, is to solve a specific problem. A problem can be anything, from (long distance) communicating to filling your taxes.
- Email was invented to solve the “problem” of long distance communication;
- Reusable Frameworks were created to solve the “problem” of constantly repeating ourselves in solving a similar problem;
You get the picture. Now, your tests are an abstraction of the problem you are solving. The tests describe in short what the problem includes.
Say you are creating a Forum system to allow people to communicate with each other. But you don’t want to allow anonymous posts, as this makes it harder to ban people or inform them about new messages.
Your problem is the absence of a user-system. Now before you start coding, think really hard about what is needed. Don’t focus on nice-to-have features, as they only clutter the spectrum. This is called the analyzing or learning stage.
Note: At first you actually don’t now you need a user-system, the user system itself emerges from the solution.
Before a user can post they first need to sign-in (or login), but this requires an account to exist. You know discovered you need a registration system.
To absence of a registration system is called a “Problem Domain”, you then abstract this problem “Problem Domain” into a test (or use-case).
Run your newly created test, and see it fail. Why? Because there is no code to make the test pass. The failing of your tests confirms your tests can only work when an implementation (or solution) is in place. If your test passes it means the test is broken, RED is good in this stage.
Now you need to make your test(s) pass (GREEN stage), add the implementation and run your tests. Does it pass? Great! good yob.
If your tests don’t pass, then fix the implementation and run your tests again. And repeat this process until they are green.
Do all the tests pass? Wonderful. Now repeat this RED-GREEN process until you no longer have any “problems” that need “solutions”. And you you end-up with fully tested code, that will never contain more then needed.
No solutions unless there is a clear problem.
Or shorter:
When you write your tests before you write the implementation, the only logical reason why your code exists is to make your tests pass.
You will never write code that has no tests, your tests are the abstraction of the “problem”, your code is the solution to that problem.
By writhing your tests first you can be certain that the tests only pass with working code, and not because they are not testing anything.
But wait, there is one more stage between the RED-GREEN process, the “blue” phase.
The “blue” (or refactoring phase) is a moment between finding and fixing problems, and taking a look at the existing code and see what could be improved. After every improvement, run your tests to make sure nothing got broken.
Caution
During the refactoring phase don’t add new tests or remove existing tests. This phase is about improving the code, not about solving the “problem domain”.
And during this cycle only write one test and solution at a time, the longer the time between coding and testing the higher the risk of creating a hard to find problem.
The same applies for refactoring. If your test fail after a small change you can be certain only that change caused the failure. The bigger your refactoring the more time you will spend debugging, and less on getting work done.
RED is good, add or fix the code to make it green;
RED-GREEN-REFACTOR is the rule;
Don’t prefix
it
block descriptions withshould
. Use Imperative mood instead;Don’t mock a type you don’t own! (see below);
If your specification is getting too complex, the design is wrong,
try decoupling a bit more;
shouldBeCalled
orwillReturn
, never together, except for builders;If you cannot describe something easily, probably you should not be
doing it that way;
Lastly, tests can only prove the is code is “wrong”, they cannot prove the code is completely “correct”, the more variations are used to greater the change of proving the code is wrong. But watch out for over testing (see below).
An Acceptance test, tests that a specific functionality as a bigger whole is working as described.
Use Behat for StoryBDD and always write new scenarios when adding a feature, or update existing stories to adapt to business requirements changes.
Remember you are writing a “business requirement” that describes “what” the requirement is, not “how” the requirement is fulfilled.
- Avoid scenario titles that repeat the feature title;
- Use scenario titles that describe the success and failure paths;
- Avoid describing technical actions (see Introducing Modelling by Example);
- “When I click”
- “And I go to the “/basket” page”
- Use the
features
directory to store feature specs;
- PhpUnit is used for unit, integration, API acceptance and functional tests;
- Use an integration testCase class when performing integration tests;
- Unit tests must be small, easy to understand and fast (less then a minute per test);
- Mark functional tests with
@group functional
; - Use descriptive assertions
- Don’t use PHPUnit to run performance suites, use PHPBench for performance suites;
Use mutation testing find to missing tests.
Caution
Mutation Testing should not be used on functional or acceptance tests but on unit and integration tests only.
Infrastructure details which heavily dependent on external factors (that cannot be replicated) may be tested with a smoke test.
Unlike a unit test, a smoke test covers a big part of the code and tests it as a whole. When there is an error (“smoke”), this indicates a failed test. Otherwise, we can assume our code works as expected.
A smoke test usually doesn’t perform any assertions but runs the code and uses the code’s own error reporting as sign of failure.
Avoid over-testing, which is best explained by the following article from Mathias Verraes;
Figuring out how much unit tests you need to write, can be tricky, especially if you are new to Test-Driven Development.
Some teams strive for 100% code coverage. Some open source projects even announce their test coverage on their GitHub profiles – as if coverage is an indicator of quality.
Coverage only measures the lines of code that are executed by the test suite. It doesn’t tell you whether the outcome of the execution is actually tested, let alone how valuable that test is.
Mathias Verraes 2014 - http://verraes.net/2014/12/how-much-testing-is-too-much/
Tests become a problem when:
- they are slow;
- they need to be changed all the time;
- they break often;
- they are hard to read;
- … or they bother you in some other way;
When any of those occur, the tests need to be inspected. Now is the time to decide whether you want to refactor the test itself, or refactor the code under test, or, in some cases, remove the tests.
Low-value tests are usually harmless. There’s no urgent need to decide upfront whether they need to be deleted. Trust your instinct, or in this case, your annoyance level.
This is not a hard line, but crossing this line may have repercussions! (it most likely will)
Imagine code that mocks a third party library. After a particular upgrade of a third library, the logic might change a bit, but the test suite will execute just fine, because it’s mocked. So later on, thinking everything is good to go, the build-wall is green after all, the software is deployed and... Boom
It may be a sign that the current design is not decoupled enough from this third party library.
Also another issue is that the third party lib might be complex and require a lot of mocks to even work properly. That leads to overly specified tests and complex fixtures, which in itself compromises the compact and readable goal.
Or to tests which do not cover the code enough, because of the complexity to mock the external system.
Instead, the most common way is to create wrappers around the external lib/system, though you should be aware of the risk of abstraction leakage, where too much low level API, concepts or exceptions, goes beyond the boundary of the wrapper.
In order to verify integration with the third party library, write integration tests, and make them as compact and readable as possible as well.
Other people have already written on the matter and experienced pain when mocking a type they didn’t own:
- http://davesquared.net/2011/04/dont-mock-types-you-dont-own.html
- http://www.markhneedham.com/blog/2009/12/13/tdd-only-mock-types-you-own
- http://blog.8thlight.com/eric-smith/2011/10/27/thats-not-yours.html
- http://stackoverflow.com/questions/1906344/should-you-only-mock-types-you-own
If everything is mocked, are you really testing the production code? Don’t hesitate to not mock!
Business logic or domain logic is the part of the program that encodes the real-world business rules that determine how data can be created, displayed, stored, and changed.
In practice Business logic includes (but is not limited) to ValueObjects, AggregateRoot/Entity, Domain messages, event objects and data Models.
In most cases it should not be possible in the first place to mock these objects as they are marked final.
Why shouldn’t you mock this logic? Because its not an interface! Business logic describes some very specific rules about the application, logic that must (not should) be followed strictly!
If it’s too difficult to create new fixtures, it is a sign the code may need some serious refactoring. An alternative is to create builders for your value objects. You can also create meaningful factory methods in the test.
Originally based on: https://github.com/mockito/mockito/wiki/How-to-write-good-tests
A test ensures something is possible with the subject, it “can do” or “does something”. It does not describe “what” a subject does or is “described” to do.
Name your tests like you name your methods: short, descriptive and explicit.
Tip
A sentences with “and” or “then” could an indication the test is doing to much.
- Avoid using articles: “the”, “a” “an”, “then”;
- Prefer using “when” instead of “if”;
In unit tests the test-class itself always corresponds to the class that is being tested (the subject under the test).
Prefer using the it
notation for a test name.
Note
Because there is no hard contract (test does not describe what the subject does),
it’s acceptable to use “should” like ShouldReadColorsWhenFalseInConfigurationFile
.
Some examples on how to compose a unit test name:
[property] can be [actioned]
;it [throws, renders, connects, etc.] when [condition] [in, is] [expected condition result]
;[subject property/information] is [perform expected. like: read correctly, written correctly]
it can be [actioned] [to, with, from, in, etc] [object]
;
A ValueObject or Entity should use the `it` notation:
it [actions] [property]
;it will throw when [condition]
;its a [type name]
;
Final examples:
ConfigurationTest:
Listener Configuration is read correctly
;
MoneyTest:
its amount can be retrieved
;its currency can be retrieved
;it allows another money object with the same currency
;it can subtract another money object with same currency
;it can be negated
;it can be multiplied by a factor
;it can be allocated to number of targets
;it can be allocated by ratios
;it can be compared to another money object with same currency
;
DateTimeTypeTest:
it can be created
;its ViewTimezone can be transformed to ModelTimezone
;its should fail transformation for invalid input
;it can configure time pattern
(alternative:its time pattern is configurable
);its pattern can be configured
;
UserIdTest:
its an identity
;it is convertible to a string
;it is comparable to another object
;
This document is composed from information provided by external sources, if any credits are missing please update this document by opening a pull request, thank you.
PHP Coding Standards¶
When contributing code to Park-Manager, you must follow its coding standards.
Note
The coding standards described here only apply to the server-side, namely the PHP code.
The Front-end follows a different set of standards, which you can find in Front-end Coding Standards
The Park-Manager Coding Standard is a set of rules for PHP-CS-Fixer and PHP_CodeSniffer. It is based on PSR-1 and PSR-2, with some noticeable exceptions/differences/extensions.
- Keep the nesting of control structures per method as small as possible;
- Use strict object calisthenics when possible;
- Use parentheses when creating new instances that do not require arguments
$foo = new Foo()
; - Use Null Coalesce Operator
$foo = $bar ?? $baz
; - Prefer early exit over nesting conditions or using
else
; - Abstract exception class names and exception interface names should be suffixed with
Exception
; - Interfaces must not be suffixed with
Interface
; - Concrete exception class names should not be suffixed with
Exception
; - Use clear sentence constructs for exceptions
TransformationFailed
,UsernameIsAlreadyTaken
,TypeNotAccepted
, etc; - Align chained method calls over multiple lines;
- Add spaces around a concatenation operator
$foo = 'Hello ' . 'World!';
; - Add spaces between assignment, control and return statements;
- Add spaces after a negation operator
if (! $cond)
; - Add spaces after a type cast
$foo = (int) '12345';
; - Use apostrophes for enclosing strings (
'
); - Use camelCase, not underscores, for variable, function and method names and arguments;
- Use underscores for option names and parameter names;
- Always use strict comparisons;
- Always add
declare(strict_types=1)
at the beginning of a file; - Always add native types where possible;
- Always chop down method calls that exceed 120 characters (don’t wrap);
- Omit phpDoc for parameters/returns with native types, unless adding description;
- Don’t use
@author
,@since
and similar annotations that duplicate Git information; - Don’t wrap definitions (class/interface/trait/function and closures);
- Don’t Align equals (
=
) signs in assignments; - Assignment in condition is not allowed.
Tip
You can check your code for Park-Manager coding standard by running the following command:
$ make cs-check
Some of the violations can be automatically fixed by running:
$ make cs
Declare private functions below their first usage.
Write methods in a step-down approach (like an execution stack), first the “main”
function (like __construct
) followed by the method(s) that are called by this
function. Note that if these methods call other functions these come directly
after this method, and then continue with the private methods of the main
function.
function __constructor
calls parseSchema
calls parseArgumentStrings
private function parseSchema
calls parseSchemaElement
private function parseSchemaElement
calls validateSchemaElementId
private function validateSchemaElementId
// (notice that this comes after the previous private methods)
private function parseArgumentStrings
// Continue with the other methods (public first)
The exceptions to this rule are the setUp()
and tearDown()
methods
of PHPUnit tests, which must always be the first methods to increase
readability;
Park-Manager is released under the Mozilla Public License Version 2.0 license, and the license block has to be present at the top of every PHP file, before the namespace.
<?php
declare(strict_types=1);
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace ParkManager;
Tip
Use the class name as service-id for private and and tagged services. Public services should only use developer friendly names as described below.
- A service name contains groups, separated by dots;
- All Park-Manager services use
park_manager
as first group; - Use lowercase letters for service and parameter names;
- A group name uses the underscore notation;
- A route name contains groups, separated by dots;
- All Park-Manager routes use
park_manager
as first group, the module name (except for core) as second group, and optionally the section as third; - The last group always revers to the action (either
ftp_user_list
orftp_user_register
); - Use lowercase letters for names;
- A group name uses the underscore notation;
Examples:
park_manager.security_login
park_manager.security_confirm_password_reset
park_manager.webhosting.client.account_list
park_manager.webhosting.client.ftp_user_list
Front-end Coding Standards¶
Here’s a short example containing most features described below:
{% short_method_call_that_fits_on_one_line(arguments) %}
{% link_to(
some_object_with_a_long_name.title,
parent_object_child_object_path(some_object_with_a_long_name)
) %}
- Follow the Twig Coding Standards;
- Add Form buttons in the templates, not in the form classes or the action;
- Don’t perform complex logic in the template; use an extension, or better compute the value as part of the ViewModel.
- When wrapping long lines, keep the method name on the same line of the
interpolation operator
{%
and keep each method argument on its own line; - Do not add a license block in Twig template, all templates are provided under the same license as the project itself (MPL v. 2.0.);
Security Conventions¶
Security is the most important quality criteria of a hosting platform.
In short: When one website gets hacked it’s a problem. When the hosting platform gets hacked, it’s everyone’s problem!
The goal of this document is to prevent known problems, and to enforce proper practices. This is not a single solution to every possible problem.
Park-Manager’s infrastructure is powered by the Symfony framework which already takes care of a number of concerns including CSRF tokens, user Authentication and authorization, logging, and input sanitation. And are therefor not listed in detail here.
Note
Security is a moving target. New exploits and attack vectors are discovered almost everyday. Be sure to follow keep yourself up-to-date with the latest developments.
Be sure to follow the excellent Paragon Initiative Enterprises Blog for new developments and security best practices.
Please set aside most of what you’ve heard over the years; chances are, most of it just muddies the water. Security is not a product. Security is not a checklist. Security is not an absolute.
Security is a process. Security is an emergent property of a mature mindset in the face of risk.
Perfect security is not possible, but attackers do have budgets. If you raise the cost of attacking a system (your application or the networking infrastructure it depends on) so high that the entities that would be interested in defeating your security are incredibly unlikely to succeed, you’ll be incredibly unlikely to be compromised.
Even better, the most effective ways to raise the cost of attack against your system don’t significantly increase the cost of using the system legitimately. This is no accident; as Avi Douglen says, “Security at the expense of usability, comes at the expense of security.”
If your goal is to write secure (PHP) applications:
- You generally don’t need to know who the attacker is.
- You generally don’t need to know why they want to break in.
Conversely:
- You should know what attacks are possible.
- You should know where attacks should come from.
- If an incident does occur, you should know when and how it happened, so you can prevent it (and similar attacks) from happening again.
Your system might not be the end-game, especially if the attacker is sophisticated. Always look to improve. Security is a process. It’s not a destination.
Please don’t report security issues publicly, but follow the procedure described in Reporting a Security Issue.
Park-Manager does not only contain user information, but also any information the users store (including emails and personal information about their users).
To prevent disclosure or corruption any access requires proper authorization.
- Authentication information is stored in a database (SQL, LDAP, NoSQL).
- Access to the database is restricted to the application and system administrators.
- To prevent access from the outside a Firewall must be implemented by the sysadmin.
- Passwords are stored in accordance with the [password storage requirements].
- Sensitive data (home address, TLS private keys, etc) is stored encrypted.
- Encryption is performed with either symmetric (single key) or asymmetric cryptography (public/key pair).
- For searching encrypted data see: Indexing encrypted data.
- Storage of TLS private keys for hosting is done using asymmetric cryptography,
only the
configuration-update
daemon can decrypt the private keys.
TLS private keys (decrypted for usage) are stored under the root system-user, with strict system ACL/chmod applied (only the root system-user can read them).
Access to email storage is restricted to the email platform (Postfix/Exim, Dovecot) and must not be readable by hosting users or from the application platform directly.
Database access from the email platform is read-only trough custom DB functions, the functions validate access and update the audit logs.
Functions are executed with ``SECURITY DEFINER`` - access right of the function owner.
Note
First of Make sure you understand why something is (in)secure. Don’t blindly implement something because an expert told you too.
Requirements:
- Prevent data from corrupting the instructions that operate on it.
- Validate and sanitize user input;
- Guard against information injection (SQL, XPath, Regex injections, etc);
- Disallow NUL bytes in non-binary data;
Be explicit and comprehensive with the application logic;
Assume that all code that is not tested is vulnerable to attacks;
Keep dependencies and software up to date and don’t rely on abandoned components;
Don’t write your own cryptography;
Disable content sniffing (for Internet Explorer), explicitly provide a
Content-Type
;Set
X-Download-Options: noopen
(for Internet Explorer) to force a download, rather then executing it within the providing website’s context;Employ the principle of least privilege;
Enable TLS (whenever possible) with strong ciphers, see also TLS/SSL requirements;
Use only (revokable) authorization tokens for third-party API communication. No shared username/passwords allowed.
Contact the service provider when is this not (yet) supported.
For a Symfony (powered) application use the NelmioSecurityBundle.
See also: security-guide-for-developers for a complete (and up-to-date) list of recommendations.
Set all configuration to be secure by default, use strong encryption ciphers and disable weak encryption protocols. Require strong passwords by default.
Allow the implementor to weaken security, but provide proper warnings with what will happen if they lower the security settings.
Input provided by either a web form, REST API, client-side cookie, file upload processing, HTML5 storage, or response processing from an outgoing API request.
Anything that is provided/communicated by someone or something not part of the trusted information system (database, system cache, local filesystem).
- Don’t trust any received information blindly, validate and sanitize.
- Enforce strict rules about which values and formats are allowed;
- Either “a username must not contain special characters like
!#%&+<>?
”; - Restrict long values, unless this is required;
- Reject unsupported content-types;
- Validate the received data conform the expected content format;
- Do strict type checking, check something is a string rather than that something is an array;
- Use a safe-list, not a forbidden-list for characters, formats and accepted values.
- Either “a username must not contain special characters like
- Guard against known attack like XML entity expansion, XML Injection or JSON hash table collision.
- Don’t process user-input values trough PHP’s
unserialize
function! Use save serialization with XML, CVS or JSON. - Disallow deep nesting of data structures, restrict depth.
- Use
XMLReader
to prevent reading to much data in memory. - Use the
$depth
parameter for json_decode
- Use
Normally this is not needed, but to prevent leaking sensitive memory data in a core-dump or process exploit it is a good practice to “remove” (zero out) sensitive data from memory once it’s no longer used:
if (!\password_verify($password, $record['password'])) {
// The $password is invalid, the but $record['password'] string
// should be removed from memory.
sodium_memzero($record['password']);
throw new AuthenticationError('Invalid username or password.');
}
// The $password is valid, so remove both from memory.
sodium_memzero($password);
sodium_memzero($record['password']);
This includes but is not limited to: (plain) password strings, token strings, encryption keys, credit-card/social-security numbers. Any data that when leaked will have a big impact on a users security.
But keep the following in mind:
- Use only
sodium_memzero
or a low-level C implementation; Don’t simpleunset
or set\0
on the value, this is not enough! - Only remove data that is actually sensitive, as this operation
will overwrite the memory segment with
NUL
data for the length. - This method does not actually release the memory space, use
unset
for that. - Don’t remove invalid information, a token that doesn’t produce a valid result is properly bogus input and can be ignored. Else you may risk a DoS attack for large strings.
Not properly escaping dynamic output (like user input) can lead to various security issues. Where Cross-site scripting (XSS) is the most common.
The only proper way to prevent this is context-aware escaping. Escape a string for usage in HTML (using ``htmlspecialchars()``).
But instead of manually escaping all dynamic output, it’s better to use a template system that already does this for you.
Twig provides a powerful auto escaping system, that solve 99% of the escaping problems we all encounter, it’s therefor the default template-engine of Symfony and Park-Manager.
Caution
HTML attributes may not always be properly escaped, be sure to use
the escape filter
with the proper context-type html_attr
for attributes.
Always test if you are not sure escaping is done properly. Never disable auto-escaping application globally!
For content systems it’s advised to use special mark-up languages, like Markdown or UBB.
If user-provided HTML must be supported, run it trough HTML Purifier (before putting it in storage).
First of, try to limit the communication with the Command line (or shell execution). The hosting platform should be as environment agnostic as possible.
Never use the back-tick operator `` ` `` for executing commands.
Never directly use the PHP command-line functions (popen, proc_open, exec, etc), use the Symfony Process component to safely execute command-line operations.
Use
Process
with an array to safely compose a command-line operation.Only use
Process
(with a string) for commands that never change or require special operations like> some-data
.Make sure to properly escape any command and arguments used in
Process
. UseSymfony\Component\Process\ProcessUtils::escapeArgument()
instead ofescapeshellarg
;
Note
Don’t directly communicate with the operating system (OS) to create a new system user, update storage quota, or perform any root-user operation.
Use the System ServiceBus instead.
See also: Command Injection - OWASP
Cryptography is a really really complex subject, there a number of things you must take care of (forget one, and the whole Cryptography system is broken).
Confidentiality: The ability to prevent eavesdroppers from discovering the plaintext message, or information about the plaintext message (either hamming weight).
Integrity: The ability to prevent an active attacker from modifying the message without the legitimate users noticing.
This is usually provided via a Message Integrity Code (MIC).
Authenticity: The ability to prove that a message was generated by a particular party, and prevent forgery of new messages.
This is usually provided via a Message Authentication Code (MAC). Note that authenticity automatically implies integrity.
Don’t write your own cryptography.
Tip
PHP (since version 7.2) comes with pre-bundled support for Libsodium, a powerful and easy to use crypto library for developers.
Halite provided by Paragon Initiative Enterprises is the preferred way of using Libsodium. It provides a number of extra’s to strengthen Libsodium’s already powerful crypto engine.
But using Libsodium directly is also allowed.
Caution
Don’t use a password as encryption key, use the library’s provided key derivation functions (to protect against password cracking).
use ParagonIE\HiddenString\HiddenString;
use ParagonIE\Halite\KeyFactory;
$passwd = new HiddenString('correct horse battery staple');
// Use random_bytes(16); to generate the salt:
$salt = "\xdd\x7b\x1e\x38\x75\x9f\x72\x86\x0a\xe9\xc8\x58\xf6\x16\x0d\x3b";
$encryptionKey = KeyFactory::deriveEncryptionKey($passwd, $salt);
See also: Using Encryption and Authentication Correctly
Note
All direct calls to Libsodium or other crypto related functions must be use
“root-namespace” directive \password_verify
not password_verify
.
Encrypting and decrypting data slows down the system, and provides another challenge when working with expectations for results.
Caution
Disabling encryption during tests introduces the risk of improper results during actual usage. Don’t use mocked calls to the crypto engine.
Use the actual encrypted data for tests, and provide tempered data for failure tests.
- Mark the (unit) test as
@group slow
to speed-up the overall testing suite; - If encryption or cryptographic hashing is detail of the tested class, use a Faked encryption implementation. But always clearly state this implementation is insecure and for testing only!
TLS must be enabled by default.
Tools must not allow to disable TLS! Inform the user when something goes wrong, and provide a link to a manual with more instructions.
Don’t add options like
--insecure
, they only weaken the security and don’t solve the actual problem.Hosting operations like: email, FTP and web access may allow to disable TLS, but must actively discourage this practice.
Use strong ciphers https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_Ciphersuite
Always link to a trusted source for recommended ciphers and configuration, don’t hardcode them or keep them in the local documentation only (this gets outdated easily).
Refuse the acceptance of expired or revoked certificates and keys.
Enable peer-certificate verification.
Disable compression (to prevent against BREACH and CRIME attacks).
Use https://github.com/paragonie/certainty to ensure an up-to-date CA list.
Tip
Thanks to LetsEncrypt and other free alternatives, LTS/SSL is now more accessible then ever.
These principles only apply the HTTP protocol over TLS or HTTP2.
- Enable HTTP Strict Transport Security (HSTS).
- Disallow mixed content, all content must be is served over TLS.
- Don’t Recommend Certificate and Public Key Pinning (until all concerns with this technique are resolved).
- Don’t write your own cryptography (including a hashing algorithm).
- Guard against a Collision attack, use modern hashing techniques like sha256 or better.
- Sha1 is allowed for low collision cache data like a Redis or Memcache storage key.
- Use time-safe string comparison for cryptographic hashes (including passwords and checksum);
- In PHP use hash_equals().
- Use the double-hmac-strategy for languages that don’t have a time safe string comparison method.
REST endpoints are an easy target for attacks, the OWASP has published an REST Security Cheat Sheet with best practices and recommendations.
Any REST implementation MUST follow these recommendations.
The Api-platform is the recommended framework for realizing a secure REST functionality. It follows the OWASP REST best practices as explained in: https://github.com/api-platform/core/blob/master/features/security/README.md
One popular attack method is uploading malicious files to a server. Uploading could be provided for either support-ticket attachments, or batch processing.
Batch processing is a topic of it’s own, ensure only accepted file formats are passed trough, before processing.
The first rule of secure file uploading is: don’t allow anything that is not usable to the context. Don’t allow uploading of image files for a XML document processor.
And don’t trust what the user provides, the file’s content is whats true. Filenames can be changed and user-provided mime-types can be spoofed.
Don’t allow file uploading for unauthenticated users.
- Don’t expose the internal storage location and filename to the user.
- Unless the file is to be provided as-is (like a custom theme); Use a very strict validation for unprotected file storage!
Encourage anti-virus scanning when possible (requires an scanner gateway).
Always force a download, don’t allow opening in the browser.
- Safe-list accepted mime-types (no forbidden-list).
- Disallow any executable file including: exe, bat, cmd, reg, dmg.
- Archive files may be allowed as they provide a boundary before execution.
- Favor a write/read-only filesystem (no execution possible).
- This requires a proper configuration by the sysadmin.
Get the actual mime-type based on the file-content.
Don’t use
getimagesize
to check if the file is a (save) image.You can upload a valid JPEG image and still hide a malicious payload in its EXIF comments. Consider stripping EXIF data all together.
The other problem with file uploads is, well, downloading them safely.
- SVG images, when accessed directly, will execute JavaScript code in the user’s browser. This is true despite the misleading image/ prefix in the MIME type.
- MIME type sniffing can lead to type confusion attacks, as discussed previously. See X-Content-Type-Options.
- If you forego the previous advice about how to store uploaded files safely, an attacker that manages to upload a .php or .phtml file may be able to execute arbitrary code by accessing the file directly in their browser, thereby giving them complete control over the server. Play it safe.
- Check for proper authorization to: upload and view/download the file.
- Disallow caching by a Proxy or ensure authentication is checked.
- Use a short cache lifetime, either 3 minutes to prevent DoS attacks.
- Caching allows to restart quickly when the connection got lost.
This excludes direct-public facing uploads, like custom themes.
Storage must prevent direct access from users, as this could lead to disclosure of information or execution of uploaded files. Only the application should be able to read the file contents.
This is a process, bailout directly when a condition does not pass.
- Validate all security conditions are correct.
- Validate the file is actually uploaded.
- Check user is authorized to upload, with the correct attributes (no uploading attachments for ticket the user doesn’t have access to).
- Check file-extension (initial validation).
- Check actual mime-type based on the file-content (strict validation, PHP already provides the correct type).
- Optionally scan the file for viruses or malicious content.
- Generate a unique id for the upload (either UUID), used to reference the upload.
- Store the uploaded file under a unique-name (truly random, not UUID).
- Store the unique file-name in the database, it must never be exposed publicly.
- Store the: original filename (sanitized), actual mime-type, file size (KiB) and sha256-checksum in the database.
- Only use the UUID to referencing the file publicly.
_Optionally file encryption can be considered, but this will also slows down the download process, and thus should only be done for sensitive data._
Caution
Please be extra cautious in this section, SQL injections are a major risk. And introducing them in today’s times is unacceptable.
In short, DON’T EVER USE ``addslashes``!. Use the correct driver provided escaping functions. Don’t build your own escaper.
Restricting which characters may be used in a value, DOES NOT prevent against SQL injections.
- Use proper escaping (Again. DON’T EVER USE
addslashes
). - Use prepared statements for SQL (see notes below).
- Avoid using dynamic SQL in db user-defined functions, be sure to use proper escaping when they are used.
- Always provide the required/allowed owner-ids when performing a search operation; Prevent returning of unauthorized results, during in the querying.
- Use role access separation for the various applications and background services (the email platform, web application, configuration-update daemon all have there own access/authentication role with explicit granting).
- Revoke all access to database objects from a role (except for the owner), and grant explicitly when access is needed.
- Ensure only the installer/upgrader, and system administrators can change the structure of the database. World accessible applications (like the web application) must not be able to change the structure and database configuration.
Note
Note on prepared statements:
Be sure when using PDO to disable PDO::ATTR_EMULATE_PREPARES
as this is known
to cause security problems. Only really old versions of the MySQL client required this.
The RollerworksSearch system doesn’t use prepared statements for SQL/DQL, but instead ensures a proper escaping of the value. This is an exceptional case.
Elasticsearch works with JSON to query the index for matching documents.
- Disallow to inject a structure directly from the outside, require explicit
building of the structure. Use a RollerworksSearch
SearchCondition
instead. - Don’t generate the JSON structure using string concatenation.
- Protect the Elasticsearch installation from direct unauthorized access.
First of: Don’t modify the password (except for trimming surrounding whitespace), don’t remove any special characters or do case normalizing.
And don’t forbid usage of specific characters. Including emoji’s.
Note
Limit passwords to 120 characters max, to prevent DoS attacks.
120 characters equals to roughly 1024 bit’s of data. Trying to crack this will take more time then the average life time of a password.
- Passwords should have a limited lifetime, keep the last-modification date-time of a password separate from the “main” last-modification date-time.
- Require the user to provide a new password when it’s expired.
- Don’t allow the user to do anything else before the password is updated.
- When passwords can expire, don’t allow the re-usage of old passwords.
- To improve the UX, allow the user to “show” the password filled in the field. Provide a warning to the user about the risks.
- Don’t refill a password field when the form contained errors.
- Don’t refill a password field when modifying existing data, allow the field to be empty. And only validate when a value is provided.
- Show the a strength indication of the password, and fail when it’s below a required minimum.
- Require the user to provide the current password when changing the password.
- Don’t write your own password cryptography.
- Don’t store the password, store a cryptographic hash of the password.
- Use bcrypt or when available use Halite/Argon2 (from libsodium). Keep the password-hash encryption key separate from the database.
- Don’t use PBKDF2 for authentication, this algorithm was designed for key derivation, which can then be used as a cryptographic key.
- Use constant-time comparison (see Hash generating and comparison).
- Never store a password in a readable format or encrypted (except for Halite Password).
- Check (after a successful authentication) if the password needs to be rehashed. And update it immediately.
When a user forgets a password, there is only one option: reset the password. But as you have guessed, the user can’t do this without a password.
Caution
This bears emphasis: When you give your users the capability to reset their password, you are creating a backdoor into their account.
For all security recommendations please see:
https://paragonie.com/blog/2016/09/untangling-forget-me-knot-secure-account-recovery-made-simple
Note
Security questions aren’t a good idea, they should only be used to harden the verification process (prevent sending unwanted reset emails).
No matter which technique is used (email, SMS, postal code) it’s important to first verify the reset is requested by the user and not by an attacker.
Even when the attacker is not able to intercept the reset link. The user is bothered with unwanted emails, or SMS messages (at 2:00 AM).
To prevent this from happening the following requirements must be followed:
- Only allow to send a new reset per user, every n minutes/hours (either 10 minutes).
- Log every reset request (with IP address).
- Block access when to many attempts are made within a period (say 5 attempts from the same IP every 10 minutes).
- Ask some information only the user knows (either a security question, see notes below).
- Require to solve a (re)CAPTCHA after 3 failed attempts.
Once the identity of the user is verified continue with the next step.
- Email (traditional method):
- Email message with a link to provide a one-time login.
- Token link can only be used once, once visited the token expires within 5 minutes.
- Token expires after 20 minutes.
- Allow to configure a GnuPG public key for encryption.
Keep the following in mind during the reset:
- Security token/code must be cryptographically random.
- Check IP of the reset request equals the IP during the actual reset.
- Use a cryptographically random token and a separate auth-code (see untangling-forget-me-knot for details); Thread auth-code as a password, it must be properly hashed.
- Don’t give an indication about the existence of the user
(unless proper answers were provided or access is blocked for the visitor):
- ‘No such user with this email address or wrong security questions provided.’ (not recommended, but better then nothing);
- ‘To many attempts from this IP address, please try again later.’;
- ‘An email has been send, please follow the instructions provided.’;
- ‘An text message has been send to your mobile phone, please type the received recover-code below.’;
Once the user is authenticated do the following:
- Require to provide a new password. Don’t allow the user to do anything else before the password is updated;
- Once the password is updated, inform the user by email, the password was reset;
- Regenerate the session-id (and destroy the old one);
- Expire all active sessions;
See also: Forgot Password Cheat Sheet
Note
The Park-Manager user system doesn’t perform an actual authentication during a reset operation. After the password is successfully reset the user needs to login with the new password.
Any security questions or identity information presented to users to reset forgotten passwords should ideally have the following four characteristics:
- Memorable: If users can’t remember their answers to their security questions, you have achieved nothing.
- Consistent: The user’s answers should not change over time. For instance, asking “What is the name of your significant other?” may have a different answer 5 years from now.
- Nearly universal: The security questions should apply to a wide an audience as possible.
- Safe: The answers to security questions should not be something that is easily guessed, or researched (either, something that is matter of public record).
Requirements:
- Use at least three separate questions.
- Normalize casing and spaces (only remove leading and pending space characters).
- Be explicit about the question and format, provide an example for formats ‘either January 1900’.
- Cryptographically hash the answers, they may contain sensitive information (see Hash generating and comparison).
- Allow one custom question and answer (user’s choice to use or not).
- Don’t hardcode the list, but let the Administrator configure a list manually.
- Allow to configure constraints for an answer (either only numbers, pattern, etc.)
- Allow localization support, also allow localized constraints (hh:mm vs hh:mm [am|pm]).
- Allow to mark a question as “removed”, will only be shown during a reset. But cannot be used anymore.
See also:
Examples:
- What was the house number and street name you lived in as a child?
- What were the last four digits of your childhood telephone number?
- What primary school did you attend?
- In what town or city was your first full time job?
- In what town or city did you meet your spouse/partner?
- What is the middle name of your oldest child?
- What are the last five digits of your driver’s license number?
- What is your grandmother’s (on your mother’s side) maiden name?
- What is your spouse or partner’s mother’s maiden name?
- In what town or city did your mother and father meet?
- What time of the day were you born? (hh:mm)
- What time of the day was your first child born? (hh:mm)
- What is your oldest sibling’s birthday month and year? (either, January 1900)
Park-Manager License¶
Park-Manager is released under the Mozilla Public License Version 2.0.
According to Wikipedia:
“The Mozilla Public License (MPL) is a free and open source software license developed and maintained by the Mozilla Foundation. It is a weak copyleft license, characterized as a middle ground between permissive free software licenses and the GNU General Public License (GPL), that seeks to balance the concerns of proprietary and open source developers.”
Caution
The Mozilla Public License is a permissive license but includes some important clauses about redistribution.
When you “encrypt” or compile the source code as a combined work for distribution please be sure to fully understand the legal implications and rights enforced by the license.
DO NOT contact a Core member to ask for an exclusive license! Park-Manager is a free software system that is not owned by single person or company.
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
Standards¶
When contributing to Park-Manager, you must follow its (code) standards.
Documentation Standards¶
In order to help the reader as much as possible and to create code examples that look and feel familiar, you should follow these standards.
- The following characters are chosen for different heading levels: level 1
is
=
, level 2-
, level 3~
, level 4.
and level 5"
; - Each line should break approximately after the first word that crosses the 72nd character (so most lines end up being 72-78 characters);
- The
::
shorthand is preferred over.. code-block:: php
to begin a PHP code block (read the Sphinx documentation to see when you should use the shorthand); - Inline hyperlinks are not used. Separate the link and their target definition, which you add on the bottom of the page;
- Inline markup should be closed on the same line as the open-string;
Example
=======
When you are working on the docs, you should follow the
`Park-Manager Documentation`_ standards.
Level 2
-------
A PHP example would be::
echo 'Hello World';
Level 3
~~~~~~~
.. code-block:: php
echo 'You cannot use the :: shortcut here';
.. _`Park-Manager Documentation`: http://docs.park-manager.com/en/latest/contributing/documentation/standards.html
- The code follows the
Park-Manager Coding Standards
as well as the Twig Coding Standards; - To avoid horizontal scrolling on code blocks, we prefer to break a line correctly if it crosses the 85th character, which in many IDEs is signalised by a vertical line;
- When you fold one or more lines of code, place
...
in a comment at the point of the fold. These comments are:
// ... (php),
# ... (yaml/bash),
{# ... #} (twig)
<!-- ... --> (xml/html),
; ... (ini),
... (text)
- When you fold a part of a line, e.g. a variable value, put
...
(without comment) at the place of the fold; - Description of the folded code: (optional)
If you fold several lines: the description of the fold can be placed after the
...
If you fold only part of a line: the description can be placed before the line; - If useful to the reader, a PHP code example should start with the namespace declaration;
- When referencing classes, be sure to show the
use
statements at the top of your code block. You don’t need to show alluse
statements in every example, just show what is actually being used in the code block; - If useful, a
codeblock
should begin with a comment containing the filename of the file in the code block. Don’t place a blank line after this comment, unless the next line is also a comment; - You should put a
$
in front of every terminal line.
Configuration examples should show recommended formats using configuration blocks. The recommended formats (and their orders) are:
- Services: PHP-DSL
- Configuration (including routing): YAML
- Validation: XML
- Doctrine Mapping: XML
// src/Foo/Bar.php
namespace Foo;
use Acme\Demo\Cat;
// ...
class Bar
{
// ...
public function foo($bar)
{
// set foo with a value of bar
$foo = ...;
$cat = new Cat($foo);
// ... check if $bar has the correct value
return $cat->baz($bar, ...);
}
}
Caution
In YAML you should put a space after {
and before }
(e.g. { _controller: ... }
),
but this should not be done in Twig (e.g. {'hello' : 'value'}
).
For sections, use the following capitalization rules: Capitalization of the first word, and all other words, except for closed-class words:
The Vitamins are in my Fresh California Raisins
Please use appropriate, informative, rather formal language;
Do not place any kind of advertisements in the documentation;
The documentation should be neutral, without judgements, opinions. Make sure you do not favor anyone, our community is great as a whole, there is no need to point who is better than the rest of us;
You should use a form of you instead of we (either avoid the first person point of view: use the second instead);
When referencing a hypothetical person, such as “a user with a session cookie”, gender-neutral pronouns (they/their/them) should be used. For example, instead of:
- he or she, use they
- him or her, use them
- his or her, use their
- his or hers, use theirs
- himself or herself, use themselves
Contributing Documentation¶
Contributing Documentation¶
Contributing to the Documentation¶
The documentation is as important as the code. It follows the exact same principles: DRY, tests, ease of maintenance, extensibility, optimization, and refactoring just to name a few. And of course, documentation has bugs, typos, hard to read tutorials, and many more.
Before contributing, you need to become familiar with the markup language used by the documentation.
The Park-Manager documentation is hosted on GitHub, in the main repository:
https://github.com/park-manager/park-manager
If you want to submit a patch, fork the official repository on GitHub and then clone your fork to you local destination:
$ git clone git@github.com:YOUR_USERNAME/park-manager.git
Under the name origin
you will have from now on the access to your fork.
Add also the main repository as the upstream
remote.
$ git remote add upstream git@github.com:park-manager/park-manager.git
See also Submitting a Patch
Note
Remember to name your commits descriptively, keep them possibly small, with just unitary changes (such that you change something only in one part of the docs, not everywhere).
When you’re done, push this branch to your GitHub fork and initiate a pull request.
Your pull request will be reviewed, you will be asked to apply fixes if necessary and then it will be merged into the main repository.
To test the documentation before a commit, make sure Docker is running:
$ make doc
- In the
docs
directory view the generated HTML files in thebuild
directory.
GitHub covers the topic of pull requests in detail.
Note
The Park-Manager documentation is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Warning
You should always prefix the PR name with a [Documentation]
tag!
And your commits with doc
as the <type>
.
You can prefix the title of your pull request in a few cases:
[WIP]
(Work in Progress) is used when you are not yet finished with your pull request, but you would like it to be reviewed. The pull request won’t be merged until you say it is ready.[BundleName]
when you add documentation of the Park-Manager Modules.[API]
when you are contributing docs tothe API guide
.
For instance if your pull request is about documentation of some feature of the Core module,
but it is still a work in progress it should look like: [WIP][Documentation][Core] Arbitrary feature documentation
.
If you’re documenting a brand new feature or a change that’s been made in
Park-Manager, you should precede your description of the change with a
.. versionadded:: 1.X
tag and a short description:
.. versionadded:: 1.1
The ``getProductDiscount`` method was introduced in Park-Manager v1.1.
All documentation in the Park-Manager Documentation should follow the documentation standards.
The easiest contributions you can make is reporting issues: a typo, a grammar mistake, a bug in a code example, a missing explanation, and so on.
Steps:
- Submit a new issue in the GitHub tracker;
- (optional) Submit a patch.
Documentation Format¶
The Park-Manager documentation uses the reStructuredText as its markup language and Sphinx for building the output (HTML, PDF, ...).
reStructuredText “is an easy-to-read, what-you-see-is-what-you-get plaintext markup syntax and parser system”.
You can learn more about its syntax by reading existing Park-Manager documents or by reading the reStructuredText Primer on the Sphinx website.
If you are familiar with Markdown, be careful as things are sometimes very similar but different:
- Lists starts at the beginning of a line (no indentation is allowed);
- Inline code blocks use double-ticks (
``like this``
).
Sphinx is a build system that adds some nice tools to create documentation from the reStructuredText documents. As such, it adds new directives and interpreted text roles to standard reST markup.
All code examples uses PHP as the default highlighted language. You can change
it with the code-block
directive:
.. code-block:: yaml
{ foo: bar, bar: { foo: bar, bar: baz } }
If your PHP code begins with <?php
, then you need to use html+php
as
the highlighted pseudo-language:
.. code-block:: html+php
<?php echo $this->foobar(); ?>
Note
A list of supported languages is available on the Pygments website.
Whenever you show a configuration, you must use the configuration-block
directive to show the configuration in all supported configuration formats
(PHP
, YAML
, and XML
)
.. configuration-block::
.. code-block:: yaml
# Configuration in YAML
.. code-block:: xml
<!-- Configuration in XML //-->
.. code-block:: php
// Configuration in PHP
The previous reST snippet renders as follow:
- YAML
# Configuration in YAML
- XML
<!-- Configuration in XML //-->
- PHP
// Configuration in PHP
The current list of supported formats are the following:
Markup format | Displayed |
---|---|
html | HTML |
xml | XML |
php | PHP |
yaml | YAML |
json | JSON |
jinja | Twig |
html+jinja | Twig |
html+php | PHP |
ini | INI |
php-annotations | Annotations |
To add links to other pages in the documents use the following syntax:
:doc:`/path/to/page`
Using the path and filename of the page without the extension, for example:
:doc:`/architecture/index`
:doc:`/modules/webhosting/installation`
The link’s text will be the main heading of the document linked to. You can also specify an alternative text for the link:
:doc:`Webhosting Module </modules/webhosting/installation>`
You can also link to pages outside of the documentation, for instance to the Github.
`Github`_ //it is an intext link.
At the bottom of the document in which you are using your link add a reference:
.. _`Github`: http://www.github.com // with a url to your desired destination.
Park-Manager Documentation License¶
The Park-Manager documentation is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
You are free:
- to Share — to copy, distribute and transmit the work;
- to Remix — to adapt the work.
Under the following conditions:
- Attribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work);
- Share Alike — If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one.
With the understanding that:
Waiver — Any of the above conditions can be waived if you get permission from the copyright holder;
Public Domain — Where the work or any of its elements is in the public domain under applicable law, that status is in no way affected by the license;
Other Rights — In no way are any of the following rights affected by the license:
- Your fair dealing or fair use rights, or other applicable copyright exceptions and limitations;
- The author’s moral rights;
- Rights other persons may have either in the work itself or in how the work is used, such as publicity or privacy rights.
Notice — For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page.
This is a human-readable summary of the Legal Code (the full license).
Contributing Translations¶
Contributing Translations¶
Always use identification keys for translations instead of content strings;
Translations keys should always describe their purpose and not their location.
A transformation for a form label “Username”, would be named
label.username
, notedit_form.label.username
;An error message should give a good indication of the reason,
password_not_strong_enough
notpassword_invalid
;Follow the Translation Best practices;
Reuse translation keys when possible, use only section specif keys when they depend on context, like
profile.edit.title
;Use Intl translator format for better flexibility;
Keep translations per logical domain:
validations
: holds the translation for validator violation messages;navigation
: holds the translations for navigation items, like the navigation menu and breadcrumbs;search
: holds the translations for search-field labels;messages
: holds translations with no specific domain;
Community¶
Community¶
Respectful Review Comments¶
Reviewing issues and pull requests is a great way to get started with contributing to the Park-Manager community. Anyone can do it! But before you give a comment, take a step back and think, is what you are about to say actually what you intend?
Communicating over the Internet with nothing but text can pose a big challenge, especially if you remember that the Park-Manager community is world-wide and is composed of a wide variety of people with differing ideas and opinions.
Not everyone speaks English or is able to use a keyboard. Some might have dyslexia or similar conditions that affect their writing.
Not to mention that some might have a bad experience from previous contributions (to other projects).
You’re not alone in this. This guide will try to help you write constructive, respectful and helpful reviews and replies.
Tip
This guide is not about lecturing you to “conform” or give-up your ideas and opinions but helping you to better communicate, prevent possible confusion, and keeping the Park-Manager community a welcoming place for everyone. You are free to disagree with someone’s opinions, but don’t be disrespectful.
First of, accept that many programming decisions are opinions. Discuss trade offs, which you prefer, and reach a resolution quickly. It’s not about being right or wrong, but using what works.
We don’t expect you to be completely formal, or to even write error-free English. Just remember this: don’t swear, and be respectful to others.
Don’t reply in anger or with an aggressive tone. You’re angry, we understand that, but swearing/cursing and name calling doesn’t really encourage anyone to help you. Take a deep breath, count to 10 and try to clearly explain what problems you encounter.
In an effort to be inclusive to a wide group of people, it’s recommended to use personal pronouns that don’t suggest a particular gender. Unless someone has stated their pronouns, use “they”, “them” instead of “he”, “she”, “his”, “hers”, “his/hers”, “he/she”, etc.
Try to avoid using wording that may be considered excluding, needlessly gendered (e.g. words that have a male or female base), racially motivated or singles out a particular group in society. For example, it’s recommended to use words like “folks”, “team”, “everyone” instead of “guys”, “ladies”, “yanks”, etc.
While reviewing issues and pull requests you may run into some suggestions (including patches) that don’t reflect your ideas, are not good, or downright wrong.
Now, when you prepare your comment, consider the amount of work and time the author has spent on their idea and how your response would make them feel.
Did you correctly understand their intention? Or are you making assumptions? Whatever your response, be explicit. Remember people don’t always understand your intentions online.
Avoid using terms that could be seen as referring to personal traits (“dumb”, “stupid”). Assume everyone is intelligent and well-meaning.
Tip
Good questions avoid judgement and avoid assumptions about the author’s perspective.
Maybe you can ask for clarification? Suggest an alternative? Or provide a simple explanation why you disagree with their proposal.
This looks wrong. Are you sure it's correct?
(eg. typo/syntax error)What do you think of "RequestFactory" instead of RequestCreator?
Even if something is really wrong or “a bad idea”, stay respectful and don’t get into endless you-are-wrong discussions or “flame wars”.
Don’t use hyperbole (“always”, “never”, “endlessly”, “nothing”, “worst”, “horrible”, “terrible”).
Don’t: “I don’t like how you wrote this code” - there is no clear explanation why you don’t like how it’s written.
Better: “I find it hard to read this code as there many nested if statements, can you make it more readable? By encapsulating some of it’s details or maybe adding some comments to explain the overall logic.” - You explain why you find the code hard to read and give some suggestions for improvement.
If a piece of code is in fact wrong, explain why:
This code doesn't comply with Park-Manager's CS rules. Please see [...] for details
.I think the code is less readable now
- careful here, be sure explain why you think the code is less readable, and maybe give some suggestions?
Examples of valid reasons to reject:
- We tried that in the past (link to the relevant PR) but we needed to revert it for XXX reason.
- That change would introduce too many performance problems. In the past we’ve always rejected changes like this.
- I profiled this change and it hurts performance significantly (if you don’t profile, it’s an opinion, so we can ignore)
- Code doesn’t match Park-Manager’s CS rules (e.g. use
[]
instead ofarray()
)- We only provide integration with very popular projects (e.g. we integrate Bootstrap but not your own CSS framework)
- This would require adding lots of code and making lots of changes for a feature that doesn’t look so important. That could hurt maintaining in the future.
Rarely something is perfect from the start, while the code itself is good. It may not be optimal or conform the Park-Manager coding style.
Again, understand the author already spent time on the issue and asking for (small) changes may be misinterpreted or seen as a personal attack.
Be thankful for their work (so far), stay positive and really help them to make the contribution a great one. Especially if they are a first time contributor.
Use words like “Please”, “Thank you” and “Could you” instead of making demands;
- “Thank you for your work so far. I left some suggestions for improvement to make the code more readable.”
- “Your code contains some coding-style problems, can you fix these before we merge? Thank you”
- “Please use 4 spaces instead of tabs”, “This needs be on the previous line”;
During a pull request review you can usually leave more then one comment, you don’t have to use “Please” all the time. But it wouldn’t hurt.
It may not seem like much, but saying “Thank you” does make others feel more welcome.
In short: Extreme misbehavior will not be tolerated and may even get you banned; Keep it real and friendly.
Don’t use sarcasm for a serious topic, that’s not something that belongs
to the Park-Manager community. And don’t marginalize someone’s problems;
Well I guess that's not supposed to happen? 😆
.
Even if someone’s explanation is “inviting to joke about it”, it’s a real problem to them. Making jokes about this doesn’t help with solving their problem and only makes them feel stupid. Instead try to discover what the problem is really about.
Don’t feel bad if you “failed” to follow these tips. As long as your intentions were good and you didn’t really offend or insult anyone; you can explain you misunderstood, you didn’t mean to marginalize or simply failed.
But don’t say it “just because”, if your apology is not really meant you will lose credibility and respect from other developers.
Do unto others as you would have them do unto you.
Community Reviews¶
Park-Manager is an open-source project driven by a large community. If you don’t feel ready to contribute code or patches, reviewing issues and pull requests (PRs) can be a great start to get involved and give back. In fact, people who “triage” issues are the backbone to Park-Manager’s success!
Community reviews are essential for the development of the Park-Manager framework, since there are many more pull requests and bug reports than there are members in the Park-Manager core team to review, fix and merge them.
On the Park-Manager issue tracker, you can find many items in a Needs Review status:
- Bug Reports: Bug reports need to be checked for completeness. Is any important information missing? Can the bug be easily reproduced?
- Pull Requests: Pull requests contain code that fixes a bug or implements new functionality. Reviews of pull requests ensure that they are implemented properly, are covered by test cases, don’t introduce new bugs and maintain backward compatibility.
Note that anyone who has some basic familiarity with Park-Manager and PHP can review bug reports and pull requests. You don’t need to be an expert to help.
Before you begin, remember that you are looking at the result of someone else’s hard work. A good review comment thanks the contributor for their work, identifies what was done well, identifies what should be improved and suggests a next step.
Park-Manager uses GitHub to manage bug reports and pull requests. If you want to do reviews, you need to create a GitHub account and log in.
A good way to get started with reviewing is to pick a bug report from the bug reports in need of review.
The steps for the review are:
Is the Report Complete?
Good bug reports contain a link to a fork of the Park-Manager Standard Edition (the “reproduction project”) that reproduces the bug. If it doesn’t, the report should at least contain enough information and code samples to reproduce the bug.
Reproduce the Bug
Download the reproduction project and test whether the bug can be reproduced on your system. If the reporter did not provide a reproduction project, create one by forking the Park-Manager Standard Edition.
Update the Issue Status
At last, add a comment to the bug report. Thank the reporter for reporting the bug. Include the line
Status: <status>
in your comment to trigger our Carson Bot which updates the status label of the issue. You can set the status to one of the following:Needs Work If the bug does not contain enough information to be reproduced, explain what information is missing and move the report to this status.
Works for me If the bug report does contain enough information to be reproduced but works on your system, or if the reported bug is a feature and not a bug, provide a short explanation and move the report to this status.
Reviewed If you can reproduce the bug, move the report to this status. If you created a reproduction project, include the link to the project in your comment.
Example
Here is a sample comment for a bug report that could be reproduced:
Thank you @weaverryan for creating this bug report! This indeed looks
like a bug. I reproduced the bug in the "kernel-bug" branch of
https://github.com/webmozart/park-manager-standard.
Status: Reviewed
The process for reviewing pull requests (PRs) is similar to the one for bug reports. Reviews of pull requests usually take a little longer since you need to understand the functionality that has been fixed or added and find out whether the implementation is complete.
It is okay to do partial reviews! If you do a partial review, comment how far you got and leave the PR in “Needs Review” state.
Pick a pull request from the PRs in need of review and follow these steps:
Is the PR Complete?
Every pull request must contain a header that gives some basic information about the PR. You can find the template for that header in the Contribution Guidelines.
Is the Base Branch Correct?
GitHub displays the branch that a PR is based on below the title of the pull request. Is that branch correct?
- Bug fixes and new features must always target the master branch. Only exceptional fixes should target a maintenance branch.
Reproduce the Problem
Read the issue that the pull request is supposed to fix. Reproduce the problem on a clean Park-Manager Standard Edition project and try to understand why it exists. If the linked issue already contains such a project, install it and run it on your system.
Review the Code
Read the code of the pull request and check it against some common criteria:
- Does the code address the issue the PR is intended to fix/implement?
- Does the PR stay within scope to address only that issue?
- Does the PR contain automated tests? Do those tests cover all relevant edge cases?
- Does the PR contain sufficient comments to easily understand its code?
- Does the code break backward compatibility? If yes, does the PR header say so?
- Does the PR contain deprecations? If yes, does the PR header say so? Does
the code contain
trigger_error()
statements for all deprecated features? - Are all deprecations and backward compatibility breaks documented in the latest UPGRADE-X.X.md file? Do those explanations contain “Before”/”After” examples with clear upgrade instructions?
Note
Eventually, some of these aspects will be checked automatically.
Test the Code
Take your project from step 3 and test whether the PR works properly. Replace the Park-Manager project in the
vendor
directory by the code in the PR by running the following Git commands. Insert the PR ID (that’s the number after the#
in the PR title) for the<ID>
placeholders:$ cd vendor/park-manager/park-manager $ git fetch origin pull/<ID>/head:pr<ID> $ git checkout pr<ID>
For example:
$ git fetch origin pull/15723/head:pr15723 $ git checkout pr15723
Now you can
test the project
against the code in the PR.Update the PR Status
At last, add a comment to the PR. Thank the contributor for working on the PR. Include the line
Status: <status>
in your comment to trigger our Carson Bot which updates the status label of the issue. You can set the status to one of the following:Needs Work If the PR is not yet ready to be merged, explain the issues that you found and move it to this status.
Reviewed If the PR satisfies all the checks above, move it to this status. A core contributor will soon look at the PR and decide whether it can be merged or needs further work.
Example
Here is a sample comment for a PR that is not yet ready for merge:
Thank you @weaverryan for working on this! It seems that your test
cases don't cover the cases when the counter is zero or smaller.
Could you please add some tests for that?
Status: Needs Work
Organization¶
Park-Manager is developed by a vibrant community of commercial companies and individual developers. This chapter describes the rules & processes we use to organize our work.
Organization¶
Vision & Strategy¶
Vision & strategy is defined by the Project Leader, Core Team and Community members.
If you would like to suggest a new tool, process, feel free to submit a PR to this section of the documentation.
Be sure to motivate your suggestion.
Note
Feel free to use external articles to motivate your proposal, however don’t simple post website link(s) with no additional information.
Suggestions with no helpful information and no feedback are automatically closed after a reasonable period of time.
We use GitHub as the main tool to organize the community work and everything that happens around Park-Manager. Releases, bug reports, feature requests, roadmap management, etc. happens on this platform.
If you are not sure about your issue, please use the Park-Manager Slack to discuss it with the fellow community members before opening it on GitHub.
We use milestones to mark the lowest branch an issue or a PR applies to.
For example, if a bug report is marked with 1.0 milestone, the related bugfix PR should be opened against 1.0 branch. Then, after this PR is merged, it would be released in the next 1.0.x release.
Learn more about the The Release Process.
Before any PR is merged, the following things need to be confirmed:
- Changes can be included in the upcoming release.
- PR has been approved by at least 1 fellow Core Team member.
- PR adheres to the PR template and contains the MPL v. 2.0 license.
- PR includes relevant documentation updates.
- PR contains appropriate UPGRADE file updates if necessary.
- PR is properly labeled and milestone is assigned if needed.
- All required checks are passing. It is green!
Certain PRs can only be merged by the Project Lead:
- BC Breaks;
- Introducing new modules or high level architecture layers;
- Renaming existing components;
- If in doubt, ask your friendly neighborhood Project Lead;
A PR must only be merged using Hubkit, Hubkit ensures Changelog generation works as expected and keeps all relevant information bundled with the Git merge-commit itself.
Anyone is free to vote on a proposed feature.
Tip
Don’t work on implementing a feature request or idea until there enough positive votes, rather focus on features that are accepted and can be implemented now.
Voting on a feature request is done by either giving a review approval
for a pull request or giving a Thumb up
emoji-reaction (not a reply).
A negative vote on the suggestion must always have a clear (and respectful) explanation why you think this is not a good idea.
Don’t give a “Thumb down” emoji-reaction when you like the idea but have some concerns about the issue.
Note
While everyone is free to vote on a feature request, the proposed feature must fit within the vision of the Park-Manager project.
If at least 30% of the Core Team gives a negative vote, the feature request is rejected. You cannot vote your own proposal.
Park-Manager Core Team¶
The Park-Manager Core Team is the group of developers that determine the direction and evolution of the Park-Manager project. Their votes rule if the features and patches proposed by the community are approved or rejected.
All the Park-Manager Core Team members are long-time contributors with solid technical expertise and they have demonstrated a strong commitment to drive the project forward.
This document states the rules that govern the Park-Manager Core Team. These rules are effective upon publication of this document and all Park-Manager Core Team members must adhere to said rules and protocol.
Park-Manager Core Team members have the following roles:
- Project Leader
- Defines business vision & strategy
- Elects Park-Manager Core Team Members
- Lead Developer
- Defines technical vision & strategy
- Coordinates community
- Connects with other projects
- Documentation Lead
- Coordinates the work related to the documentation
- Core Developer
- Can review & merge all code PRs and issues
- Focuses on a specific area of the system
- Project Leader:
- Sebastiaan Stok (sstok)
- Lead Developer:
- N/A
- Documentation Lead:
- N/A
- Core Developers:
- N/A
At present, new Park-Manager Core Team membership applications are not accepted, although we are in the process of inviting new members.
A Park-Manager Core Team membership can be revoked for any of the following reasons:
- Refusal to follow the rules and policies stated in this document;
- Lack of activity for the past six months;
- Willful negligence or intent to harm the Park-Manager project;
- Upon decision of the Project Leader.
Should new Park-Manager Core Team memberships be accepted in the future, revoked members must wait at least 6 months before re-applying.
The rules described in this document may be amended at anytime by the Project Leader.
The Release Process¶
This document explains the process followed by the Park-Manager project to develop, release and maintain its different versions.
Note
Until a stable v1.0.0 version is released this document is not in-effect yet. Any BC breakage results in a new minor version release and directly fully discontinues all previous versions (no patch releases are provided for older versions).
Park-Manager releases follow the semantic versioning strategy and they are published through a time-based model:
- A new Park-Manager minor version (for example 1.1, 1.2, etc.) comes out every month
- A new Park-Manager major version (for example 2.0, 3.0) comes out every two years (around the same time Symfony releases a new major version) and it’s released at the same time as the last minor version of the previous major version;
- A new Park-Manager patch version (for example 1.24.1, 1.24.2, etc.) comes out every month, only for the last maintained minor version;
The full development period for any minor version lasts one month, unless there to many critical issues. When there are to many critical issues with a new feature the release might be delayed with two a weeks maximum, or the offending feature might be reverted until the next scheduled release.
During the development period, any new feature can be reverted if it won’t be finished in time or if it won’t be stable enough to be included in the coming release.
Each Park-Manager major version is maintained for a fixed period of time. This maintenance is divided into:
- Bug fixes and security fixes: During this period, being three months long, all issues can be fixed. The end of this period is referenced as being the end of maintenance of a release.
- Security fixes only: During this period, being six months long, only security related issues can be fixed. The end of this period is referenced as being the end of life of a release.
- A minor version is maintained until the release of the next minor version, the release of version 1.2 directly discontinues maintenance of version 1.1;
All Park-Manager releases have to comply with our Backward Compatibility Promise.
Whenever keeping backward compatibility is not possible, the feature, the enhancement or the bug fix will be scheduled for the next major version.
This release process was adopted to give more predictability and transparency. It was inspired based on the following goals:
- Shorten the release cycle (allow users to benefit from the new features faster);
- Improve the experience of Park-Manager core contributors: everyone knows when a feature might be available in Park-Manager;
- Give companies a strict and predictable timeline they can rely on to plan their upgrades.
The one month period was chosen to allow features to be shipped almost instantly, while gathering early feedback from experimental features.
It also allows for plenty of time to work on new features and it allows for non-ready features to be postponed to the next version without having to wait too long for the next cycle.
Maintenance Rules¶
This document explains the rules for maintaining the Park-Manager project (that is the
code & documentation hosted on the main park-manager/park-manager
Git repository).
These rules are specific to Core Team Members as listed in Park-Manager Core Team.
- Configure your global Git to sign all commits (and tags);
- Don’t commit changes directly to a main repository branches, for example don’t push to the master branch directly. Use pull requests instead;
- Don’t create new release unless being asked by the Project Leader;
- Only merge pull requests using HubKit, and follow the Pull Request Checklist
- Don’t change GitHub repository labels (https://github.com/park-manager/park-manager/labels) and topics unless being asked by a Project Leader or Lead Developer;
- Don’t close an issue/pull request unless a proper reason was given;
- Don’t lock and issue/pr unless it violates the Code of Conduct or is a security patch provided by the Core Team;
Note
In exception to rule nr. 2 small CS/spelling fixes to a recently merged pull request are allowed but should be avoided.
Any functional changes must always be provided with a pull request.
New releases are coordinated by the Project Project Leader. Core Members handling a release are revered to as Release Managers.
Note
Issues and pull request are managed using milestones, when a release
is created for a specific milestone (for example 1.2
) all open pull request
and issues must be moved to the next possible milestone (for example 1.3
).
Before any release is created, the following things need to be confirmed:
- The correct branch is checked-out;
- A patch release contains only bug fixes and no new features;
- The UPGRADE instructions are up-to-date and properly formatted;
- The release title doesn’t violate any registered trademarks and has not been used previously.
Releases are created using HubKit which ensures:
- The Git tag is signed;
- A GitHub release page is created (with a proper changelog);
- Split repositories are synchronized;
- Version numbers are continuous;
To create a new release simply run hubkit release
followed
by the version number or patch
/minor
/major
respectively.
$ hubkit release minor
This process might take some time as all split repositories are updated and tagged, existing tags in split repositories are automatically ignored.
Credits¶
This section is based on the great Symfony documentation and borrows some parts from the Sylius project.