diff --git a/.github/workflows/deploy-gh-pages.yml b/.github/workflows/deploy-gh-pages.yml new file mode 100644 index 0000000..edfaacb --- /dev/null +++ b/.github/workflows/deploy-gh-pages.yml @@ -0,0 +1,44 @@ +name: Build and Deploy to GitHub Pages + +defaults: + run: + shell: bash + working-directory: ./docs + +on: + push: + paths: + - 'docs/**' + +jobs: + deploy: + name: Build and Deploy to GitHub Pages + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: ./docs + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: yarn + cache-dependency-path: docs/yarn.lock + - name: Install dependencies + run: yarn install --frozen-lockfile + - name: Build website + run: yarn build + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # Build output to publish to the `gh-pages` branch: + publish_dir: ./docs/build + # The following lines assign commit authorship to the official + # GH-Actions bot for deploys to `gh-pages` branch: + # https://github.com/actions/checkout/issues/13#issuecomment-724415212 + # The GH actions bot is used by default if you didn't specify the two fields. + # You can swap them out with your own user credentials. + user_name: github-actions[bot] + user_email: 41898282+github-actions[bot]@users.noreply.github.com diff --git a/.github/workflows/test-deploy-gh-pages.yml b/.github/workflows/test-deploy-gh-pages.yml new file mode 100644 index 0000000..7f95853 --- /dev/null +++ b/.github/workflows/test-deploy-gh-pages.yml @@ -0,0 +1,29 @@ +name: Build GitHub Pages on PRs No Deploy + +defaults: + run: + shell: bash + working-directory: ./docs + +on: + pull_request: + paths: + - 'docs/**' + # Review gh actions docs if you want to further define triggers, paths, etc + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on + +jobs: + test-deploy: + name: Build GitHub Pages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: yarn + cache-dependency-path: docs/yarn.lock + - name: Install dependencies + run: yarn install --frozen-lockfile + - name: Test build website + run: yarn build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a43a581 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# IDE +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c6c2c2 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Website + +This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. + +### Installation + +``` +$ yarn +``` + +### Local Development + +``` +$ yarn start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + +### Build + +``` +$ yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +### Deployment + +Using SSH: + +``` +$ USE_SSH=true yarn deploy +``` + +Not using SSH: + +``` +$ GIT_USER= yarn deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..e00595d --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/blog/2022-08-02-welcome.md b/blog/2022-08-02-welcome.md new file mode 100644 index 0000000..37be8b0 --- /dev/null +++ b/blog/2022-08-02-welcome.md @@ -0,0 +1,10 @@ +--- +slug: welcome +title: Welcome +authors: [ericr] +tags: [laminas, PHP] +--- +Welcome to the new documentation website for the LM-Commons organization. + +This site is work in progress and the intent is obviously to keep it current with updates to the LM-Commons packages. + diff --git a/blog/2022-10-24-lmcbootstrapmenu-v1.0.0.md b/blog/2022-10-24-lmcbootstrapmenu-v1.0.0.md new file mode 100644 index 0000000..d70791e --- /dev/null +++ b/blog/2022-10-24-lmcbootstrapmenu-v1.0.0.md @@ -0,0 +1,11 @@ +--- +slug: lmcbootstrapmenu-release-v1.0.0 +title: LmcBootstrapMenu v1.0.0. is available +authors: [ericr] +tags: [laminas, PHP, bootstrap] +--- +LmcBootstrapMenu is a new LM-Commons package that provides a Bootstrap menu helper. + +The repository is available on GitHub [LM-Commons/LmcBootstrapMenu](https://github.com/LM-Commons/LmcBootstrapMenu). + +It is also available on Packagist via Composer. Please see installation instructions in the `README.md` file in the GitHub repository. \ No newline at end of file diff --git a/blog/authors.yml b/blog/authors.yml new file mode 100644 index 0000000..490e13a --- /dev/null +++ b/blog/authors.yml @@ -0,0 +1,5 @@ +ericr: + name: Eric Richer + title: LM-Commons Admin + url: https://github.com/visto9259 + image_url: https://github.com/visto9259.png diff --git a/docs/LmcRbacMvc.md b/docs/LmcRbacMvc.md new file mode 100644 index 0000000..63d16c7 --- /dev/null +++ b/docs/LmcRbacMvc.md @@ -0,0 +1,6 @@ +--- +sidebar_position: 2 +--- +LmcRbacMvc is a role-based access control Laminas MVC module to provide additional features on top of Laminas\Permissions\Rbac + +[Documentation](https://lm-commons.github.io/lmcrbacmvc) \ No newline at end of file diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 0000000..0fb4993 --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,41 @@ +--- +sidebar_position: 1 +--- + +# Introduction + +LM-Commons is a GitHub organization dedicated to the collaborative +and community-driven long-term maintenance of packages & libraries based on the Laminas MVC and Components. + +Many of the packages found in the LM-Commons repositories are former ZF-Commons packages +that were migrated to the Laminas Project framework. + +## LmcUser +LmcUser is a generic user registration and authentication module for Laminas. Supports Laminas\Db and Doctrine2. + +## LmcRbacMvc +LmcRbacMvc is a role-based access control Laminas MVC module to provide additional features on top of Laminas\Permissions\Rbac + +## LmcRbac + +LmcRbac is a role-based access control module to provide additional features on top of Laminas\Permissions\Rbac (formerly zfc-rbac) + +## LmcCors +LmcCors is a simple Laminas MVC module that helps you to deal with Cross-Origin Resource Sharing (CORS). + +## LmcUserDoctrineORM +LmcUserDoctrineORM is a Doctrine2 ORM storage adapter for LmcUser. + +## LmcAdmin +LmcAdmin is an admin interface for Laminas MVC framework. + +## LmcMail +LmcMail is Wrapper for Laminas Mail that uses View Model to compose the email body. + +##### Notices and Disclaimers +This is not an official Laminas Project organization. + +Issues and questions related to the Laminas MVC and components +should be addressed to the Laminas Project organisation. + +Laminas is a trademark of the Laminas Project, a Series of LF Projects, LLC. diff --git a/docs/lmc-admin/01-introduction.md b/docs/lmc-admin/01-introduction.md new file mode 100644 index 0000000..1d67939 --- /dev/null +++ b/docs/lmc-admin/01-introduction.md @@ -0,0 +1,27 @@ +# Introduction +> +> This is work in progress to convert to Laminas +> +# LmcAdmin Module for Laminas Framework +Created by [Jurian Sluiman](http://juriansluiman.nl) and [Martin Shwalbe](https://github.com/Hounddog). + +## Introduction +LmcAdmin is a minimal admin interface for generic administrative purposes. It is a common screen with navigation that hides behind authentication and authorization. + +## Installation +LmcAdmin is enabled to be installed via composer. Load `lm-commons/lmc-admin` in your `composer.json` file. You can specify its version (currently only 1.0.0 is recommended) or use `dev-master` to load the latest version from master. Enable LmcAdmin in your `module.config.php` configuration file. + +If you do not want to use composer, clone this project (either as a git submodule or not) into `./vendor/` directory. + +## Usage +LmcAdmin allows you to create routes under a single parent "admin" route. You can also use it to enable navigation in your admin layout. Furthermore integration of [LmcRbacMvc](https://github.com/LM-Commons/LmcRbacMvc) is provided. + +The complete configuration is flexible, so you can update the zfcadmin parent route, its children, the navigation and all default provided view scripts. Read more in the [documentation](docs/lmc-admin/01-introduction.md) about usage and customization of LmcAdmin. + +## Development +LmcAdmin is currently under development. The authors feel LmcAdmin is stable enough for production versions and you can always fix your LmcAdmin version to a specific tag. + +## Support + +- File issues at https://github.com/LM-Commons/LmcAdmin/issues. +- Ask questions in the [LM-Commons gitter](https://gitter.im/LM-Commons/community) chat. \ No newline at end of file diff --git a/docs/lmc-admin/_category_.json b/docs/lmc-admin/_category_.json new file mode 100644 index 0000000..144590a --- /dev/null +++ b/docs/lmc-admin/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "LmcAdmin", + "position": 5, + "link": { + "type": "generated-index", + "description": "Admin interface for Laminas MVC framework." + } +} diff --git a/docs/lmc-cors/01-introduction.md b/docs/lmc-cors/01-introduction.md new file mode 100644 index 0000000..5522163 --- /dev/null +++ b/docs/lmc-cors/01-introduction.md @@ -0,0 +1,24 @@ +# Introduction +LmcCors is a simple Laminas MVC module that helps you to deal with Cross-Origin Resource Sharing (CORS). + +It allows to easily configure your Laminas MVC application so that it automatically builds HTTP responses that follow the CORS documentation. + +## What is CORS ? + +CORS is a mechanism that allows to perform cross-origin requests from your browser. + +For instance, let's say that your website is hosted in the domain `http://example.com`. +By default, user agents won't be allowed to perform AJAX requests to another domain for security +reasons (for instance `http://funny-domain.com`). + +With CORS, you can allow your server to reply to such requests. + +You can find better documentation on how CORS works on the web: + +* [Mozilla documentation about CORS](https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS) +* [CORS server flowchart](http://www.html5rocks.com/static/images/cors_server_flowchart.png) + +## Support + +- File issues at https://github.com/LM-Commons/LmcCors/issues. +- Ask questions in the [LM-Commons gitter](https://gitter.im/Lm-Commons/community) chat. diff --git a/docs/lmc-cors/02-installation.md b/docs/lmc-cors/02-installation.md new file mode 100644 index 0000000..c640358 --- /dev/null +++ b/docs/lmc-cors/02-installation.md @@ -0,0 +1,12 @@ +# Installation +Install the module by typing (or add it to your `composer.json` file): + +```sh +$ php composer.phar require lm-commons/lmc-cors +``` + +Then, enable it by adding "LmcCors" in your `application.config.php` or `modules.config.php` file. + +By default, LmcCors is configured to deny every CORS requests. To change that, you need to copy +the [`config/lmc_cors.global.php.dist`](https://github.com/LM-Commons/LmcCors/blob/master/config/lmc_cors.global.php.dist) file to your `autoload` folder +(remove the `.dist` extension), and modify it to suit your needs. diff --git a/docs/lmc-cors/03-guide.md b/docs/lmc-cors/03-guide.md new file mode 100644 index 0000000..dde41ec --- /dev/null +++ b/docs/lmc-cors/03-guide.md @@ -0,0 +1,143 @@ +# Guide + +## Event registration + +LmcCors registers the `LmcCors\Mvc\CorsRequestListener` with the `MvcEvent::EVENT_ROUTE` event, with a priority +of -1. This means that this listener is executed AFTER the route has been matched. + +## Configuring the module + +As by default, all the various options are set globally for all routes: + +- `allowed_origins`: (array) List of allowed origins. To allow any origin, you can use the wildcard (`*`) character. If + multiple origins are specified, LmcCors will automatically check the `"Origin"` header's value, and only return the + allowed domain (if any) in the `"Allow-Access-Control-Origin"` response header. To allow any sub-domain, you can prefix + the domain with the wildcard character (i.e. `*.example.com`). Please note that you don't need to + add your host URI (so if your website is hosted as "example.com", "example.com" is automatically allowed. +- `allowed_methods`: (array) List of allowed HTTP methods. Those methods will be returned for the preflight request to + indicate which methods are allowed to the user agent. You can even specify custom HTTP verbs. +- `allowed_headers`: (array) List of allowed headers that will be returned for the preflight request. This indicates + to the user agent which headers are permitted to be sent when doing the actual request. +- `max_age`: (int) Maximum age (seconds) the preflight request should be cached by the user agent. This prevents the + user agent from sending a preflight request for each request. +- `exposed_headers`: (array) List of response headers that are allowed to be read in the user agent. Please note that + some browsers do not implement this feature correctly. +- `allowed_credentials`: (boolean) If true, it allows the browser to send cookies along with the request. + +If you want to configure specific routes, you can add `ZfrCors\Options\CorsOptions::ROUTE_PARAM` to your route configuration: + +```php + [ + 'allowed_origins' => ['*'], + 'allowed_methods' => ['GET', 'POST', 'DELETE'], + ], + 'router' => [ + 'routes' => [ + 'readOnlyRoute' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/foo/bar', + 'defaults' => [ + // This will replace allowed_methods configuration to only allow GET requests + // and only allow a specific origin instead of the wildcard origin + LmcCors\Options\CorsOptions::ROUTE_PARAM => [ + 'allowed_origins' => ['http://example.org'], + 'allowed_methods' => ['GET'], + ], + ], + ], + ], + 'someAjaxCalls' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/ajax', + 'defaults' => [ + // This overrides the wildcard origin + LmcCors\Options\CorsOptions::ROUTE_PARAM => [ + 'allowed_origins' => ['http://example.org'], + ], + ], + ], + 'may_terminate' => false, + 'child_routes' => [ + 'blog' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/blogpost', + 'defaults' => [ + // This would only allow `http://example.org` to GET this route + \LmcCors\Options\CorsOptions::ROUTE_PARAM => [ + 'allowed_methods' => ['GET'], + ], + ], + ], + 'may_terminate' => true, + 'child_routes' => [ + 'delete' => [ + 'type' => 'segment', + 'options' => [ + 'route' => ':id', + // This would only allow origin `http://example.org` to apply DELETE on this route + 'defaults' => [ + \LmcCors\Options\CorsOptions::ROUTE_PARAM => [ + 'allowed_methods' => ['DELETE'], + ], + ], + ], + ], + ], + ], + ], + ], + ], + ], +]; +``` + +## Preflight request + +If LmcCors detects a preflight CORS request, a new HTTP response will be created, and LmcCors will send the appropriate +headers according to your configuration. The response will always be sent with a 200 status code (OK). + +Please note that this will also prevent further MVC steps from being executed, since all subsequent MVC steps are +skipped till `Laminas\Mvc\MvcEvent::EVENT_FINISH`, which is responsible for actually sending the response. + +## Actual request + +When an actual request is made, LmcCors first checks it the origin is allowed. If it is not, then a new response with +a 403 status code (Forbidden) is created and sent. + +Please note that this will also prevent further MVC steps from being executed, since all subsequent MVC steps are +skipped till `Laminas\Mvc\MvcEvent::EVENT_FINISH`, which is responsible for actually sending the response. + +If the origin is allowed, LmcCors will just add the appropriate headers to the request produced by `Laminas\Mvc`. + +## Security concerns + +Don't use this module to secure your application! You must use a proper authorization module, like +[BjyAuthorize](https://github.com/bjyoungblood/BjyAuthorize), [LmcRbacMvc](https://github.com/LM-Commons/LmcRbacMvc) or +[SpiffyAuthorize](https://github.com/spiffyjr/spiffy-authorize). + +LmcCors only allows to accept or refuse a cross-origin request. + +## Custom schemes + +Internally, LmcCors uses `Laminas\Uri\UriFactory` class. If you are using custom schemes (for instance if you are +testing your API with some Google Chrome extensions), you need to add support for those schemes by adding them to +the `UriFactory` config (please [refer to the doc](https://docs.laminas.dev/laminas-uri/usage/#creating-a-new-custom-class-uri)). + +## Example +To register the `chrome-extension` custom scheme in your API, simply add: + +```php +use Laminas\Uri\UriFactory; + +UriFactory::registerScheme('chrome-extension', 'Laminas\Uri\Uri'); +``` + +to the `onBootstrap()` method in `module/Application/Module.php`. + +Registering the `chrome-extension` custom scheme like this allows you to use Google Chrome extensions for testing your API. \ No newline at end of file diff --git a/docs/lmc-cors/_category_.json b/docs/lmc-cors/_category_.json new file mode 100644 index 0000000..44197a5 --- /dev/null +++ b/docs/lmc-cors/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "LmcCors", + "position": 4, + "link": { + "type": "generated-index", + "description": "LmcCors is a simple Laminas MVC module that helps you to deal with Cross-Origin Resource Sharing (CORS)." + } +} diff --git a/docs/lmc-mail/Configuration.md b/docs/lmc-mail/Configuration.md new file mode 100644 index 0000000..4a0163b --- /dev/null +++ b/docs/lmc-mail/Configuration.md @@ -0,0 +1,44 @@ +--- +sidebar_position: 3 +--- +LmcMail supports the Laminas SMTP Mail Transport or the Laminas File Mail Transport and this is configured by the `transport` config key in the `lmcmail.local.php` file: + +```php title="/config/autoload/lmcmail.local.php" + [ + 'from' => [ + 'email' => 'user@example.com', + 'name' => 'User', + ], + + // For SMTP + 'transport' => [ + 'type' => 'smtp', + 'options' => [ + 'host' => 'example.com', + 'connection_class' => 'plain', + 'connection_config' => [ + 'ssl' => 'tls', + 'username' => 'user@example.com', + 'password' => 'somepassword', + ], + 'port' => 587, + ], + ] + // OR + + 'transport' => [ + 'type' => 'file', + 'options' => [ + 'path' => '/path/to/email/folder', + ], + ], + ], +]; +``` +In a development environment, it is typical to use a File Mail Transport. In a production environment, an SMTP Mail Transport will more likely be used. + +The `'transport'` configuration must comply with the `Laminas\Mail\Transport\Factory\Factory::create` method. + +The `'from'` configuration defines a default *from* address. The *from* address can also be specified at message creation. diff --git a/docs/lmc-mail/Installation.md b/docs/lmc-mail/Installation.md new file mode 100644 index 0000000..baac904 --- /dev/null +++ b/docs/lmc-mail/Installation.md @@ -0,0 +1,16 @@ +--- +sidebar_position: 2 +--- + +Install the module via Composer: + +````shell +$ composer require lm-commons/lmc-mail +```` + +Composer will inject the module into the modules configuration, or you can add it manually to the `modules.config.php` or +`application.config.php`. + +Customize the module by copying and renaming the sample configuration file `lm-commons/lmc-mail/config/lmcmail.local.php.dist` to the application's +`config/autoload`. + diff --git a/docs/lmc-mail/Introduction.md b/docs/lmc-mail/Introduction.md new file mode 100644 index 0000000..fb41b7e --- /dev/null +++ b/docs/lmc-mail/Introduction.md @@ -0,0 +1,12 @@ +--- +sidebar_position: 1 +--- + +LmcMail is an email service module that provides the ability to use the View renderer of a Laminas MVC application +and the installed View Helper plugins to render HTML emails. + + +## Requirements + +- PHP 8.1 or higher +- Laminas MVC diff --git a/docs/lmc-mail/Usage.md b/docs/lmc-mail/Usage.md new file mode 100644 index 0000000..c8214d5 --- /dev/null +++ b/docs/lmc-mail/Usage.md @@ -0,0 +1,73 @@ +--- +sidebar_position: 4 +--- +The Mail service can be retrieved from the service manager: + +```php +$messageService = $serviceManager->get(LmcMail\Service\MessageService::class); +``` + +Basic example to send an HTML email: + +````php +$viewModel = new \Laminas\View\Model\ViewModel(); +$viewModel->setTemplate('mail/html'); +$message = $messageService->createHtmlMessage( + ['email' => 'john@example.com', 'name' => 'John'], //from + ['email' => 'jane@example.com', 'name' => 'Jane'] //to + "This is the subject line, //subject + $viewModel); // View model + +$messageService->send($message); +```` + +The `'mail/html'` template must exist in the application's view template map. The HTML mail renderer will use +a layout template aliased as `'mail/layout'` in the view template map. This is defined in the `module.config.php` file. + +## Available methods + +### createHtmlMessage + +````php + /** + * Create an HTML message + * @param string|Address|AddressInterface|array|AddressList|Traversable $from + * @param string|Address|AddressInterface|array|AddressList|Traversable $to + * @param string $subject + * @param string|ModelInterface $nameOrModel + * @return Message + */ +createHtmlMessage(string|Address|AddressInterface|array|AddressList|Traversable $from, + string|Address|AddressInterface|array|AddressList|Traversable $to, + string $subject, + string|ModelInterface $nameOrModel): \Laminas\Mime\Message::class +```` +If `$nameorModel` is a string, it must correspond to the view template to use. + + +### createTextMessage +````php +/** + * Create a text message + * @param string|Address|AddressInterface|array|AddressList|Traversable $from + * @param string|Address|AddressInterface|array|AddressList|Traversable $to + * @param string $subject + * @param string|ModelInterface $nameOrModel + * @return Message + */ +createTextMessage(string|Address|AddressInterface|array|AddressList|Traversable $from, + string|Address|AddressInterface|array|AddressList|Traversable $to, + string $subject, + ModelInterface $nameOrModel): \Laminas\Mail\Message::class +```` +If `$nameorModel` is a string, it must correspond to the view template to use. + +### send +````php +/** + * Send the message + * @param Message $message + */ +send(Message $message): void +```` +where `$message` can be any object of type `\Laminas\Mail\Message` not necessarily one created by the above methods. diff --git a/docs/lmc-mail/_category_.json b/docs/lmc-mail/_category_.json new file mode 100644 index 0000000..ea43019 --- /dev/null +++ b/docs/lmc-mail/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "LmcMail", + "position": 6, + "link": { + "type": "generated-index", + "description": "Wrapper for Laminas Mail that uses View Model to compose the email body" + } +} diff --git a/docs/lmc-mail/advanced-customization.md b/docs/lmc-mail/advanced-customization.md new file mode 100644 index 0000000..04e7e1f --- /dev/null +++ b/docs/lmc-mail/advanced-customization.md @@ -0,0 +1,65 @@ +--- +sidebar_position: 5 +--- +LmcMail can be customized to the applications needs. + +#### Using view templates + +LmcMail uses nested view models to render the body of HTML messages. + +In a similar fashion to the view model structure of the Laminas MVC Skeleton, +the body is rendered using a layout view model to which the view model parameter (`$nameOrModel`) to the `createHtmlMessage` method is added a child. +The rendered output of the `$nameOrModel` view model is captured in the variable `message` which is passed to the layout view model. + +A default template `mail/layout` is supplied is `view/layout/layout.phtml`. This template can be the starting point for your own layout template. +The layout template can be set using the `setLayoutTemplate()` method. Alternatively, +the `mail/layout` entry in the View Manager template map can be overridden to point to your template. Another alternative is to use a factory delegator to the `MessageServiceFactory::class` to set the layout template after the Message Service is created. + +View Helpers can be used when rendering view models. A common use case is to use `$this->url()` to render a link to your application. + +#### Use alternate View Resolved and View Helper Manager + +LmcMail uses Service Manager aliases to get the View Resolver and View Helper Manager which resolves to the Laminas MVC resolver and manager. This allows to use any view template and helpers already defined in the application. + +````php +'aliases' => [ + // These aliases are used by the MailViewRendererFactory + // by default, they resolve to the Laminas MVC View Helper manager and Resolver + 'lmc_mail_view_helper_manager' => 'ViewHelperManager', + 'lmc_mail_view_resolver' => 'ViewResolver', +], +```` +If you want to use a different resolver and view helper manager, then update the aliases to point to your classes: + +````php +'aliases' => [ + 'lmc_mail_view_helper_manager' => 'MyHelperManager', + 'lmc_mail_view_resolver' => 'MyViewResolver', +], +```` + +If you want to use your own renderer, then you can override the Service Manager factory: +````php +'factories' => [ + // Override the factory with your own + 'lmc_mail_view_renderer' => MyViewRendererFactory::class, + /* ... */ +], +```` + +### Event Listening + +`MessageService::send()` triggers two events: + +- `MessageEvent::SEND` is triggered right before the message is sent by the transport service. +- `MessageEvent::SEND_POST` is triggered right after the message has been sent by the transport service. + +The listener to these events will receive an event of class`MessageEvent` that extends the `Event` class with: + +- A `$message` property containing the message. The message is also stored in an event parameter named 'message'. +- A `getMessage()` method to get the `$message` property. +- A `setMessage(Message $message)` method to set the `$message` property and the corresponding event parameters. + +The `MessageService::send()` method, after triggering the `MessageEvent::SEND` event, will retrieve the message from the event and pass it to the transport service. This allows for the listener to modify the message if needed. + +A typical use case for listening to the send events would be to log that a message was sent. diff --git a/docs/lmc-rbac/01-introduction.md b/docs/lmc-rbac/01-introduction.md new file mode 100644 index 0000000..34e1e08 --- /dev/null +++ b/docs/lmc-rbac/01-introduction.md @@ -0,0 +1,12 @@ +# Introduction +Role-based access control module to provide additional features on top of Zend\Permissions\Rbac + +Based on [ZF-Commons/zfc-rbac](https://github.com/ZF-Commons/zfc-rbac) v3.x. + +If you are looking for the Laminas version +of zfc-rbac v2, please use [LM-Commons/LmcRbacMvc](https://github.com/LM-Commons/LmcRbacMvc). + +## Support + +- File issues at https://github.com/LM-Commons/LmcRbac/issues. +- Ask questions in the [LM-Commons gitter](https://gitter.im/Lm-Commons/community) chat. diff --git a/docs/lmc-rbac/02-installtion.md b/docs/lmc-rbac/02-installtion.md new file mode 100644 index 0000000..2f0c9a5 --- /dev/null +++ b/docs/lmc-rbac/02-installtion.md @@ -0,0 +1,25 @@ +--- +sidebar_label: Requirements and Installation +--- +# Requirements and Installation +## Requirements + +- PHP 7.3 or higher + + +## Installation + +LmcRbac only officially supports installation through Composer. For Composer documentation, please refer to +[getcomposer.org](http://getcomposer.org/). + +Install the module: + +```sh +$ composer require lm-commons/lmc-rbac:^1.1 +``` + +Enable the module by adding `LmcRbac` key to your `application.config.php` file. Customize the module by copy-pasting +the `config.global.php` file to your `config/autoload` folder. + +You can also find some Doctrine entities in the [data](https://github.com/LM-Commons/LmcRbac/tree/master/data) folder that will help you to more quickly take advantage +of LmcRbac. diff --git a/docs/lmc-rbac/_category_.json b/docs/lmc-rbac/_category_.json new file mode 100644 index 0000000..4077e21 --- /dev/null +++ b/docs/lmc-rbac/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "LmcRbac", + "position": 3, + "link": { + "type": "generated-index", + "description": "Role-based access control module to provide additional features on top of Laminas\\Permissions\\Rbac (formerly zfc-rbac)" + } +} diff --git a/docs/lmc-user/_category_.json b/docs/lmc-user/_category_.json new file mode 100644 index 0000000..c9c8938 --- /dev/null +++ b/docs/lmc-user/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "LmcUser", + "position": 1, + "link": { + "type": "generated-index", + "description": "A generic user registration and authentication module for Laminas. Supports Laminas\\Db and Doctrine2." + } +} diff --git a/docs/lmc-user/installation.md b/docs/lmc-user/installation.md new file mode 100644 index 0000000..4460d0c --- /dev/null +++ b/docs/lmc-user/installation.md @@ -0,0 +1,101 @@ +--- +sidebar_position: 2 +--- +# Installation + +## Main Setup + + +### With composer + +1. Add this project in your composer.json: + + ```json + "require": { + "lm-commons/lmc-user": "^3.1" + } + ``` + +2. Now tell composer to download LmcUser by running the command: + + ```bash + $ php composer.phar update + ``` + +### Post installation + +1. Enabling it in your `application.config.php`file. + + ```php + array( + // ... + 'LmcUser', + ), + // ... + ); + ``` + + +### Post-Install: Laminas\Db + +1. If you do not already have a valid Laminas\Db\Adapter\Adapter in your service + manager configuration, put the following in `./config/autoload/database.local.php`: + +```php + array( + 'driver' => 'PdoMysql', + 'hostname' => 'changeme', + 'database' => 'changeme', + 'username' => 'changeme', + 'password' => 'changeme', + ), + 'service_manager' => array( + 'factories' => array( + 'Laminas\Db\Adapter\Adapter' => 'Laminas\Db\Adapter\AdapterServiceFactory', + ), + ), +); + +``` + +Navigate to http://yourproject/user and you should land on a login page. + +## Password Security + + +**DO NOT CHANGE THE PASSWORD HASH SETTINGS FROM THEIR DEFAULTS** unless A) you +have done sufficient research and fully understand exactly what you are +changing, **AND** B) you have a **very** specific reason to deviate from the +default settings. + +If you are planning on changing the default password hash settings, please read +the following: + +- [PHP Manual: crypt() function](http://php.net/manual/en/function.crypt.php) +- [Securely Storing Passwords in PHP by Adrian Schneider](http://www.syndicatetheory.com/labs/securely-storing-passwords-in-php) + +The password hash settings may be changed at any time without invalidating existing +user accounts. Existing user passwords will be re-hashed automatically on their next +successful login. + +**WARNING:** Changing the default password hash settings can cause serious +problems such as making your hashed passwords more vulnerable to brute force +attacks or making hashing so expensive that login and registration is +unacceptably slow for users and produces a large burden on your server(s). The +default settings provided are a very reasonable balance between the two, +suitable for computing power in 2013. + + +Migration from ZfcUser +---------------------- + +If using Zend DB update table name to lmc_user + +Replace all namespace references to ZfcUser to LmcUser + +Update references to the lowercase key zfcuser to lmcuser + diff --git a/docs/lmc-user/introduction.md b/docs/lmc-user/introduction.md new file mode 100644 index 0000000..d14de29 --- /dev/null +++ b/docs/lmc-user/introduction.md @@ -0,0 +1,28 @@ +--- +sidebar_position: 1 +--- + +# Introduction + +LmcUser is a user registration and authentication module for Laminas. +LmcUser provides the foundations for adding +user authentication and registration to your Laminas site. It is designed to be very +simple and easy to extend. + +More information and examples are available on the [LmcUser Wiki](https://github.com/LM-Commons/LmcUser/wiki) + +Based on ZfcUser by Evan Coury and the ZF-Commons team + +## Requirements + +* [Laminas](https://github.com/laminas/) (latest master) + +## Features / Goals + +* Authenticate via username, email, or both (can opt out of the concept of + username and use strictly email) [COMPLETE] +* User registration [COMPLETE] +* Forms protected against CSRF [COMPLETE] +* Out-of-the-box support for Doctrine2 _and_ Laminas\Db [COMPLETE] +* Robust event system to allow for extending [COMPLETE] +* Provide ActionController plugin and view helper [COMPLETE] \ No newline at end of file diff --git a/docs/lmc-user/options.md b/docs/lmc-user/options.md new file mode 100644 index 0000000..a248f7e --- /dev/null +++ b/docs/lmc-user/options.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 3 +--- + +# Options + +The LmcUser module has some options to allow you to quickly customize the basic +functionality. After installing LmcUser, copy +`./vendor/lm-commons/lmc-user/config/lmcuser.global.php.dist` to +`./config/autoload/lmcuser.global.php` and change the values as desired. + +The following options are available: + +- **user_entity_class** - Name of Entity class to use. Useful for using your own + entity class instead of the default one provided. Default is + `LmcUser\Entity\User`. +- **enable_username** - Boolean value, enables username field on the + registration form. Default is `false`. +- **auth_identity_fields** - Array value, specifies which fields a user can + use as the 'identity' field when logging in. Acceptable values: username, email. +- **enable_display_name** - Boolean value, enables a display name field on the + registration form. Default value is `false`. +- **enable_registration** - Boolean value, Determines if a user should be + allowed to register. Default value is `true`. +- **login_after_registration** - Boolean value, automatically logs the user in + after they successfully register. Default value is `false`. +- **use_registration_form_captcha** - Boolean value, determines if a captcha should + be utilized on the user registration form. Default value is `true`. (Note, + right now this only utilizes a weak Laminas\Text\Figlet CAPTCHA, but I have plans + to make all Laminas\Captcha adapters work.) +- **login_form_timeout** - Integer value, specify the timeout for the CSRF security + field of the login form in seconds. Default value is 300 seconds. +- **user_form_timeout** - Integer value, specify the timeout for the CSRF security + field of the registration form in seconds. Default value is 300 seconds. +- **use_redirect_parameter_if_present** - Boolean value, if a redirect GET + parameter is specified, the user will be redirected to the specified URL if + authentication is successful (if present, a GET parameter will override the + login_redirect_route specified below). +- **login_redirect_route** String value, name of a route in the application + which the user will be redirected to after a successful login. +- **logout_redirect_route** String value, name of a route in the application which + the user will be redirected to after logging out. +- **password_cost** - This should be an integer between 4 and 31. The number + represents the base-2 logarithm of the iteration count used for hashing. + Default is `10` (about 10 hashes per second on an i5). +- **enable_user_state** - Boolean value, enable user state usage. Should user's + state be used in the registration/login process? +- **default_user_state** - Integer value, default user state upon registration. + What state user should have upon registration? +- **allowed_login_states** - Array value, states which are allowing user to login. + When user tries to login, is his/her state one of the following? Include null if + you want user's with no state to login as well. \ No newline at end of file diff --git a/docs/lmc-user/support.md b/docs/lmc-user/support.md new file mode 100644 index 0000000..9b47990 --- /dev/null +++ b/docs/lmc-user/support.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 5 +--- +# Support + +- File issues at https://github.com/LM-Commons/LmcUser/issues. +- Ask questions in the [LM-Commons Gitter](https://gitter.im/LM-Commons/community) chat. \ No newline at end of file diff --git a/docs/lmc-user/wiki/_category_.json b/docs/lmc-user/wiki/_category_.json new file mode 100644 index 0000000..1fd3e57 --- /dev/null +++ b/docs/lmc-user/wiki/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Wiki", + "position": 4, + "link": { + "type": "generated-index", + "description": "Welcome to the LmcUser How-Tos" + } +} diff --git a/docs/lmc-user/wiki/how-to-change-password-and-email.md b/docs/lmc-user/wiki/how-to-change-password-and-email.md new file mode 100644 index 0000000..5225851 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-change-password-and-email.md @@ -0,0 +1,10 @@ +--- +sidebar_position: 9 +--- +# How to change password and email (not complete) + +http://DomainName/user/ + +http://DomainName/user/change-email/ + +http://DomainName/user/change-password/ \ No newline at end of file diff --git a/docs/lmc-user/wiki/how-to-check-if-the-user-is-logged-in.md b/docs/lmc-user/wiki/how-to-check-if-the-user-is-logged-in.md new file mode 100644 index 0000000..7f5f6cf --- /dev/null +++ b/docs/lmc-user/wiki/how-to-check-if-the-user-is-logged-in.md @@ -0,0 +1,64 @@ +--- +sidebar_position: 8 +--- +# How to check if the user is logged in +## Task +Check if the user is logged in (ie: user identity widget) + +## Solution +There are three ways. + +### View +LmcUser provides a View Helper ([lmcUserIdentity](https://github.com/LM-Commons/LmcUser/blob/master/src/LmcUser/View/Helper/LmcUserIdentity.php)) which you can use from any view script in your application. + +```php + +lmcUserIdentity()): ?> + + lmcUserLoginWidget(['redirect'=>'application']); ?> + + + lmcUserIdentity()->getDisplayname(); ?> + +``` + +You can also get user's fields (if the user is logged in), like email: + +```php +lmcUserIdentity()->getEmail(); ?> +``` + +### Controller + +LmcUser provides a Controller Plugin ([lmcUserAuthentication](https://github.com/LM-Commons/LmcUser/blob/master/src/LmcUser/Controller/Plugin/LmcUserAuthentication.php)) which you can use from any controller in your application. You can check if the user is connected and get his data: + +```php +lmcUserAuthentication()->hasIdentity()) { + //get the email of the user + echo $this->lmcUserAuthentication()->getIdentity()->getEmail(); + //get the user_id of the user + echo $this->lmcUserAuthentication()->getIdentity()->getId(); + //get the username of the user + echo $this->lmcUserAuthentication()->getIdentity()->getUsername(); + //get the display name of the user + echo $this->lmcUserAuthentication()->getIdentity()->getDisplayname(); +} +?> +``` + +You can get the `lmcuser_auth_service` in a controller by doing: +```php +$authService = $this->lmcUserIdentity()->getAuthService(); +``` +### Service Manager + +```php +getServiceManager(); +$auth = $sm->get('lmcuser_auth_service'); +if ($auth->hasIdentity()) { + echo $auth->getIdentity()->getEmail(); +} +?> +``` diff --git a/docs/lmc-user/wiki/how-to-choose-which-user-fields-are-used-during-authentication.md b/docs/lmc-user/wiki/how-to-choose-which-user-fields-are-used-during-authentication.md new file mode 100644 index 0000000..86b2878 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-choose-which-user-fields-are-used-during-authentication.md @@ -0,0 +1,30 @@ +--- +sidebar_position: 6 +--- +# How to choose which user fields are used during authentication + +## Task +How to specify which fields a user can use as their 'identity' when logging in. + +## Solution + +The configuration directive `auth_identity_fields` is used to control the fields used to look up user identities stored in LmcUser. You can configure this directive (via your `config/autoload/lmcuser.global.php` override file) to one of four possible modes: + +1. Authenticate via email address only: +```php +'auth_identity_fields' => ['email'], +``` +2. Authenticate via username only: +```php +'auth_identity_fields' => ['username'], +``` + +3. Authenticate via both methods, with username field checked first: +```php +'auth_identity_fields' => ['username', 'email'], +``` + +4. Authenticate via both methods, with email address field checked first: +```php +'auth_identity_fields' => ['email', 'username'], +``` diff --git a/docs/lmc-user/wiki/how-to-configure-a-session-timeout.md b/docs/lmc-user/wiki/how-to-configure-a-session-timeout.md new file mode 100644 index 0000000..5038b58 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-configure-a-session-timeout.md @@ -0,0 +1,43 @@ +--- +sidebar_position: 10 +--- +# How to configure a session timeout +## Task +Automatically terminate a user's session after a specific period of inactivity. + +## Solution + +1. Add [session factories and configuration](https://docs.laminas.dev/laminas-session/config/) to your application via a module config or `config/autoload` file: + + ```php + return [ + 'service_manager' => [ + 'factories' => [ + // Configures the default SessionManager instance + 'Laminas\Session\ManagerInterface' => 'Laminas\Session\Service\SessionManagerFactory', + + // Provides session configuration to SessionManagerFactory + 'Laminas\Session\Config\ConfigInterface' => 'Laminas\Session\Service\SessionConfigFactory', + ], + ], + 'session_manager' => [ + // SessionManager config: validators, etc + ], + 'session_config' => [ + // Set the session and cookie expiries to 15 minutes + 'cache_expire' => 900, + 'cookie_lifetime' => 900, + ], + ]; + ``` + +2. In `Application\Module::onBootstrap`, pull an instance of the SessionManager. This will inject the properly-configured SessionManager instance as the default for all new session containers. + + ```php + public function onBootstrap(MvcEvent $e) + { + $manager = $e->getApplication()->getServiceManager()->get('Laminas\Session\ManagerInterface'); + } + ``` + +Alternatively, you could use an external module such as [`HtSession`](https://github.com/hrevert/HtSession) instead of a manual configuration. diff --git a/docs/lmc-user/wiki/how-to-embed-the-login-form-on-another-page.md b/docs/lmc-user/wiki/how-to-embed-the-login-form-on-another-page.md new file mode 100644 index 0000000..9687926 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-embed-the-login-form-on-another-page.md @@ -0,0 +1,24 @@ +--- +sidebar_position: 2 +--- + +# How to embed the login form on another page + +## Task +Embed the login form on another page (ie: homepage login widget) + +## Solution +LmcUser provides a View Helper ([LmcUserLoginWidget](https://github.com/LM-Commons/LmcUser/blob/master/src/LmcUser/View/Helper/LmcUserLoginWidget.php)) which you can use from any view script in your application. Just add the following call to the location in your markup where you want the form to be rendered: + +```php +lmcUserLoginWidget(); ?> +``` + +## Note +The view helper can also __return__ the login form: + +```php +lmcUserLoginWidget(['render' => false]); ?> +``` + +This will return an object of type [Login](https://github.com/LM-Commons/LmcUser/blob/master/src/LmcUser/Form/Login.php) that can be used to generate a custom login form. diff --git a/docs/lmc-user/wiki/how-to-modify-the-form-objects-used-by-lmcuser.md b/docs/lmc-user/wiki/how-to-modify-the-form-objects-used-by-lmcuser.md new file mode 100644 index 0000000..fb752d1 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-modify-the-form-objects-used-by-lmcuser.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 3 +--- +# How to modify the form objects used by LmcUser +## Task +Modify the form objects used by LmcUser for registration and/or login. + +## Solution +You can accomplish this by hooking into the `init` event of the form you wish to modify. The simplest way to do this is pull the shared event manager and attach a listener. + +```php +getApplication()->getEventManager()->getSharedManager(); + $events->attach('LmcUser\Form\Register','init', function($e) { + $form = $e->getTarget(); + // Do what you please with the form instance ($form) + }); + $events->attach('LmcUser\Form\RegisterFilter','init', function($e) { + $filter = $e->getTarget(); + // Do what you please with the filter instance ($filter) + }); +} +``` + +TODO: Illustrate a method that doesn't use StaticEventManager diff --git a/docs/lmc-user/wiki/how-to-modify-the-login-and-or-registration-form-timeout.md b/docs/lmc-user/wiki/how-to-modify-the-login-and-or-registration-form-timeout.md new file mode 100644 index 0000000..12e20f6 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-modify-the-login-and-or-registration-form-timeout.md @@ -0,0 +1,82 @@ +--- +sidebar_position: 7 +--- +# How to modify the login and/or registration form timeout + +## Task +The login/registration form timeout is too small and needs to be extended. + +## Solution +To simply extend the login or registration form timeout, create a local configuration file inside the project (ex: `/config/autoload/lmcuser.local.php`) and add the necessary configuration options. For example, to allow the login form to live for 30 minutes before expiring : + +```php +return [ + 'lmc_user' => [ + 'login_form_timeout' => 1800, + ], +]; +``` + +### Note +The registration form's option is `user_form_timeout`. + +For very long TTL, instead of setting a very high timeout value, it is also possible to refresh the CSRF field value via Ajax. To do so, create a controller and a route to it. For example, we may want to map `/user/login/keep-alive` to this controller action : + +```php +getServiceLocator()->get('lmcuser_login_form'); + return new JsonModel([ + 'timestamp' => strtotime('now'), + 'hash' => $loginForm->get('csrf')->getValidator()->getHash(true), + ]); + } +} +``` + +Then, in your module config, add the `JsonStrategy` + +```php + 'view_manager' => [ + // ... other configs + 'strategies' => [ + 'ViewJsonStrategy', + ], + ], +``` + +and, finally, in your view script (assuming jQuery is used) + +```js +function pingLoginForm() { + setTimeout(function() { + $.getJSON("url('keep-alive-route-name'); ?>", function(data) { + if (data.timestamp) { + $(':hidden[name="csrf"]').val(data.hash); + } + }); + pingLoginForm(); + }, 302000); // 5 minutes + 2 seconds +} +pingLoginForm(); +``` + +### Note +The Javascript timer timeout value may be set via the lmc_user config by setting the view model the proper value from the controller (Laminas MVC v2 only) + +```php +getServiceLocator()->get('lmcuser_module_options')->getLoginFormTimeout(); +return new ViewModel([ + // ... + 'loginFormTimeout' => $timeout, +]); +``` diff --git a/docs/lmc-user/wiki/how-to-override-built-in-view-scripts.md b/docs/lmc-user/wiki/how-to-override-built-in-view-scripts.md new file mode 100644 index 0000000..bdd6e27 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-override-built-in-view-scripts.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 1 +--- +# How to override built in view scripts +## Task +Override the built-in view scripts for pages such as registration and sign-in with your own custom view scripts + +## Solution + +1. In your module, under the `view` directory, create the folder tree `lmc-user/user` +2. Create the necessary override view scripts, depending on which page(s) you want to change: + * User Login page: `lmc-user/user/login.phtml` + * User Registration page: `lmc-user/user/register.phtml` + * Default post-login landing page: `lmc-user/user/index.phtml` +3. Put this into your `module.config.php` file + +```php +'view_manager' => [ + 'template_path_stack' => [ + 'zfc-user' => __DIR__ . '/../view', + ], + ], +``` + +Refer to each [built-in view script](https://github.com/LM-Commons/LmcUser/tree/master/view/lmc-user/user) to see how the form is configured and rendered. + +NOTE: Your module must be loaded after LmcUser or the overriding will not work. To do this, place your module after LmcUser in the `modules` key of your application configuration (`config/application.config.php`). diff --git a/docs/lmc-user/wiki/how-to-perform-a-custom-action-when-a-new-user-account-is-created.md b/docs/lmc-user/wiki/how-to-perform-a-custom-action-when-a-new-user-account-is-created.md new file mode 100644 index 0000000..c15df74 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-perform-a-custom-action-when-a-new-user-account-is-created.md @@ -0,0 +1,56 @@ +--- +sidebar_position: 5 +--- +# How to perform a custom action when a new user account is created + +## Task +Perform a custom action when a new user account is created. + +## Solution +The user service (`lmcuser_user_service`) provided by LmcUser triggers an event (`register`) immediately before persisting the user account. + +If you have access to the service locator which LmcUser has previously loaded it's services into, you can simply pull the user service and attach an event to it's internal event manager. + +```php +get('lmcuser_user_service')->getEventManager(); +$lmcServiceEvents->attach('register', function($e) { + $user = $e->getParam('user'); // User account object + $form = $e->getParam('form'); // Form object + // Perform your custom action here +}); +``` + +If you can't get access to the user service instance directly, you can use the StaticEventManager to attach an event directly via the class name: + +```php +attach('LmcUser\Service\User', 'register', function($e) { + $user = $e->getParam('user'); // User account object + $form = $e->getParam('form'); // Form object + // Perform your custom action here +}); +``` + +## Retrieving the User Id +If you need to retrieve the `user_id`, just attach to `register.post` and the user entity should have it. + +## Example + +File: module/Application/Module.php +```php +attach('LmcUser\Service\User', 'register', function($e) { + $user = $e->getParam('user'); // User account object + $form = $e->getParam('form'); // Form object + // Perform your custom action here + }); + } +} +``` diff --git a/docs/lmc-user/wiki/how-to-secure-sessions-against-session-hijacking-attacks.md b/docs/lmc-user/wiki/how-to-secure-sessions-against-session-hijacking-attacks.md new file mode 100644 index 0000000..08a61a0 --- /dev/null +++ b/docs/lmc-user/wiki/how-to-secure-sessions-against-session-hijacking-attacks.md @@ -0,0 +1,34 @@ +--- +sidebar_position: 11 +--- +# How to secure sessions against session hijacking attacks + +## Task + +Configure the Session Manager to help mitigate session hijacking attacks. + +## Solution + +If you haven't already done so, add the [session manager factory](https://docs.laminas.dev/laminas-session/config/) to your application via a module config or `config/autoload` file. + +In the same file (or another file if you prefer), add the `session_manager` key and insert the session validators you wish to load. In this case we'll use both `RemoteAddr` and `HttpUserAgent`: + +```php +return [ + 'service_manager' => [ + 'factories' => [ + 'Laminas\Session\ManagerInterface' => 'Laminas\Session\Service\SessionManagerFactory', + ], + ], + 'session_manager' => [ + 'validators' => [ + 'Laminas\Session\Validator\RemoteAddr', + 'Laminas\Session\Validator\HttpUserAgent', + ] + ], +]; +``` + +Alternatively, you could use an external module such as [`HtSession`](https://github.com/hrevert/HtSession) instead of a manual configuration. + +> NOTE: This does not really secure your session against hijacking attacks unless it's 1994. Please use HTTPS, secure cookies, HTTP only cookies, CSRF protection, credential re-entry and session regeneration to make sure your sessions are secure. diff --git a/docs/lmc-user/wiki/how-to-store-custom-form-values-into-your-user-entity-at-registration.md b/docs/lmc-user/wiki/how-to-store-custom-form-values-into-your-user-entity-at-registration.md new file mode 100644 index 0000000..98c08cf --- /dev/null +++ b/docs/lmc-user/wiki/how-to-store-custom-form-values-into-your-user-entity-at-registration.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 4 +--- +# How to store custom form values into your user entity at registration + +Ref, using ZF 2.1.4 at the time this was written + +A bit of a follow up to: +https://github.com/LM-Commons/LmcUser/wiki/How-to-embed-the-login-form-on-another-page + +In your bootstrap event (hopefully in a custom Module for your user entities and roles and so forth), add this block to your onBootstrap code: + + + +```php +getApplication()->getEventManager(); + $em = $eventManager->getSharedManager(); + + // ... + + $lmcServiceEvents = $e->getApplication()->getServiceManager()->get('lmcuser_user_service')->getEventManager(); + + // To validate new field + $em->attach('LmcUser\Form\RegisterFilter','init', function($e) { + $filter = $e->getTarget(); + $filter->add([ + 'name' => 'favorite_icecream', + 'required' => true, + 'allowEmpty' => false, + 'filters' => [['name' => 'StringTrim']], + 'validators' => [ + [ + 'name' => 'NotEmpty', + ], + ], + ]); + }); + + // Store the field + $lmcServiceEvents->attach('register', function($e) { + $form = $e->getParam('form'); + $user = $e->getParam('user'); + + /* @var $user \FooUser\Entity\User */ + $user->setUsername( $form->get('username')->getValue() ); + $user->setFavoriteIceCream( $form->get('favorite_icecream')->getValue() ); + }); +} +``` diff --git a/docusaurus.config.js b/docusaurus.config.js new file mode 100644 index 0000000..810ddc5 --- /dev/null +++ b/docusaurus.config.js @@ -0,0 +1,147 @@ +// @ts-check +// `@type` JSDoc annotations allow editor autocompletion and type checking +// (when paired with `@ts-check`). +// There are various equivalent ways to declare your Docusaurus config. +// See: https://docusaurus.io/docs/api/docusaurus-config + +import {themes as prismThemes} from 'prism-react-renderer'; + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'LM Commons', + tagline: 'Community developed packages for the Laminas MVC', + favicon: 'img/favicon.ico', + + // Set the production url of your site here + url: 'https://lm-commons.github.io', + // Set the // pathname under which your site is served + // For GitHub pages deployment, it is often '//' + baseUrl: '/', + + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: 'LM-Commons', // Usually your GitHub org/user name. + projectName: 'Docs', // Usually your repo name. + + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + + // Even if you don't use internationalization, you can use this field to set + // useful metadata like html lang. For example, if your site is Chinese, you + // may want to replace "en" with "zh-Hans". + i18n: { + defaultLocale: 'en', + locales: ['en'], + }, + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + sidebarPath: './sidebars.js', + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: 'https://github.com/lm-commons/tree/master/', + }, + blog: { + showReadingTime: true, + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: 'https://github.com/lm-commons/tree/master/', + }, + theme: { + customCss: './src/css/custom.css', + }, + }), + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + // Replace with your project's social card + image: 'img/LMC-social-card.jpg', + navbar: { + title: 'LM Commons', + logo: { + alt: 'LM Commons Logo', + src: 'img/LMC-logo.png', + }, + items: [ + { + type: 'docSidebar', + sidebarId: 'tutorialSidebar', + position: 'left', + label: 'Docs', + }, + {to: '/blog', label: 'Blog', position: 'left'}, + { + href: 'https://github.com/lm-commons', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Packages', + items: [ + { + label: 'LmcUser', + to: '/docs/lmc-user/introduction', + }, + { + label: 'LmcRbacMvc', + to: 'https://lm-commons.github.io/lmcrbacmvc/', + }, + { + label: 'LmcRbac', + to: '/docs/lmc-rbac/introduction', + }, + { + label: 'LmcCors', + to: '/docs/lmc-cors/introduction', + }, + { + label: 'LmcAdmin', + to: '/docs/lmc-admin/introduction', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'Gitter', + href: 'https://gitter.im/LM-Commons/community', + }, + ], + }, + { + title: 'More', + items: [ + { + label: 'Blog', + to: '/blog', + }, + { + label: 'GitHub', + href: 'https://github.com/LM-Commons', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} LM Commons Organization. Built with Docusaurus.`, + }, + prism: { + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + }, + }), +}; + +export default config; diff --git a/package.json b/package.json new file mode 100644 index 0000000..0a4c1fa --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "lmc-github-io-docs-temp", + "version": "0.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids" + }, + "dependencies": { + "@docusaurus/core": "3.1.1", + "@docusaurus/preset-classic": "3.1.1", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "prism-react-renderer": "^2.3.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "3.1.1", + "@docusaurus/types": "3.1.1" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 3 chrome version", + "last 3 firefox version", + "last 5 safari version" + ] + }, + "engines": { + "node": ">=18.0" + } +} diff --git a/sidebars.js b/sidebars.js new file mode 100644 index 0000000..3327580 --- /dev/null +++ b/sidebars.js @@ -0,0 +1,33 @@ +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // By default, Docusaurus generates a sidebar from the docs folder structure + tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], + + // But you can create a sidebar manually + /* + tutorialSidebar: [ + 'intro', + 'hello', + { + type: 'category', + label: 'Tutorial', + items: ['tutorial-basics/create-a-document'], + }, + ], + */ +}; + +export default sidebars; diff --git a/src/components/HomepageFeatures/index.js b/src/components/HomepageFeatures/index.js new file mode 100644 index 0000000..d3d18a1 --- /dev/null +++ b/src/components/HomepageFeatures/index.js @@ -0,0 +1,68 @@ +import clsx from 'clsx'; +import Heading from '@theme/Heading'; +import styles from './styles.module.css'; + +const FeatureList = [ + { + title: 'Easy to Use', + Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, + description: ( + <> + Docusaurus was designed from the ground up to be easily installed and + used to get your website up and running quickly. + + ), + }, + { + title: 'Focus on What Matters', + Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, + description: ( + <> + Docusaurus lets you focus on your docs, and we'll do the chores. Go + ahead and move your docs into the docs directory. + + ), + }, + { + title: 'Powered by React', + Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, + description: ( + <> + Extend or customize your website layout by reusing React. Docusaurus can + be extended while reusing the same header and footer. + + ), + }, +]; + +function Feature({Svg, title, description}) { + return ( +
+
+ +
+
+ {title} +

