├── .coveralls.yml
├── .gitignore
├── .styleci.yml
├── .travis.yml
├── Controller
└── GeneratorController.php
├── DependencyInjection
├── Configuration.php
└── HackzillaPasswordGeneratorExtension.php
├── Entity
└── Options.php
├── Exception
└── UnknownGeneratorException.php
├── Form
└── Type
│ └── OptionType.php
├── HackzillaPasswordGeneratorBundle.php
├── Makefile
├── README.md
├── Resources
├── config
│ ├── routing.yml
│ └── services.yml
├── translations
│ ├── messages.bg.xlf
│ ├── messages.en.xlf
│ └── messages.fr.xlf
└── views
│ ├── Generator
│ ├── computer-layout.html.twig
│ ├── form.html.twig
│ ├── human-layout.html.twig
│ └── hybrid-layout.html.twig
│ ├── layout.html.twig
│ └── menu.html.twig
├── Tests
├── Controller
│ └── GeneratorControllerTest.php
├── Entity
│ └── OptionsTest.php
├── Form
│ └── Type
│ │ └── OptionTypeTest.php
├── PublicService.php
├── TestKernel.php
└── bootstrap.php
├── composer.json
└── phpunit.xml.dist
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | # single file
2 | coverage_clover: build/logs/clover.xml
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/*
2 | composer.lock
3 | .php_cs.cache
4 | .phpunit
5 | .phpunit.result.cache
6 | /phpunit.xml
7 | /.idea
8 |
9 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: symfony
2 |
3 | enabled:
4 | - align_double_arrow
5 | - newline_after_open_tag
6 | - ordered_use
7 | - short_array_syntax
8 | - php_unit_construct
9 | - php_unit_strict
10 |
11 | disabled:
12 | - unalign_double_arrow
13 | - unalign_equals
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 |
3 | language: php
4 |
5 | php:
6 | - 8.0
7 | - 8.1
8 | - nightly
9 |
10 | env:
11 | global:
12 | - PATH="$HOME/.composer/vendor/bin:$PATH"
13 |
14 | matrix:
15 | fast_finish: true
16 | include:
17 | - php: 8.0
18 | env: CS_FIXER=run
19 | - php: 8.0
20 | env: SYMFONY_VERSION="5.4.*"
21 | - php: 8.0
22 | env: SYMFONY_VERSION="6.1.*"
23 | - php: 8.1
24 | env: SYMFONY_VERSION="6.1.*"
25 | allow_failures:
26 | - php: nightly
27 | - env: SYMFONY_VERSION="6@dev"
28 |
29 | sudo: false
30 |
31 | cache:
32 | directories:
33 | - $HOME/.composer/cache
34 |
35 | before_script:
36 | - composer selfupdate
37 | - composer config -g github-oauth.github.com $GITHUB_OAUTH_TOKEN
38 | - composer global require phpunit/phpunit friendsofphp/php-cs-fixer --no-update
39 | - composer global update --prefer-dist --no-interaction
40 | - if [ "$SYMFONY_VERSION" != "" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --no-update; fi;
41 | - composer update --prefer-dist --no-interaction $COMPOSER_FLAGS
42 |
43 | script:
44 | - make test
45 |
46 | after_success:
47 | - php vendor/bin/coveralls -c .coveralls.yml -v
48 |
--------------------------------------------------------------------------------
/Controller/GeneratorController.php:
--------------------------------------------------------------------------------
1 | humanPasswordGenerator = $humanPasswordGenerator;
58 | $this->hybridPasswordGenerator = $hybridPasswordGenerator;
59 | $this->computerPasswordGenerator = $computerPasswordGenerator;
60 | $this->requirementPasswordGenerator = $requirementPasswordGenerator;
61 | $this->dummyPasswordGenerator = $dummyPasswordGenerator;
62 | $this->formFactory = $formFactory;
63 | $this->twigEnvironment = $twigEnvironment;
64 | }
65 |
66 | /**
67 | * Password generator form.
68 | *
69 | * @param Request $request
70 | * @param string|null $mode
71 | *
72 | * @return \Symfony\Component\HttpFoundation\Response
73 | */
74 | public function formAction(Request $request, ?string $mode = null): Response
75 | {
76 | $mode = $this->getMode($request, $mode);
77 | $passwordGenerator = $this->getPasswordGenerator($mode);
78 |
79 | $passwords = $error = null;
80 | $options = new Options($passwordGenerator);
81 |
82 | $form = $this->buildForm($passwordGenerator, $options, $mode);
83 |
84 | $form->handleRequest($request);
85 |
86 | if ($form->isSubmitted() && $form->isValid()) {
87 | try {
88 | $passwords = $passwordGenerator->generatePasswords($options->getQuantity());
89 | } catch (CharactersNotFoundException $e) {
90 | $error = 'CharactersNotFoundException';
91 | }
92 | }
93 |
94 | $content = $this->twigEnvironment->render(
95 | '@HackzillaPasswordGenerator/Generator/form.html.twig', [
96 | 'form' => $form->createView(),
97 | 'mode' => $mode,
98 | 'passwords' => $passwords,
99 | 'error' => $error,
100 | ]
101 | );
102 |
103 | return new Response($content);
104 | }
105 |
106 | /**
107 | * Lookup Password Generator Service.
108 | *
109 | * @param string $mode
110 | *
111 | * @return PasswordGeneratorInterface
112 | *
113 | * @throws UnknownGeneratorException
114 | */
115 | private function getPasswordGenerator($mode): PasswordGeneratorInterface
116 | {
117 | switch ($mode) {
118 | case 'dummy':
119 | return $this->dummyPasswordGenerator;
120 |
121 | case 'computer':
122 | return $this->computerPasswordGenerator;
123 |
124 | case 'human':
125 | return $this->humanPasswordGenerator;
126 |
127 | case 'hybrid':
128 | return $this->hybridPasswordGenerator;
129 | }
130 |
131 | throw new UnknownGeneratorException();
132 | }
133 |
134 | /**
135 | * Figure out password generator mode.
136 | *
137 | * @param Request $request
138 | * @param string $mode
139 | *
140 | * @return string
141 | */
142 | private function getMode(Request $request, $mode = null): string
143 | {
144 | if (is_null($mode)) {
145 | switch ($request->query->get('mode')) {
146 | case 'dummy':
147 | case 'human':
148 | case 'hybrid':
149 | case 'computer':
150 | return $request->query->get('mode');
151 |
152 | default:
153 | return 'computer';
154 | }
155 | }
156 |
157 | return $mode;
158 | }
159 |
160 | /**
161 | * Build form.
162 | *
163 | * @param PasswordGeneratorInterface $passwordGenerator
164 | * @param Options $options
165 | * @param string $mode
166 | *
167 | * @return \Symfony\Component\Form\Form
168 | */
169 | private function buildForm(PasswordGeneratorInterface $passwordGenerator, Options $options, $mode = ''): FormInterface
170 | {
171 | return $this->formFactory->create(OptionType::class, $options, [
172 | 'action' => $this->generateUrl(
173 | 'hackzilla_password_generator_show',
174 | [
175 | 'mode' => $mode,
176 | ]
177 | ),
178 | 'method' => 'GET',
179 | 'generator' => $passwordGenerator,
180 | ]);
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 | processConfiguration($configuration, $configs);
26 |
27 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
28 | $loader->load('services.yml');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Entity/Options.php:
--------------------------------------------------------------------------------
1 | passwordGenerator = $passwordGenerator;
17 | }
18 |
19 | public function __get($name): mixed
20 | {
21 | return $this->passwordGenerator->getOptionValue(strtoupper($name));
22 | }
23 |
24 | public function __set($name, $value): void
25 | {
26 | $this->passwordGenerator->setOptionValue(strtoupper($name), $value);
27 | }
28 |
29 | public function getQuantity(): int
30 | {
31 | return (int) $this->quantity;
32 | }
33 |
34 | public function setQuantity($quantity): void
35 | {
36 | $this->quantity = $quantity;
37 | }
38 |
39 | public function getUppercase(): bool
40 | {
41 | return (bool) $this->__get('uppercase');
42 | }
43 |
44 | public function setUppercase(bool $uppercase): void
45 | {
46 | $this->__set('uppercase', $uppercase);
47 | }
48 |
49 | public function getLowercase(): bool
50 | {
51 | return (bool) $this->__get('lowercase');
52 | }
53 |
54 | public function setLowercase(bool $lowercase): void
55 | {
56 | $this->__set('lowercase', $lowercase);
57 | }
58 |
59 | public function getNumbers(): bool
60 | {
61 | return (bool) $this->__get('numbers');
62 | }
63 |
64 | public function setNumbers(bool $numbers): void
65 | {
66 | $this->__set('numbers', $numbers);
67 | }
68 |
69 | public function getSymbols(): bool
70 | {
71 | return (bool) $this->__get('symbols');
72 | }
73 |
74 | public function setSymbols(bool $symbols): void
75 | {
76 | $this->__set('symbols', $symbols);
77 | }
78 |
79 | public function getAvoidSimilar(): bool
80 | {
81 | return (bool) $this->__get('avoid_similar');
82 | }
83 |
84 | public function setAvoidSimilar(bool $symbols): void
85 | {
86 | $this->__set('avoid_similar', $symbols);
87 | }
88 |
89 | public function getLength(): ?int
90 | {
91 | return $this->__get('length');
92 | }
93 |
94 | public function setLength(?int $symbols): void
95 | {
96 | $this->__set('length', $symbols);
97 | }
98 |
99 | public function getWords(): int
100 | {
101 | return (int) $this->__get('words');
102 | }
103 |
104 | public function setWords(int $symbols): void
105 | {
106 | $this->__set('words', $symbols);
107 | }
108 |
109 | public function getMin(): int
110 | {
111 | return (int) $this->__get('min');
112 | }
113 |
114 | public function setMin(int $symbols): void
115 | {
116 | $this->__set('min', $symbols);
117 | }
118 |
119 | public function getMax(): int
120 | {
121 | return (int) $this->__get('max');
122 | }
123 |
124 | public function setMax(int $symbols): void
125 | {
126 | $this->__set('max', $symbols);
127 | }
128 |
129 | public function getSegmentCount(): int
130 | {
131 | return (int) $this->__get('segment_count');
132 | }
133 |
134 | public function setSegmentCount(int $segmentCount): void
135 | {
136 | $this->__set('segment_count', $segmentCount);
137 | }
138 |
139 | public function getSegmentLength(): int
140 | {
141 | return (int) $this->__get('segment_length');
142 | }
143 |
144 | public function setSegmentLength(int $segmentCount): void
145 | {
146 | $this->__set('segment_length', $segmentCount);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Exception/UnknownGeneratorException.php:
--------------------------------------------------------------------------------
1 | add(
29 | 'quantity',
30 | IntegerType::class,
31 | [
32 | 'label' => 'OPTION_HOW_MANY_PASSWORDS',
33 | ],
34 | );
35 |
36 | $generator = $options['generator'];
37 |
38 | if (!is_a($generator, PasswordGeneratorInterface::class)) {
39 | return;
40 | }
41 |
42 | foreach ($generator->getOptions() as $key => $option) {
43 | if (
44 | $generator instanceof HumanPasswordGenerator
45 | && HumanPasswordGenerator::OPTION_LENGTH === $key
46 | ) {
47 | continue;
48 | }
49 |
50 | switch ($option->getType()) {
51 | case Option::TYPE_STRING:
52 | $this->addStringType($builder, $key, $option);
53 | break;
54 |
55 | case Option::TYPE_BOOLEAN:
56 | $this->addBooleanType($builder, $key, $option);
57 | break;
58 |
59 | case Option::TYPE_INTEGER:
60 | $this->addIntegerType($builder, $key, $option);
61 | break;
62 | }
63 | }
64 | }
65 |
66 | /**
67 | * @param FormBuilderInterface $builder
68 | * @param $key
69 | * @param OptionInterface $option
70 | */
71 | private function addStringType(FormBuilderInterface $builder, $key, OptionInterface $option): void
72 | {
73 | $builder->add(
74 | $builder->create(
75 | strtolower($key),
76 | TextType::class,
77 | [
78 | 'data' => $option->getValue(),
79 | 'label' => 'OPTION_'.$key,
80 | 'required' => false,
81 | ],
82 | ),
83 | );
84 | }
85 |
86 | /**
87 | * @param FormBuilderInterface $builder
88 | * @param $key
89 | * @param OptionInterface $option
90 | */
91 | private function addBooleanType(FormBuilderInterface $builder, $key, OptionInterface $option): void
92 | {
93 | $builder->add(
94 | $builder->create(
95 | strtolower($key),
96 | CheckboxType::class,
97 | [
98 | 'value' => 1,
99 | 'data' => $option->getValue(),
100 | 'label' => 'OPTION_'.$key,
101 | 'required' => false,
102 | ],
103 | ),
104 | );
105 | }
106 |
107 | /**
108 | * @param FormBuilderInterface $builder
109 | * @param $key
110 | * @param OptionInterface $option
111 | */
112 | private function addIntegerType(FormBuilderInterface $builder, $key, OptionInterface $option): void
113 | {
114 | $builder->add(
115 | $builder->create(
116 | strtolower($key),
117 | IntegerType::class,
118 | [
119 | 'data' => $option->getValue(),
120 | 'label' => 'OPTION_'.$key,
121 | 'required' => false,
122 | ],
123 | ),
124 | );
125 | }
126 |
127 | /**
128 | * {@inheritdoc}
129 | */
130 | public function configureOptions(OptionsResolver $resolver): void
131 | {
132 | $resolver->setDefaults(
133 | [
134 | 'data_class' => Options::class,
135 | 'csrf_protection' => false,
136 | 'generator' => null,
137 | ],
138 | );
139 | }
140 |
141 | public function getName(): string
142 | {
143 | return $this->getBlockPrefix();
144 | }
145 |
146 | public function getBlockPrefix(): string
147 | {
148 | return '';
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/HackzillaPasswordGeneratorBundle.php:
--------------------------------------------------------------------------------
1 | = 8.0.2
16 | * [hackzilla/password-generator](https://github.com/hackzilla/password-generator) ^1.3.0
17 | * Symfony ^6.0|^7.0
18 |
19 |
20 | Version Matrix
21 | --------------
22 |
23 | | Password Generator Bundle | Symfony | PHP |
24 | |---------------------------|------------------------------|----------|
25 | | 6.x | ^6.0 | ^7.0 | >=8.0.2* |
26 | | 5.x | ^4.0 | ^5.0 | ^6.0 | >=7.1* |
27 | | 4.x | ^3.0 | ^4.0 | ^5.0 | >=7.1* |
28 | | 3.x | ^3.0 | ^4.0 | >=7.1 |
29 | | 2.x | ^2.7 | ^3.0 | >=5.5 |
30 | | 1.x | ^2.3 | >=5.3 |
31 |
32 | * Symfony 5.0 requires PHP v7.2+
33 | * Symfony 6.0 requires PHP v8.0.2+
34 | * Symfony 7.0 requires PHP v8.2+
35 |
36 | Installation
37 | ------------
38 |
39 | Add HackzillaPasswordGeneratorBundle in your composer.json:
40 |
41 | ```yaml
42 | {
43 | "require": {
44 | "hackzilla/password-generator-bundle": "^6.0"
45 | }
46 | }
47 | ```
48 |
49 | Install Composer
50 |
51 | ```
52 | curl -sS https://getcomposer.org/installer | php
53 | mv composer.phar /usr/local/bin/composer
54 | ```
55 |
56 | Now tell composer to download the library by running the command:
57 |
58 | ``` bash
59 | $ composer require hackzilla/password-generator-bundle
60 | ```
61 |
62 | Composer will install the bundle into your project's `vendor/hackzilla` directory.
63 |
64 | ### Step 2: Enable the bundle
65 |
66 | Enable the bundle in the kernel:
67 |
68 | ``` php
69 | =8.0.2
97 |
98 | Migrating from v4
99 | -----------------
100 |
101 | Version 5 release is just drops support for Symfony v3.
102 |
103 | Migrating from v3
104 | -----------------
105 |
106 | Version 4 release is just a version bump.
107 |
108 | Migrating from v2
109 | -----------------
110 |
111 | Version 3 release is just a version bump.
112 |
113 |
114 | Migrating from v1
115 | -----------------
116 |
117 | Migration should be straight forward, as much of the changes are related to Symfony v3
118 |
119 | * Upgrade to at least PHP 5.5
120 | * Reference Types by Fully Qualified Class Name (FQCN) (>= Symfony 2.8)
121 | * FormTypes use getBlockPrefix, rather than getName
122 | * OptionType is now a service
123 | * CamelCased services are now lowercase with separator (e.g. hackzilla.password_generator.human.maxWordLength changed to hackzilla.password_generator.human.max_word_length)
124 | * Removed previously deprecated service (hackzilla.password_generator).
125 |
126 | Example Implementation
127 | ----------------------
128 |
129 | See [Password generator app](https://github.com/hackzilla/password-generator-app)
130 |
131 |
132 | Pull Requests
133 | -------------
134 |
135 | I'm open to pull requests for additional languages, features and/or improvements.
136 |
--------------------------------------------------------------------------------
/Resources/config/routing.yml:
--------------------------------------------------------------------------------
1 | hackzilla_password_generator_form:
2 | path: /
3 | controller: Hackzilla\Bundle\PasswordGeneratorBundle\Controller\GeneratorController::formAction
4 |
5 | hackzilla_password_generator_human_form:
6 | path: /human
7 | controller: Hackzilla\Bundle\PasswordGeneratorBundle\Controller\GeneratorController::formAction
8 | defaults: { mode: human }
9 |
10 | hackzilla_password_generator_hybrid_form:
11 | path: /hybrid
12 | controller: Hackzilla\Bundle\PasswordGeneratorBundle\Controller\GeneratorController::formAction
13 | defaults: { mode: hybrid }
14 |
15 | hackzilla_password_generator_computer_form:
16 | path: /computer
17 | controller: Hackzilla\Bundle\PasswordGeneratorBundle\Controller\GeneratorController::formAction
18 | defaults: { mode: computer }
19 |
20 | hackzilla_password_generator_show:
21 | path: /{mode}
22 | controller: Hackzilla\Bundle\PasswordGeneratorBundle\Controller\GeneratorController::generate
23 |
--------------------------------------------------------------------------------
/Resources/config/services.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | hackzilla.password_generator.computer.length: 12
3 |
4 | hackzilla.password_generator.human.word_list: /usr/share/dict/words
5 | hackzilla.password_generator.human.word_separator: '-'
6 | hackzilla.password_generator.human.word_count: 3
7 | hackzilla.password_generator.human.min_word_length: 5
8 | hackzilla.password_generator.human.max_word_length: 10
9 |
10 | hackzilla.password_generator.hybrid.segment_count: 4
11 | hackzilla.password_generator.hybrid.segment_length: 3
12 | hackzilla.password_generator.hybrid.segment_separator: '-'
13 |
14 | services:
15 | hackzilla.password_generator.requirement:
16 | class: Hackzilla\PasswordGenerator\Generator\RequirementPasswordGenerator
17 | public: true
18 |
19 | hackzilla.password_generator.dummy:
20 | class: Hackzilla\PasswordGenerator\Generator\DummyPasswordGenerator
21 | public: true
22 |
23 | hackzilla.password_generator.computer:
24 | class: Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator
25 | public: true
26 | calls:
27 | - [setLength, ["%hackzilla.password_generator.computer.length%"]]
28 |
29 | hackzilla.password_generator.human:
30 | class: Hackzilla\PasswordGenerator\Generator\HumanPasswordGenerator
31 | public: true
32 | calls:
33 | - [setWordCount, ["%hackzilla.password_generator.human.word_count%"]]
34 | - [setMinWordLength, ["%hackzilla.password_generator.human.min_word_length%"]]
35 | - [setMaxWordLength, ["%hackzilla.password_generator.human.max_word_length%"]]
36 | - [setWordSeparator, ["%hackzilla.password_generator.human.word_separator%"]]
37 | - [setWordList, ["%hackzilla.password_generator.human.word_list%"]]
38 |
39 | hackzilla.password_generator.hybrid:
40 | class: Hackzilla\PasswordGenerator\Generator\HybridPasswordGenerator
41 | public: true
42 | calls:
43 | - [setSegmentCount, ["%hackzilla.password_generator.hybrid.segment_count%"]]
44 | - [setSegmentLength, ["%hackzilla.password_generator.hybrid.segment_length%"]]
45 | - [setSegmentSeparator, ["%hackzilla.password_generator.hybrid.segment_separator%"]]
46 |
47 | Hackzilla\Bundle\PasswordGeneratorBundle\Form\Type\OptionType:
48 | tags:
49 | - { name: form.type }
50 |
51 | Hackzilla\Bundle\PasswordGeneratorBundle\Controller\GeneratorController:
52 | public: true
53 | tags: ['controller.service_arguments']
54 | arguments:
55 | - '@hackzilla.password_generator.human'
56 | - '@hackzilla.password_generator.hybrid'
57 | - '@hackzilla.password_generator.computer'
58 | - '@hackzilla.password_generator.requirement'
59 | - '@hackzilla.password_generator.dummy'
60 | - '@form.factory'
61 | - '@twig'
62 | calls:
63 | - [setContainer, ['@service_container']]
64 |
--------------------------------------------------------------------------------
/Resources/translations/messages.bg.xlf:
--------------------------------------------------------------------------------
1 |
2 |
{{ 'HEADING_SUB_DESCRIPTION'|trans }} [{{ 'LABEL_CITATION'|trans }}]
3 | {{ 'MODE_HUMANS'|trans }} 5 | {{ 'MODE_COMPUTERS'|trans }} 7 | {{ 'MODE_HYBRID'|trans }} 9 |