{description}

+
+
+ ); +} + +export default function HomepageFeatures() { + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + <> + {/*} + + {*/} + + ))} +
+
+
+ ); +} diff --git a/src/components/HomepageFeatures/styles.module.css b/src/components/HomepageFeatures/styles.module.css new file mode 100644 index 0000000..b248eb2 --- /dev/null +++ b/src/components/HomepageFeatures/styles.module.css @@ -0,0 +1,11 @@ +.features { + display: flex; + align-items: center; + padding: 2rem 0; + width: 100%; +} + +.featureSvg { + height: 200px; + width: 200px; +} diff --git a/src/css/custom.css b/src/css/custom.css new file mode 100644 index 0000000..2bc6a4c --- /dev/null +++ b/src/css/custom.css @@ -0,0 +1,30 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #2e8555; + --ifm-color-primary-dark: #29784c; + --ifm-color-primary-darker: #277148; + --ifm-color-primary-darkest: #205d3b; + --ifm-color-primary-light: #33925d; + --ifm-color-primary-lighter: #359962; + --ifm-color-primary-lightest: #3cad6e; + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme='dark'] { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: #21af90; + --ifm-color-primary-darker: #1fa588; + --ifm-color-primary-darkest: #1a8870; + --ifm-color-primary-light: #29d5b0; + --ifm-color-primary-lighter: #32d8b4; + --ifm-color-primary-lightest: #4fddbf; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 0000000..5589ae2 --- /dev/null +++ b/src/pages/index.js @@ -0,0 +1,36 @@ +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; +import HomepageFeatures from '@site/src/components/HomepageFeatures'; + +import Heading from '@theme/Heading'; +import styles from './index.module.css'; + +function HomepageHeader() { + const {siteConfig} = useDocusaurusContext(); + return ( +
+
+ + {siteConfig.title} + +

{siteConfig.tagline}

+
+
+ ); +} + +export default function Home() { + const {siteConfig} = useDocusaurusContext(); + return ( + + +
+ +
+
+ ); +} diff --git a/src/pages/index.module.css b/src/pages/index.module.css new file mode 100644 index 0000000..9f71a5d --- /dev/null +++ b/src/pages/index.module.css @@ -0,0 +1,23 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + text-align: center; + position: relative; + overflow: hidden; +} + +@media screen and (max-width: 996px) { + .heroBanner { + padding: 2rem; + } +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/pages/markdown-page.md b/src/pages/markdown-page.md new file mode 100644 index 0000000..9756c5b --- /dev/null +++ b/src/pages/markdown-page.md @@ -0,0 +1,7 @@ +--- +title: Markdown page example +--- + +# Markdown page example + +You don't need React to write simple standalone pages. diff --git a/static/.nojekyll b/static/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/static/img/LMC-logo.png b/static/img/LMC-logo.png new file mode 100644 index 0000000..df20b7d Binary files /dev/null and b/static/img/LMC-logo.png differ diff --git a/static/img/LMC-social-card.jpg b/static/img/LMC-social-card.jpg new file mode 100644 index 0000000..9b1572c Binary files /dev/null and b/static/img/LMC-social-card.jpg differ diff --git a/static/img/docusaurus-social-card.jpg b/static/img/docusaurus-social-card.jpg new file mode 100644 index 0000000..ffcb448 Binary files /dev/null and b/static/img/docusaurus-social-card.jpg differ diff --git a/static/img/docusaurus.png b/static/img/docusaurus.png new file mode 100644 index 0000000..f458149 Binary files /dev/null and b/static/img/docusaurus.png differ diff --git a/static/img/favicon.ico b/static/img/favicon.ico new file mode 100644 index 0000000..1545f38 Binary files /dev/null and b/static/img/favicon.ico differ diff --git a/static/img/logo.svg b/static/img/logo.svg new file mode 100644 index 0000000..9db6d0d --- /dev/null +++ b/static/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/undraw_docusaurus_mountain.svg b/static/img/undraw_docusaurus_mountain.svg new file mode 100644 index 0000000..af961c4 --- /dev/null +++ b/static/img/undraw_docusaurus_mountain.svg @@ -0,0 +1,171 @@ + + Easy to Use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/undraw_docusaurus_react.svg b/static/img/undraw_docusaurus_react.svg new file mode 100644 index 0000000..94b5cf0 --- /dev/null +++ b/static/img/undraw_docusaurus_react.svg @@ -0,0 +1,170 @@ + + Powered by React + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/undraw_docusaurus_tree.svg b/static/img/undraw_docusaurus_tree.svg new file mode 100644 index 0000000..d9161d3 --- /dev/null +++ b/static/img/undraw_docusaurus_tree.svg @@ -0,0 +1,40 @@ + + Focus on What Matters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +