mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 09:35:49 +02:00
Merge branch 'permission_rework'
This commit is contained in:
commit
c529df89a0
102 changed files with 3656 additions and 3510 deletions
|
@ -9,6 +9,8 @@
|
||||||

|

|
||||||
[](https://part-db.crowdin.com/part-db)
|
[](https://part-db.crowdin.com/part-db)
|
||||||
|
|
||||||
|
*When updgrading from a version from before 2022-11-27, please read [this](https://github.com/Part-DB/Part-DB-symfony/discussions/193) before upgrading!*
|
||||||
|
|
||||||
# Part-DB
|
# Part-DB
|
||||||
Part-DB is an Open-Source inventory managment system for your electronic components.
|
Part-DB is an Open-Source inventory managment system for your electronic components.
|
||||||
It is installed on a web server and so can be accessed with any browser without the need to install additional software.
|
It is installed on a web server and so can be accessed with any browser without the need to install additional software.
|
||||||
|
|
|
@ -871,3 +871,10 @@ table.dataTable tr.selected td.select-checkbox:after, table.dataTable tr.selecte
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***********************************************
|
||||||
|
* Permission checkboxes
|
||||||
|
***********************************************/
|
||||||
|
.permission-checkbox:checked {
|
||||||
|
background-color: var(--bs-success);
|
||||||
|
border-color: var(--bs-success);
|
||||||
|
}
|
298
composer.lock
generated
298
composer.lock
generated
|
@ -1455,16 +1455,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/orm",
|
"name": "doctrine/orm",
|
||||||
"version": "2.13.3",
|
"version": "2.13.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/orm.git",
|
"url": "https://github.com/doctrine/orm.git",
|
||||||
"reference": "e750360bd52b080c4cbaaee1b48b80f7dc873b36"
|
"reference": "a5a6cc6630ce497290396d5f206887227820a634"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/orm/zipball/e750360bd52b080c4cbaaee1b48b80f7dc873b36",
|
"url": "https://api.github.com/repos/doctrine/orm/zipball/a5a6cc6630ce497290396d5f206887227820a634",
|
||||||
"reference": "e750360bd52b080c4cbaaee1b48b80f7dc873b36",
|
"reference": "a5a6cc6630ce497290396d5f206887227820a634",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1493,13 +1493,13 @@
|
||||||
"doctrine/annotations": "^1.13",
|
"doctrine/annotations": "^1.13",
|
||||||
"doctrine/coding-standard": "^9.0.2 || ^10.0",
|
"doctrine/coding-standard": "^9.0.2 || ^10.0",
|
||||||
"phpbench/phpbench": "^0.16.10 || ^1.0",
|
"phpbench/phpbench": "^0.16.10 || ^1.0",
|
||||||
"phpstan/phpstan": "~1.4.10 || 1.8.5",
|
"phpstan/phpstan": "~1.4.10 || 1.9.2",
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
||||||
"psr/log": "^1 || ^2 || ^3",
|
"psr/log": "^1 || ^2 || ^3",
|
||||||
"squizlabs/php_codesniffer": "3.7.1",
|
"squizlabs/php_codesniffer": "3.7.1",
|
||||||
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
|
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
|
||||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
|
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
|
||||||
"vimeo/psalm": "4.27.0"
|
"vimeo/psalm": "4.30.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-dom": "Provides support for XSD validation for XML mapping files",
|
"ext-dom": "Provides support for XSD validation for XML mapping files",
|
||||||
|
@ -1549,22 +1549,22 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/orm/issues",
|
"issues": "https://github.com/doctrine/orm/issues",
|
||||||
"source": "https://github.com/doctrine/orm/tree/2.13.3"
|
"source": "https://github.com/doctrine/orm/tree/2.13.4"
|
||||||
},
|
},
|
||||||
"time": "2022-10-07T06:37:17+00:00"
|
"time": "2022-11-20T18:53:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/persistence",
|
"name": "doctrine/persistence",
|
||||||
"version": "3.0.4",
|
"version": "3.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/persistence.git",
|
"url": "https://github.com/doctrine/persistence.git",
|
||||||
"reference": "05612da375f8a3931161f435f91d6704926e6ec5"
|
"reference": "2a9c70a5e21f8968c5a46b79f819ea52f322080b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/persistence/zipball/05612da375f8a3931161f435f91d6704926e6ec5",
|
"url": "https://api.github.com/repos/doctrine/persistence/zipball/2a9c70a5e21f8968c5a46b79f819ea52f322080b",
|
||||||
"reference": "05612da375f8a3931161f435f91d6704926e6ec5",
|
"reference": "2a9c70a5e21f8968c5a46b79f819ea52f322080b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1635,7 +1635,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/persistence/issues",
|
"issues": "https://github.com/doctrine/persistence/issues",
|
||||||
"source": "https://github.com/doctrine/persistence/tree/3.0.4"
|
"source": "https://github.com/doctrine/persistence/tree/3.1.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1651,7 +1651,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-10-13T07:34:14+00:00"
|
"time": "2022-11-18T14:10:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/sql-formatter",
|
"name": "doctrine/sql-formatter",
|
||||||
|
@ -2155,7 +2155,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/florianv/symfony-swap/issues",
|
"issues": "https://github.com/florianv/symfony-swap/issues",
|
||||||
"source": "https://github.com/florianv/symfony-swap/tree/master"
|
"source": "https://github.com/florianv/symfony-swap/tree/v5.4.0"
|
||||||
},
|
},
|
||||||
"time": "2022-06-13T07:34:25+00:00"
|
"time": "2022-06-13T07:34:25+00:00"
|
||||||
},
|
},
|
||||||
|
@ -2366,16 +2366,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "imagine/imagine",
|
"name": "imagine/imagine",
|
||||||
"version": "1.3.2",
|
"version": "1.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/php-imagine/Imagine.git",
|
"url": "https://github.com/php-imagine/Imagine.git",
|
||||||
"reference": "ae864f26afbf8859ebd2e2b9df92d77ee175dc13"
|
"reference": "a6e6da93ea0f76aba33b0e8ed1325523c0413da2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/php-imagine/Imagine/zipball/ae864f26afbf8859ebd2e2b9df92d77ee175dc13",
|
"url": "https://api.github.com/repos/php-imagine/Imagine/zipball/a6e6da93ea0f76aba33b0e8ed1325523c0413da2",
|
||||||
"reference": "ae864f26afbf8859ebd2e2b9df92d77ee175dc13",
|
"reference": "a6e6da93ea0f76aba33b0e8ed1325523c0413da2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2422,9 +2422,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/php-imagine/Imagine/issues",
|
"issues": "https://github.com/php-imagine/Imagine/issues",
|
||||||
"source": "https://github.com/php-imagine/Imagine/tree/1.3.2"
|
"source": "https://github.com/php-imagine/Imagine/tree/1.3.3"
|
||||||
},
|
},
|
||||||
"time": "2022-04-01T11:58:30+00:00"
|
"time": "2022-11-16T13:09:11+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jbtronics/2fa-webauthn",
|
"name": "jbtronics/2fa-webauthn",
|
||||||
|
@ -2485,16 +2485,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laminas/laminas-code",
|
"name": "laminas/laminas-code",
|
||||||
"version": "4.7.0",
|
"version": "4.7.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/laminas/laminas-code.git",
|
"url": "https://github.com/laminas/laminas-code.git",
|
||||||
"reference": "0337d9265bc2e6376babad8c511500821620cb30"
|
"reference": "91aabc066d5620428120800c0eafc0411e441a62"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/laminas/laminas-code/zipball/0337d9265bc2e6376babad8c511500821620cb30",
|
"url": "https://api.github.com/repos/laminas/laminas-code/zipball/91aabc066d5620428120800c0eafc0411e441a62",
|
||||||
"reference": "0337d9265bc2e6376babad8c511500821620cb30",
|
"reference": "91aabc066d5620428120800c0eafc0411e441a62",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2547,7 +2547,7 @@
|
||||||
"type": "community_bridge"
|
"type": "community_bridge"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-09-13T10:33:30+00:00"
|
"time": "2022-11-21T01:32:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lcobucci/clock",
|
"name": "lcobucci/clock",
|
||||||
|
@ -3344,16 +3344,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
"version": "v4.15.1",
|
"version": "v4.15.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
"reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900"
|
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
||||||
"reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
|
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3394,9 +3394,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1"
|
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
|
||||||
},
|
},
|
||||||
"time": "2022-09-04T07:30:47+00:00"
|
"time": "2022-11-12T15:38:23+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikolaposa/version",
|
"name": "nikolaposa/version",
|
||||||
|
@ -8492,16 +8492,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
|
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||||
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -8516,7 +8516,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -8554,7 +8554,7 @@
|
||||||
"portable"
|
"portable"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -8570,20 +8570,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-grapheme",
|
"name": "symfony/polyfill-intl-grapheme",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||||
"reference": "433d05519ce6990bf3530fba6957499d327395c2"
|
"reference": "511a08c03c1960e08a883f4cffcacd219b758354"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
|
||||||
"reference": "433d05519ce6990bf3530fba6957499d327395c2",
|
"reference": "511a08c03c1960e08a883f4cffcacd219b758354",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -8595,7 +8595,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -8635,7 +8635,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -8651,20 +8651,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-icu",
|
"name": "symfony/polyfill-intl-icu",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-icu.git",
|
"url": "https://github.com/symfony/polyfill-intl-icu.git",
|
||||||
"reference": "e407643d610e5f2c8a4b14189150f68934bf5e48"
|
"reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/e407643d610e5f2c8a4b14189150f68934bf5e48",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/a3d9148e2c363588e05abbdd4ee4f971f0a5330c",
|
||||||
"reference": "e407643d610e5f2c8a4b14189150f68934bf5e48",
|
"reference": "a3d9148e2c363588e05abbdd4ee4f971f0a5330c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -8676,7 +8676,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -8722,7 +8722,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -8738,20 +8738,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-idn",
|
"name": "symfony/polyfill-intl-idn",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
||||||
"reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8"
|
"reference": "639084e360537a19f9ee352433b84ce831f3d2da"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da",
|
||||||
"reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
|
"reference": "639084e360537a19f9ee352433b84ce831f3d2da",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -8765,7 +8765,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -8809,7 +8809,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -8825,20 +8825,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-normalizer",
|
"name": "symfony/polyfill-intl-normalizer",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||||
"reference": "219aa369ceff116e673852dce47c3a41794c14bd"
|
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
|
||||||
"reference": "219aa369ceff116e673852dce47c3a41794c14bd",
|
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -8850,7 +8850,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -8893,7 +8893,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -8909,20 +8909,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
|
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
|
||||||
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -8937,7 +8937,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -8976,7 +8976,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -8992,20 +8992,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php72",
|
"name": "symfony/polyfill-php72",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||||
"reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2"
|
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2",
|
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
|
||||||
"reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2",
|
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -9014,7 +9014,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -9052,7 +9052,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php72/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -9068,20 +9068,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php73",
|
"name": "symfony/polyfill-php73",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php73.git",
|
"url": "https://github.com/symfony/polyfill-php73.git",
|
||||||
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
|
"reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
|
||||||
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
"reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -9090,7 +9090,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -9131,7 +9131,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -9147,20 +9147,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php80",
|
"name": "symfony/polyfill-php80",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
|
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -9169,7 +9169,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -9214,7 +9214,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -9230,20 +9230,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-10T07:21:04+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php81",
|
"name": "symfony/polyfill-php81",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||||
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
|
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
|
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
|
||||||
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
|
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -9252,7 +9252,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -9293,7 +9293,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -9309,7 +9309,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-24T11:49:31+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/process",
|
"name": "symfony/process",
|
||||||
|
@ -13202,16 +13202,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/pcre",
|
"name": "composer/pcre",
|
||||||
"version": "3.0.2",
|
"version": "3.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/pcre.git",
|
"url": "https://github.com/composer/pcre.git",
|
||||||
"reference": "4482b6409ca6bfc2af043a5711cd21ac3e7a8dfb"
|
"reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/pcre/zipball/4482b6409ca6bfc2af043a5711cd21ac3e7a8dfb",
|
"url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2",
|
||||||
"reference": "4482b6409ca6bfc2af043a5711cd21ac3e7a8dfb",
|
"reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -13253,7 +13253,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/composer/pcre/issues",
|
"issues": "https://github.com/composer/pcre/issues",
|
||||||
"source": "https://github.com/composer/pcre/tree/3.0.2"
|
"source": "https://github.com/composer/pcre/tree/3.1.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -13269,7 +13269,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-11-03T20:24:16+00:00"
|
"time": "2022-11-17T09:50:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
|
@ -14003,16 +14003,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "1.9.1",
|
"version": "1.9.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f"
|
"reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d6fdf01c53978b6429f1393ba4afeca39cc68afa",
|
||||||
"reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f",
|
"reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -14042,7 +14042,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan/tree/1.9.1"
|
"source": "https://github.com/phpstan/phpstan/tree/1.9.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -14058,20 +14058,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-11-04T13:35:59+00:00"
|
"time": "2022-11-10T09:56:11+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-doctrine",
|
"name": "phpstan/phpstan-doctrine",
|
||||||
"version": "1.3.22",
|
"version": "1.3.23",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
||||||
"reference": "5080276a271a4ef71fbe33f4fb68f181dffeb03d"
|
"reference": "964caf844c89134e5c2f19e97cbf8b5d12193779"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/5080276a271a4ef71fbe33f4fb68f181dffeb03d",
|
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/964caf844c89134e5c2f19e97cbf8b5d12193779",
|
||||||
"reference": "5080276a271a4ef71fbe33f4fb68f181dffeb03d",
|
"reference": "964caf844c89134e5c2f19e97cbf8b5d12193779",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -14125,9 +14125,9 @@
|
||||||
"description": "Doctrine extensions for PHPStan",
|
"description": "Doctrine extensions for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.22"
|
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.23"
|
||||||
},
|
},
|
||||||
"time": "2022-11-03T15:13:13+00:00"
|
"time": "2022-11-14T07:46:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-symfony",
|
"name": "phpstan/phpstan-symfony",
|
||||||
|
@ -14271,12 +14271,12 @@
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||||
"reference": "964c5d9ca40d0ec72db203b3dd6382a30abef616"
|
"reference": "0399700d159e09b16645945758b65b921d3491fe"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/964c5d9ca40d0ec72db203b3dd6382a30abef616",
|
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0399700d159e09b16645945758b65b921d3491fe",
|
||||||
"reference": "964c5d9ca40d0ec72db203b3dd6382a30abef616",
|
"reference": "0399700d159e09b16645945758b65b921d3491fe",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
|
@ -14300,6 +14300,7 @@
|
||||||
"asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
|
"asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
|
||||||
"awesome-support/awesome-support": "<=6.0.7",
|
"awesome-support/awesome-support": "<=6.0.7",
|
||||||
"aws/aws-sdk-php": ">=3,<3.2.1",
|
"aws/aws-sdk-php": ">=3,<3.2.1",
|
||||||
|
"backdrop/backdrop": "<=1.23",
|
||||||
"badaso/core": "<2.6.1",
|
"badaso/core": "<2.6.1",
|
||||||
"bagisto/bagisto": "<0.1.5",
|
"bagisto/bagisto": "<0.1.5",
|
||||||
"barrelstrength/sprout-base-email": "<1.2.7",
|
"barrelstrength/sprout-base-email": "<1.2.7",
|
||||||
|
@ -14333,7 +14334,7 @@
|
||||||
"codeigniter4/shield": "= 1.0.0-beta",
|
"codeigniter4/shield": "= 1.0.0-beta",
|
||||||
"codiad/codiad": "<=2.8.4",
|
"codiad/codiad": "<=2.8.4",
|
||||||
"composer/composer": "<1.10.26|>=2-alpha.1,<2.2.12|>=2.3,<2.3.5",
|
"composer/composer": "<1.10.26|>=2-alpha.1,<2.2.12|>=2.3,<2.3.5",
|
||||||
"concrete5/concrete5": "<9",
|
"concrete5/concrete5": "<9.1.3|>= 9.0.0RC1, < 9.1.3",
|
||||||
"concrete5/core": "<8.5.8|>=9,<9.1",
|
"concrete5/core": "<8.5.8|>=9,<9.1",
|
||||||
"contao-components/mediaelement": ">=2.14.2,<2.21.1",
|
"contao-components/mediaelement": ">=2.14.2,<2.21.1",
|
||||||
"contao/contao": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3",
|
"contao/contao": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|>=4.13,<4.13.3",
|
||||||
|
@ -14359,7 +14360,7 @@
|
||||||
"doctrine/mongodb-odm": ">=1,<1.0.2",
|
"doctrine/mongodb-odm": ">=1,<1.0.2",
|
||||||
"doctrine/mongodb-odm-bundle": ">=2,<3.0.1",
|
"doctrine/mongodb-odm-bundle": ">=2,<3.0.1",
|
||||||
"doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4",
|
"doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4",
|
||||||
"dolibarr/dolibarr": "<16|= 12.0.5|>= 3.3.beta1, < 13.0.2",
|
"dolibarr/dolibarr": "<16|>=16.0.1,<16.0.3|= 12.0.5|>= 3.3.beta1, < 13.0.2",
|
||||||
"dompdf/dompdf": "<2.0.1",
|
"dompdf/dompdf": "<2.0.1",
|
||||||
"drupal/core": ">=7,<7.91|>=8,<9.3.19|>=9.4,<9.4.3",
|
"drupal/core": ">=7,<7.91|>=8,<9.3.19|>=9.4,<9.4.3",
|
||||||
"drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4",
|
"drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4",
|
||||||
|
@ -14379,16 +14380,17 @@
|
||||||
"ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1",
|
"ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1",
|
||||||
"ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1",
|
"ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1",
|
||||||
"ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24",
|
"ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24",
|
||||||
"ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.27",
|
"ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26",
|
||||||
"ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1",
|
"ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1",
|
||||||
"ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.19",
|
"ezsystems/ezplatform-graphql": ">=1-rc.1,<1.0.13|>=2-beta.1,<2.3.12",
|
||||||
|
"ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.26",
|
||||||
"ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8",
|
"ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8",
|
||||||
"ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7",
|
"ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7",
|
||||||
"ezsystems/ezplatform-user": ">=1,<1.0.1",
|
"ezsystems/ezplatform-user": ">=1,<1.0.1",
|
||||||
"ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.29",
|
"ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.30",
|
||||||
"ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1",
|
"ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1",
|
||||||
"ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3",
|
"ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3",
|
||||||
"ezsystems/repository-forms": ">=2.3,<2.3.2.1",
|
"ezsystems/repository-forms": ">=2.3,<2.3.2.1|>=2.5,<2.5.15",
|
||||||
"ezyang/htmlpurifier": "<4.1.1",
|
"ezyang/htmlpurifier": "<4.1.1",
|
||||||
"facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2",
|
"facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2",
|
||||||
"facturascripts/facturascripts": "<=2022.8",
|
"facturascripts/facturascripts": "<=2022.8",
|
||||||
|
@ -14397,7 +14399,7 @@
|
||||||
"fenom/fenom": "<=2.12.1",
|
"fenom/fenom": "<=2.12.1",
|
||||||
"filegator/filegator": "<7.8",
|
"filegator/filegator": "<7.8",
|
||||||
"firebase/php-jwt": "<2",
|
"firebase/php-jwt": "<2",
|
||||||
"flarum/core": ">=1,<=1.0.1",
|
"flarum/core": ">=1,<=1.0.1|>=1.5,<1.6.2",
|
||||||
"flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15",
|
"flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15",
|
||||||
"flarum/tags": "<=0.1-beta.13",
|
"flarum/tags": "<=0.1-beta.13",
|
||||||
"fluidtypo3/vhs": "<5.1.1",
|
"fluidtypo3/vhs": "<5.1.1",
|
||||||
|
@ -14434,7 +14436,9 @@
|
||||||
"hjue/justwriting": "<=1",
|
"hjue/justwriting": "<=1",
|
||||||
"hov/jobfair": "<1.0.13|>=2,<2.0.2",
|
"hov/jobfair": "<1.0.13|>=2,<2.0.2",
|
||||||
"hyn/multi-tenant": ">=5.6,<5.7.2",
|
"hyn/multi-tenant": ">=5.6,<5.7.2",
|
||||||
"ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4",
|
"ibexa/admin-ui": ">=4.2,<4.2.3",
|
||||||
|
"ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3",
|
||||||
|
"ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3",
|
||||||
"ibexa/post-install": "<=1.0.4",
|
"ibexa/post-install": "<=1.0.4",
|
||||||
"icecoder/icecoder": "<=8.1",
|
"icecoder/icecoder": "<=8.1",
|
||||||
"idno/known": "<=1.3.1",
|
"idno/known": "<=1.3.1",
|
||||||
|
@ -14451,6 +14455,7 @@
|
||||||
"ivankristianto/phpwhois": "<=4.3",
|
"ivankristianto/phpwhois": "<=4.3",
|
||||||
"jackalope/jackalope-doctrine-dbal": "<1.7.4",
|
"jackalope/jackalope-doctrine-dbal": "<1.7.4",
|
||||||
"james-heinrich/getid3": "<1.9.21",
|
"james-heinrich/getid3": "<1.9.21",
|
||||||
|
"jasig/phpcas": "<1.3.3",
|
||||||
"joomla/archive": "<1.1.12|>=2,<2.0.1",
|
"joomla/archive": "<1.1.12|>=2,<2.0.1",
|
||||||
"joomla/filesystem": "<1.6.2|>=2,<2.0.1",
|
"joomla/filesystem": "<1.6.2|>=2,<2.0.1",
|
||||||
"joomla/filter": "<1.4.4|>=2,<2.0.1",
|
"joomla/filter": "<1.4.4|>=2,<2.0.1",
|
||||||
|
@ -14478,7 +14483,7 @@
|
||||||
"league/commonmark": "<0.18.3",
|
"league/commonmark": "<0.18.3",
|
||||||
"league/flysystem": "<1.1.4|>=2,<2.1.1",
|
"league/flysystem": "<1.1.4|>=2,<2.1.1",
|
||||||
"lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3",
|
"lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3",
|
||||||
"librenms/librenms": "<=22.8",
|
"librenms/librenms": "<22.10",
|
||||||
"limesurvey/limesurvey": "<3.27.19",
|
"limesurvey/limesurvey": "<3.27.19",
|
||||||
"livehelperchat/livehelperchat": "<=3.91",
|
"livehelperchat/livehelperchat": "<=3.91",
|
||||||
"livewire/livewire": ">2.2.4,<2.2.6",
|
"livewire/livewire": ">2.2.4,<2.2.6",
|
||||||
|
@ -14503,7 +14508,7 @@
|
||||||
"modx/revolution": "<= 2.8.3-pl|<2.8",
|
"modx/revolution": "<= 2.8.3-pl|<2.8",
|
||||||
"mojo42/jirafeau": "<4.4",
|
"mojo42/jirafeau": "<4.4",
|
||||||
"monolog/monolog": ">=1.8,<1.12",
|
"monolog/monolog": ">=1.8,<1.12",
|
||||||
"moodle/moodle": "<4.0.1",
|
"moodle/moodle": "<4.0.5",
|
||||||
"mustache/mustache": ">=2,<2.14.1",
|
"mustache/mustache": ">=2,<2.14.1",
|
||||||
"namshi/jose": "<2.2",
|
"namshi/jose": "<2.2",
|
||||||
"neoan3-apps/template": "<1.1.1",
|
"neoan3-apps/template": "<1.1.1",
|
||||||
|
@ -14603,12 +14608,12 @@
|
||||||
"shopware/storefront": "<=6.4.8.1",
|
"shopware/storefront": "<=6.4.8.1",
|
||||||
"shopxo/shopxo": "<2.2.6",
|
"shopxo/shopxo": "<2.2.6",
|
||||||
"showdoc/showdoc": "<2.10.4",
|
"showdoc/showdoc": "<2.10.4",
|
||||||
"silverstripe/admin": ">=1,<1.8.1",
|
"silverstripe/admin": ">=1,<1.11.3",
|
||||||
"silverstripe/assets": ">=1,<1.10.1",
|
"silverstripe/assets": ">=1,<1.11.1",
|
||||||
"silverstripe/cms": "<4.3.6|>=4.4,<4.4.4",
|
"silverstripe/cms": "<4.11.3",
|
||||||
"silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1",
|
"silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1",
|
||||||
"silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
|
"silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
|
||||||
"silverstripe/framework": "<4.10.9",
|
"silverstripe/framework": "<4.11.14",
|
||||||
"silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2|= 4.0.0-alpha1",
|
"silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2|= 4.0.0-alpha1",
|
||||||
"silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1",
|
"silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1",
|
||||||
"silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1",
|
"silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1",
|
||||||
|
@ -14617,6 +14622,7 @@
|
||||||
"silverstripe/subsites": ">=2,<2.1.1",
|
"silverstripe/subsites": ">=2,<2.1.1",
|
||||||
"silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1",
|
"silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1",
|
||||||
"silverstripe/userforms": "<3",
|
"silverstripe/userforms": "<3",
|
||||||
|
"silverstripe/versioned-admin": ">=1,<1.11.1",
|
||||||
"simple-updates/phpwhois": "<=1",
|
"simple-updates/phpwhois": "<=1",
|
||||||
"simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4",
|
"simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4",
|
||||||
"simplesamlphp/simplesamlphp": "<1.18.6",
|
"simplesamlphp/simplesamlphp": "<1.18.6",
|
||||||
|
@ -14691,7 +14697,7 @@
|
||||||
"topthink/framework": "<=6.0.13",
|
"topthink/framework": "<=6.0.13",
|
||||||
"topthink/think": "<=6.0.9",
|
"topthink/think": "<=6.0.9",
|
||||||
"topthink/thinkphp": "<=3.2.3",
|
"topthink/thinkphp": "<=3.2.3",
|
||||||
"tribalsystems/zenario": "<9.2.55826",
|
"tribalsystems/zenario": "<=9.3.57186",
|
||||||
"truckersmp/phpwhois": "<=4.3.1",
|
"truckersmp/phpwhois": "<=4.3.1",
|
||||||
"twig/twig": "<1.44.7|>=2,<2.15.3|>=3,<3.4.3",
|
"twig/twig": "<1.44.7|>=2,<2.15.3|>=3,<3.4.3",
|
||||||
"typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.32|>=11,<11.5.16",
|
"typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.32|>=11,<11.5.16",
|
||||||
|
@ -14727,7 +14733,7 @@
|
||||||
"yetiforce/yetiforce-crm": "<=6.4",
|
"yetiforce/yetiforce-crm": "<=6.4",
|
||||||
"yidashi/yii2cmf": "<=2",
|
"yidashi/yii2cmf": "<=2",
|
||||||
"yii2mod/yii2-cms": "<1.9.2",
|
"yii2mod/yii2-cms": "<1.9.2",
|
||||||
"yiisoft/yii": ">=1.1.14,<1.1.15",
|
"yiisoft/yii": "<1.1.27",
|
||||||
"yiisoft/yii2": "<2.0.38",
|
"yiisoft/yii2": "<2.0.38",
|
||||||
"yiisoft/yii2-bootstrap": "<2.0.4",
|
"yiisoft/yii2-bootstrap": "<2.0.4",
|
||||||
"yiisoft/yii2-dev": "<2.0.43",
|
"yiisoft/yii2-dev": "<2.0.43",
|
||||||
|
@ -14798,7 +14804,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-11-04T21:04:09+00:00"
|
"time": "2022-11-23T23:04:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/diff",
|
"name": "sebastian/diff",
|
||||||
|
@ -15348,16 +15354,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symplify/easy-coding-standard",
|
"name": "symplify/easy-coding-standard",
|
||||||
"version": "11.1.16",
|
"version": "11.1.17",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symplify/easy-coding-standard.git",
|
"url": "https://github.com/symplify/easy-coding-standard.git",
|
||||||
"reference": "32ebd57f0f47713540df8ea39134adaa9d912fae"
|
"reference": "2a98e5b976a3ab573d8e5604d6eb39d9f5783760"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symplify/easy-coding-standard/zipball/32ebd57f0f47713540df8ea39134adaa9d912fae",
|
"url": "https://api.github.com/repos/symplify/easy-coding-standard/zipball/2a98e5b976a3ab573d8e5604d6eb39d9f5783760",
|
||||||
"reference": "32ebd57f0f47713540df8ea39134adaa9d912fae",
|
"reference": "2a98e5b976a3ab573d8e5604d6eb39d9f5783760",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -15387,7 +15393,7 @@
|
||||||
],
|
],
|
||||||
"description": "Prefixed scoped version of ECS package",
|
"description": "Prefixed scoped version of ECS package",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symplify/easy-coding-standard/tree/11.1.16"
|
"source": "https://github.com/symplify/easy-coding-standard/tree/11.1.17"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -15399,7 +15405,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-10-27T10:48:13+00:00"
|
"time": "2022-11-10T15:20:49+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vimeo/psalm",
|
"name": "vimeo/psalm",
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
# This should be compatible with the legacy Part-DB
|
# This should be compatible with the legacy Part-DB
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
parts:
|
#parts:
|
||||||
label: "perm.group.parts"
|
# label: "perm.group.parts"
|
||||||
structures:
|
#structures:
|
||||||
label: "perm.group.structures"
|
# label: "perm.group.structures"
|
||||||
|
data:
|
||||||
|
label: "perm.group.data"
|
||||||
system:
|
system:
|
||||||
label: "perm.group.system"
|
label: "perm.group.system"
|
||||||
|
|
||||||
|
@ -15,221 +17,52 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
# Part related permissions
|
# Part related permissions
|
||||||
|
|
||||||
parts: # e.g. this maps to perms_parts in User/Group database
|
parts: # e.g. this maps to perms_parts in User/Group database
|
||||||
group: "parts"
|
group: "data"
|
||||||
label: "perm.parts"
|
label: "perm.parts"
|
||||||
operations: # Here are all possible operations are listed => the op name is mapped to bit value
|
operations: # Here are all possible operations are listed => the op name is mapped to bit value
|
||||||
read:
|
read:
|
||||||
label: "perm.read"
|
label: "perm.read"
|
||||||
bit: 0
|
# If a part can be read by a user, he can also see all the datastructures (except devices)
|
||||||
|
alsoSet: ['storelocations.read', 'footprints.read', 'categories.read', 'suppliers.read', 'manufacturers.read',
|
||||||
|
'currencies.read', 'attachment_types.read', 'measurement_units.read']
|
||||||
edit:
|
edit:
|
||||||
label: "perm.edit"
|
label: "perm.edit"
|
||||||
bit: 2
|
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
bit: 4
|
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
#move:
|
|
||||||
# label: "perm.part.move"
|
|
||||||
# bit: 6
|
|
||||||
# alsoSet: 'read'
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
bit: 8
|
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
search:
|
|
||||||
label: "perm.part.search"
|
|
||||||
bit: 10
|
|
||||||
all_parts:
|
|
||||||
label: "perm.part.all_parts"
|
|
||||||
bit: 12
|
|
||||||
no_price_parts:
|
|
||||||
label: "perm.part.no_price_parts"
|
|
||||||
bit: 14
|
|
||||||
obsolete_parts:
|
|
||||||
label: "perm.part.obsolete_parts"
|
|
||||||
bit: 16
|
|
||||||
unknown_instock_parts:
|
|
||||||
label: "perm.part.unknown_instock_parts"
|
|
||||||
bit: 18
|
|
||||||
change_favorite:
|
change_favorite:
|
||||||
label: "perm.part.change_favorite"
|
label: "perm.part.change_favorite"
|
||||||
bit: 20
|
alsoSet: ['edit']
|
||||||
show_favorite_parts:
|
|
||||||
label: "perm.part.show_favorite"
|
|
||||||
bit: 24
|
|
||||||
show_last_edit_parts:
|
|
||||||
label: "perm.part.show_last_edit_parts"
|
|
||||||
bit: 26
|
|
||||||
show_users:
|
|
||||||
label: "perm.part.show_users"
|
|
||||||
bit: 28
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.part.show_history"
|
label: "perm.part.show_history"
|
||||||
bit: 30
|
alsoSet: ['read']
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
bit: 32
|
|
||||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
||||||
|
|
||||||
parts_name: &PART_ATTRIBUTE # We define a template here, that we can use for all part attributes.
|
|
||||||
label: "perm.part.name"
|
|
||||||
group: "parts"
|
|
||||||
operations:
|
|
||||||
read:
|
|
||||||
label: "perm.read"
|
|
||||||
bit: 0
|
|
||||||
edit:
|
|
||||||
label: "perm.edit"
|
|
||||||
bit: 2
|
|
||||||
alsoSet: 'read'
|
|
||||||
|
|
||||||
parts_category:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.category"
|
|
||||||
|
|
||||||
parts_description:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.description"
|
|
||||||
|
|
||||||
parts_minamount:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.minamount"
|
|
||||||
|
|
||||||
parts_footprint:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.footprint"
|
|
||||||
|
|
||||||
parts_comment:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.comment"
|
|
||||||
|
|
||||||
parts_manufacturer:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.manufacturer"
|
|
||||||
|
|
||||||
parts_mpn:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.mpn"
|
|
||||||
|
|
||||||
parts_status:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.status"
|
|
||||||
|
|
||||||
parts_tags:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.tags"
|
|
||||||
|
|
||||||
parts_unit:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.unit"
|
|
||||||
|
|
||||||
parts_mass:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.mass"
|
|
||||||
|
|
||||||
parts_orderdetails: &PART_MULTI_ATTRIBUTE
|
|
||||||
label: "perm.part.orderdetails"
|
|
||||||
group: "parts"
|
|
||||||
operations:
|
|
||||||
read:
|
|
||||||
label: "perm.read"
|
|
||||||
bit: 0
|
|
||||||
edit:
|
|
||||||
label: "perm.edit"
|
|
||||||
bit: 2
|
|
||||||
alsoSet: 'read'
|
|
||||||
create:
|
|
||||||
label: "perm.create"
|
|
||||||
bit: 4
|
|
||||||
alsoSet: ['read', 'edit']
|
|
||||||
delete:
|
|
||||||
label: "perm.delete"
|
|
||||||
bit: 6
|
|
||||||
alsoSet: ['read']
|
|
||||||
|
|
||||||
|
|
||||||
parts_prices:
|
|
||||||
<<: *PART_MULTI_ATTRIBUTE
|
|
||||||
label: "perm.part.prices"
|
|
||||||
|
|
||||||
parts_parameters:
|
|
||||||
<<: *PART_MULTI_ATTRIBUTE
|
|
||||||
label: "perm.part.parameters"
|
|
||||||
|
|
||||||
parts_lots:
|
|
||||||
<<: *PART_MULTI_ATTRIBUTE
|
|
||||||
label: "perm.part.lots"
|
|
||||||
|
|
||||||
parts_attachments:
|
|
||||||
group: "structures"
|
|
||||||
label: "perm.part.attachments"
|
|
||||||
operations:
|
|
||||||
read:
|
|
||||||
label: "perm.read"
|
|
||||||
bit: 0
|
|
||||||
edit:
|
|
||||||
label: "perm.edit"
|
|
||||||
bit: 2
|
|
||||||
alsoSet: 'read'
|
|
||||||
create:
|
|
||||||
label: "perm.create"
|
|
||||||
bit: 4
|
|
||||||
alsoSet: ['read', 'edit']
|
|
||||||
delete:
|
|
||||||
label: "perm.delete"
|
|
||||||
bit: 6
|
|
||||||
alsoSet: ['read']
|
|
||||||
show_history:
|
|
||||||
label: "perm.show_history"
|
|
||||||
bit: 8
|
|
||||||
revert_element:
|
|
||||||
label: "perm.revert_elements"
|
|
||||||
bit: 10
|
|
||||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
|
||||||
show_private:
|
|
||||||
label: "perm.attachment_show_private"
|
|
||||||
bit: 12
|
|
||||||
alsoSet: ["read"]
|
|
||||||
|
|
||||||
parts_order:
|
|
||||||
<<: *PART_ATTRIBUTE
|
|
||||||
label: "perm.part.order"
|
|
||||||
|
|
||||||
storelocations: &PART_CONTAINING
|
storelocations: &PART_CONTAINING
|
||||||
label: "perm.storelocations"
|
label: "perm.storelocations"
|
||||||
group: "structures"
|
group: "data"
|
||||||
operations:
|
operations:
|
||||||
read:
|
read:
|
||||||
label: "perm.read"
|
label: "perm.read"
|
||||||
bit: 0
|
|
||||||
edit:
|
edit:
|
||||||
label: "perm.edit"
|
label: "perm.edit"
|
||||||
bit: 2
|
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
bit: 4
|
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
move:
|
|
||||||
label: "perm.move"
|
|
||||||
bit: 6
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
bit: 8
|
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
list_parts:
|
|
||||||
label: "perm.list_parts"
|
|
||||||
bit: 10
|
|
||||||
show_users:
|
|
||||||
label: "perm.show_users"
|
|
||||||
bit: 12
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
bit: 14
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
bit: 16
|
|
||||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
||||||
|
|
||||||
footprints:
|
footprints:
|
||||||
|
@ -267,36 +100,24 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
tools:
|
tools:
|
||||||
label: "perm.part.tools"
|
label: "perm.part.tools"
|
||||||
operations:
|
operations:
|
||||||
import:
|
#import:
|
||||||
label: "perm.tools.import"
|
# label: "perm.tools.import"
|
||||||
bit: 0
|
#labels:
|
||||||
labels:
|
# label: "perm.tools.labels"
|
||||||
label: "perm.tools.labels"
|
|
||||||
bit: 2
|
|
||||||
#calculator:
|
#calculator:
|
||||||
# label: "perm.tools.calculator"
|
# label: "perm.tools.calculator"
|
||||||
# bit: 4
|
|
||||||
#footprints:
|
#footprints:
|
||||||
# label: "perm.tools.footprints"
|
# label: "perm.tools.footprints"
|
||||||
# bit: 6
|
|
||||||
#ic_logos:
|
#ic_logos:
|
||||||
# label: "perm.tools.ic_logos"
|
# label: "perm.tools.ic_logos"
|
||||||
# bit: 8
|
|
||||||
statistics:
|
statistics:
|
||||||
label: "perm.tools.statistics"
|
label: "perm.tools.statistics"
|
||||||
bit: 10
|
|
||||||
lastActivity:
|
lastActivity:
|
||||||
label: "perm.tools.lastActivity"
|
label: "perm.tools.lastActivity"
|
||||||
bit: 12
|
|
||||||
timetravel:
|
|
||||||
label: "perm.tools.timeTravel"
|
|
||||||
bit: 14
|
|
||||||
label_scanner:
|
label_scanner:
|
||||||
label: "perm.tools.label_scanner"
|
label: "perm.tools.label_scanner"
|
||||||
bit: 16
|
|
||||||
reel_calculator:
|
reel_calculator:
|
||||||
label: "perm.tools.reel_calculator"
|
label: "perm.tools.reel_calculator"
|
||||||
bit: 18
|
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
label: "perm.groups"
|
label: "perm.groups"
|
||||||
|
@ -304,33 +125,23 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
operations:
|
operations:
|
||||||
read:
|
read:
|
||||||
label: "perm.read"
|
label: "perm.read"
|
||||||
bit: 0
|
|
||||||
edit:
|
edit:
|
||||||
label: "perm.edit"
|
label: "perm.edit"
|
||||||
bit: 2
|
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
bit: 4
|
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
move:
|
|
||||||
label: "perm.move"
|
|
||||||
bit: 6
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
bit: 8
|
|
||||||
alsoSet: ['read', 'delete']
|
alsoSet: ['read', 'delete']
|
||||||
edit_permissions:
|
edit_permissions:
|
||||||
label: "perm.edit_permissions"
|
label: "perm.edit_permissions"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
bit: 10
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
bit: 12
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
bit: 14
|
alsoSet: ["read", "edit", "create", "delete", "edit_permissions", "show_history"]
|
||||||
alsoSet: ["read", "edit", "create", "delete", "move", "edit_permissions", "show_history"]
|
|
||||||
|
|
||||||
users:
|
users:
|
||||||
label: "perm.users"
|
label: "perm.users"
|
||||||
|
@ -338,167 +149,119 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
operations:
|
operations:
|
||||||
read:
|
read:
|
||||||
label: "perm.read"
|
label: "perm.read"
|
||||||
bit: 0
|
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
alsoSet: ['read', 'edit_username', 'edit_infos']
|
alsoSet: ['read', 'edit_username', 'edit_infos']
|
||||||
bit: 4
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
alsoSet: ['read', 'edit_username', 'edit_infos']
|
alsoSet: ['read', 'edit_username', 'edit_infos']
|
||||||
bit: 8
|
|
||||||
edit_username:
|
edit_username:
|
||||||
label: "perm.users.edit_user_name"
|
label: "perm.users.edit_user_name"
|
||||||
alsoSet: ['read']
|
alsoSet: ['read']
|
||||||
bit: 2
|
|
||||||
change_group:
|
|
||||||
label: "perm.users.edit_change_group"
|
|
||||||
alsoSet: 'read'
|
|
||||||
bit: 6
|
|
||||||
edit_infos:
|
edit_infos:
|
||||||
label: "perm.users.edit_infos"
|
label: "perm.users.edit_infos"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
bit: 10
|
|
||||||
edit_permissions:
|
edit_permissions:
|
||||||
label: "perm.users.edit_permissions"
|
label: "perm.users.edit_permissions"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
bit: 12
|
|
||||||
set_password:
|
set_password:
|
||||||
label: "perm.users.set_password"
|
label: "perm.users.set_password"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
bit: 14
|
|
||||||
change_user_settings:
|
change_user_settings:
|
||||||
label: "perm.users.change_user_settings"
|
label: "perm.users.change_user_settings"
|
||||||
bit: 16
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
bit: 18
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
bit: 20
|
alsoSet: ["read", "create", "delete", "edit_permissions", "show_history", "edit_infos", "edit_username"]
|
||||||
alsoSet: ["read", "create", "delete", "edit_permissions", "show_history", "edit_infos", "change_group", "edit_username"]
|
|
||||||
|
|
||||||
database:
|
#database:
|
||||||
label: "perm.database"
|
# label: "perm.database"
|
||||||
group: "system"
|
# group: "system"
|
||||||
operations:
|
# operations:
|
||||||
see_status:
|
# see_status:
|
||||||
label: "perm.database.see_status"
|
# label: "perm.database.see_status"
|
||||||
bit: 0
|
# update_db:
|
||||||
update_db:
|
# label: "perm.database.update_db"
|
||||||
label: "perm.database.update_db"
|
# alsoSet: 'see_status'
|
||||||
bit: 2
|
# read_db_settings:
|
||||||
alsoSet: 'see_status'
|
# label: "perm.database.read_db_settings"
|
||||||
read_db_settings:
|
# write_db_settings:
|
||||||
label: "perm.database.read_db_settings"
|
# label: "perm.database.write_db_settings"
|
||||||
bit: 4
|
# alsoSet: ['read_db_settings', 'see_status']
|
||||||
write_db_settings:
|
|
||||||
label: "perm.database.write_db_settings"
|
|
||||||
alsoSet: ['read_db_settings', 'see_status']
|
|
||||||
bit: 2
|
|
||||||
|
|
||||||
config:
|
#config:
|
||||||
label: "perm.config"
|
# label: "perm.config"
|
||||||
group: "system"
|
# group: "system"
|
||||||
operations:
|
# operations:
|
||||||
read_config:
|
# read_config:
|
||||||
label: "perm.config.read_config"
|
# label: "perm.config.read_config"
|
||||||
bit: 0
|
# edit_config:
|
||||||
edit_config:
|
# label: "perm.config.edit_config"
|
||||||
label: "perm.config.edit_config"
|
# alsoSet: 'read_config'
|
||||||
alsoSet: 'read_config'
|
# server_info:
|
||||||
bit: 2
|
# label: "perm.config.server_info"
|
||||||
server_info:
|
|
||||||
label: "perm.config.server_info"
|
|
||||||
bit: 6
|
|
||||||
|
|
||||||
system:
|
system:
|
||||||
label: "perm.system"
|
label: "perm.system"
|
||||||
group: "system"
|
group: "system"
|
||||||
operations:
|
operations:
|
||||||
use_debug:
|
|
||||||
label: "perm.config.use_debug"
|
|
||||||
bit: 0
|
|
||||||
show_logs:
|
show_logs:
|
||||||
label: "perm.show_logs"
|
label: "perm.show_logs"
|
||||||
bit: 2
|
|
||||||
delete_logs:
|
delete_logs:
|
||||||
label: "perm.delete_logs"
|
label: "perm.delete_logs"
|
||||||
alsoSet: 'show_logs'
|
alsoSet: 'show_logs'
|
||||||
bit: 4
|
server_infos:
|
||||||
|
label: "perm.server_infos"
|
||||||
|
|
||||||
devices_parts:
|
attachments:
|
||||||
label: "perm.device_parts"
|
label: "perm.part.attachments"
|
||||||
group: "parts"
|
|
||||||
operations:
|
operations:
|
||||||
read:
|
show_private:
|
||||||
label: "perm.read"
|
label: "perm.attachments.show_private"
|
||||||
bit: 0
|
list_attachments:
|
||||||
edit:
|
label: "perm.attachments.list_attachments"
|
||||||
label: "perm.edit"
|
alsoSet: ['attachment_types.read']
|
||||||
alsoSet: 'read'
|
|
||||||
bit: 2
|
|
||||||
create:
|
|
||||||
label: "perm.create"
|
|
||||||
alsoSet: ['edit', 'read']
|
|
||||||
bit: 6
|
|
||||||
delete:
|
|
||||||
label: "perm.delete"
|
|
||||||
alsoSet: ['edit', 'read']
|
|
||||||
bit: 8
|
|
||||||
|
|
||||||
self:
|
self:
|
||||||
label: "perm.self"
|
label: "perm.self"
|
||||||
operations:
|
operations:
|
||||||
edit_infos:
|
edit_infos:
|
||||||
label: "perm.self.edit_infos"
|
label: "perm.self.edit_infos"
|
||||||
bit: 0
|
|
||||||
edit_username:
|
edit_username:
|
||||||
label: "perm.self.edit_username"
|
label: "perm.self.edit_username"
|
||||||
bit: 2
|
|
||||||
show_permissions:
|
show_permissions:
|
||||||
label: "perm.self.show_permissions"
|
label: "perm.self.show_permissions"
|
||||||
bit: 4
|
|
||||||
show_logs:
|
show_logs:
|
||||||
label: "perm.self.show_logs"
|
label: "perm.self.show_logs"
|
||||||
bit: 6
|
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
label: "perm.labels"
|
label: "perm.labels"
|
||||||
operations:
|
operations:
|
||||||
create_labels:
|
create_labels:
|
||||||
label: "perm.self.create_labels"
|
label: "perm.self.create_labels"
|
||||||
bit: 0
|
|
||||||
edit_options:
|
edit_options:
|
||||||
label: "perm.self.edit_options"
|
label: "perm.self.edit_options"
|
||||||
bit: 2
|
|
||||||
alsoSet: ['create_labels']
|
alsoSet: ['create_labels']
|
||||||
read_profiles:
|
read_profiles:
|
||||||
label: "perm.self.read_profiles"
|
label: "perm.self.read_profiles"
|
||||||
bit: 10
|
|
||||||
edit_profiles:
|
edit_profiles:
|
||||||
label: "perm.self.edit_profiles"
|
label: "perm.self.edit_profiles"
|
||||||
bit: 6
|
|
||||||
alsoSet: ['read_profiles']
|
alsoSet: ['read_profiles']
|
||||||
create_profiles:
|
create_profiles:
|
||||||
label: "perm.self.create_profiles"
|
label: "perm.self.create_profiles"
|
||||||
bit: 8
|
|
||||||
alsoSet: ['read_profiles', 'edit_profiles']
|
alsoSet: ['read_profiles', 'edit_profiles']
|
||||||
delete_profiles:
|
delete_profiles:
|
||||||
label: "perm.self.delete_profiles"
|
label: "perm.self.delete_profiles"
|
||||||
bit: 4
|
|
||||||
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles']
|
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles']
|
||||||
use_twig:
|
use_twig:
|
||||||
label: "perm.labels.use_twig"
|
label: "perm.labels.use_twig"
|
||||||
bit: 12
|
|
||||||
alsoSet: ['create_labels', 'edit_options']
|
alsoSet: ['create_labels', 'edit_options']
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
bit: 14
|
|
||||||
alsoSet: ['read_profiles']
|
alsoSet: ['read_profiles']
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
bit: 16
|
|
||||||
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles', 'delete_profiles']
|
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles', 'delete_profiles']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,6 @@ services:
|
||||||
# Security
|
# Security
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
|
|
||||||
App\Security\EntityListeners\ElementPermissionListener:
|
|
||||||
tags:
|
|
||||||
- { name: "doctrine.orm.entity_listener" }
|
|
||||||
|
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
# Cache
|
# Cache
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
|
@ -177,7 +173,7 @@ services:
|
||||||
arguments:
|
arguments:
|
||||||
$allow_email_pw_reset: '%partdb.users.email_pw_reset%'
|
$allow_email_pw_reset: '%partdb.users.email_pw_reset%'
|
||||||
|
|
||||||
App\Services\TFA\BackupCodeGenerator:
|
App\Services\UserSystem\TFA\BackupCodeGenerator:
|
||||||
arguments:
|
arguments:
|
||||||
$code_length: 8
|
$code_length: 8
|
||||||
$code_count: 15
|
$code_count: 15
|
||||||
|
|
127
migrations/Version20221114193325.php
Normal file
127
migrations/Version20221114193325.php
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use App\Migration\AbstractMultiPlatformMigration;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20221114193325 extends AbstractMultiPlatformMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Update the permission system to the new system. Please note that all permissions will be reset!';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addDataMigrationAndWarning(): void
|
||||||
|
{
|
||||||
|
//Reset the permissions of the predefined groups, when their name was not changed
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
UPDATE `groups` SET permissions_data = '{"parts":{"read":true,"edit":true,"create":true,"delete":true,"change_favorite":true,"show_history":true,"revert_element":true},"tools":{"statistics":true,"label_scanner":true,"reel_calculator":true,"lastActivity":true},"attachments":{"list_attachments":true,"show_private":true},"self":{"show_permissions":true,"edit_infos":true},"labels":{"create_labels":true,"edit_options":true,"read_profiles":true,"edit_profiles":true,"create_profiles":true,"delete_profiles":true,"show_history":true,"revert_element":true},"categories":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"storelocations":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"footprints":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"manufacturers":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"attachment_types":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"currencies":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"measurement_units":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"suppliers":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"users":{"read":true,"create":true,"delete":true,"edit_username":true,"edit_infos":true,"edit_permissions":true,"set_password":true,"change_user_settings":true,"show_history":true,"revert_element":true},"groups":{"read":true,"edit":true,"create":true,"delete":true,"edit_permissions":true,"show_history":true,"revert_element":true},"system":{"show_logs":true,"server_infos":true},"devices":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true}}'
|
||||||
|
WHERE id = 1 AND name = 'admins';
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
UPDATE `groups` SET permissions_data = '{"parts":{"read":true},"tools":{"statistics":true,"label_scanner":true,"reel_calculator":true},"attachments":{"list_attachments":true},"self":{"show_permissions":true},"labels":{"create_labels":true,"edit_options":true},"storelocations":{"read":true},"footprints":{"read":true},"categories":{"read":true},"suppliers":{"read":true},"manufacturers":{"read":true},"currencies":{"read":true},"attachment_types":{"read":true},"measurement_units":{"read":true},"devices":{"read":true}}'
|
||||||
|
WHERE id = 2 AND name = 'readonly';
|
||||||
|
SQL);
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
UPDATE `groups` SET permissions_data = '{"parts":{"read":true,"edit":true,"create":true,"delete":true,"change_favorite":true,"show_history":true,"revert_element":true},"tools":{"statistics":true,"label_scanner":true,"reel_calculator":true,"lastActivity":true},"attachments":{"list_attachments":true,"show_private":true},"self":{"show_permissions":true,"edit_infos":true},"labels":{"create_labels":true,"edit_options":true,"read_profiles":true,"edit_profiles":true,"create_profiles":true,"delete_profiles":true,"show_history":true,"revert_element":true},"categories":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"storelocations":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"footprints":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"manufacturers":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"attachment_types":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"currencies":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"measurement_units":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"suppliers":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"devices":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true}}'
|
||||||
|
WHERE id = 3 AND name = 'users';
|
||||||
|
SQL);
|
||||||
|
|
||||||
|
//Disable login of all users with ID > 2 (meaning all except the anonymous and admin user)
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
UPDATE `users` SET disabled = 1
|
||||||
|
WHERE id > 2;
|
||||||
|
SQL);
|
||||||
|
|
||||||
|
//Reset the permissions of the admin user, to allow admin permissions (like the admins group)
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
UPDATE `users` SET permissions_data = '{"parts":{"read":true,"edit":true,"create":true,"delete":true,"change_favorite":true,"show_history":true,"revert_element":true},"tools":{"statistics":true,"label_scanner":true,"reel_calculator":true,"lastActivity":true},"attachments":{"list_attachments":true,"show_private":true},"self":{"show_permissions":true,"edit_infos":true},"labels":{"create_labels":true,"edit_options":true,"read_profiles":true,"edit_profiles":true,"create_profiles":true,"delete_profiles":true,"show_history":true,"revert_element":true},"categories":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"storelocations":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"footprints":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"manufacturers":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"attachment_types":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"currencies":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"measurement_units":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"suppliers":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true},"users":{"read":true,"create":true,"delete":true,"edit_username":true,"edit_infos":true,"edit_permissions":true,"set_password":true,"change_user_settings":true,"show_history":true,"revert_element":true},"groups":{"read":true,"edit":true,"create":true,"delete":true,"edit_permissions":true,"show_history":true,"revert_element":true},"system":{"show_logs":true,"server_infos":true},"devices":{"read":true,"edit":true,"create":true,"delete":true,"show_history":true,"revert_element":true}}'
|
||||||
|
WHERE id = 2;
|
||||||
|
SQL);
|
||||||
|
|
||||||
|
|
||||||
|
$this->warnIf(true, "\x1b[1;37;43m\n!!! All permissions were reset! Please change them to the desired state, immediately !!!\x1b[0;39;49m");
|
||||||
|
$this->warnIf(true, "\x1b[1;37;43m\n!!! For security reasons all users (except the admin user) were disabled. Login with admin user and reenable other users after checking their permissions !!!\x1b[0;39;49m");
|
||||||
|
$this->warnIf(true, "\x1b[1;37;43m\n!!! For more infos see: https://github.com/Part-DB/Part-DB-symfony/discussions/193 !!!\x1b[0;39;49m");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mySQLUp(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE groups ADD permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', DROP perms_system, DROP perms_groups, DROP perms_users, DROP perms_self, DROP perms_system_config, DROP perms_system_database, DROP perms_parts, DROP perms_parts_name, DROP perms_parts_description, DROP perms_parts_footprint, DROP perms_parts_manufacturer, DROP perms_parts_comment, DROP perms_parts_order, DROP perms_parts_orderdetails, DROP perms_parts_prices, DROP perms_parts_attachements, DROP perms_devices, DROP perms_devices_parts, DROP perms_storelocations, DROP perms_footprints, DROP perms_categories, DROP perms_suppliers, DROP perms_manufacturers, DROP perms_attachement_types, DROP perms_tools, DROP perms_labels, DROP perms_parts_category, DROP perms_parts_minamount, DROP perms_parts_lots, DROP perms_parts_tags, DROP perms_parts_unit, DROP perms_parts_mass, DROP perms_parts_status, DROP perms_parts_mpn, DROP perms_currencies, DROP perms_measurement_units, DROP perms_parts_parameters');
|
||||||
|
$this->addSql('ALTER TABLE users ADD permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', DROP perms_system, DROP perms_groups, DROP perms_users, DROP perms_self, DROP perms_system_config, DROP perms_system_database, DROP perms_parts, DROP perms_parts_name, DROP perms_parts_description, DROP perms_parts_footprint, DROP perms_parts_manufacturer, DROP perms_parts_comment, DROP perms_parts_order, DROP perms_parts_orderdetails, DROP perms_parts_prices, DROP perms_parts_attachements, DROP perms_devices, DROP perms_devices_parts, DROP perms_storelocations, DROP perms_footprints, DROP perms_categories, DROP perms_suppliers, DROP perms_manufacturers, DROP perms_attachement_types, DROP perms_tools, DROP perms_labels, DROP perms_parts_category, DROP perms_parts_minamount, DROP perms_parts_lots, DROP perms_parts_tags, DROP perms_parts_unit, DROP perms_parts_mass, DROP perms_parts_status, DROP perms_parts_mpn, DROP perms_currencies, DROP perms_measurement_units, DROP perms_parts_parameters');
|
||||||
|
|
||||||
|
$this->addDataMigrationAndWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mySQLDown(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE `groups` ADD perms_system INT NOT NULL, ADD perms_groups INT NOT NULL, ADD perms_users INT NOT NULL, ADD perms_self INT NOT NULL, ADD perms_system_config INT NOT NULL, ADD perms_system_database INT NOT NULL, ADD perms_parts BIGINT NOT NULL, ADD perms_parts_name SMALLINT NOT NULL, ADD perms_parts_description SMALLINT NOT NULL, ADD perms_parts_footprint SMALLINT NOT NULL, ADD perms_parts_manufacturer SMALLINT NOT NULL, ADD perms_parts_comment SMALLINT NOT NULL, ADD perms_parts_order SMALLINT NOT NULL, ADD perms_parts_orderdetails SMALLINT NOT NULL, ADD perms_parts_prices SMALLINT NOT NULL, ADD perms_parts_attachements SMALLINT NOT NULL, ADD perms_devices INT NOT NULL, ADD perms_devices_parts INT NOT NULL, ADD perms_storelocations INT NOT NULL, ADD perms_footprints INT NOT NULL, ADD perms_categories INT NOT NULL, ADD perms_suppliers INT NOT NULL, ADD perms_manufacturers INT NOT NULL, ADD perms_attachement_types INT NOT NULL, ADD perms_tools INT NOT NULL, ADD perms_labels INT NOT NULL, ADD perms_parts_category SMALLINT NOT NULL, ADD perms_parts_minamount SMALLINT NOT NULL, ADD perms_parts_lots SMALLINT NOT NULL, ADD perms_parts_tags SMALLINT NOT NULL, ADD perms_parts_unit SMALLINT NOT NULL, ADD perms_parts_mass SMALLINT NOT NULL, ADD perms_parts_status SMALLINT NOT NULL, ADD perms_parts_mpn SMALLINT NOT NULL, ADD perms_currencies INT NOT NULL, ADD perms_measurement_units INT NOT NULL, ADD perms_parts_parameters SMALLINT NOT NULL, DROP permissions_data');
|
||||||
|
$this->addSql('ALTER TABLE `users` ADD perms_system INT NOT NULL, ADD perms_groups INT NOT NULL, ADD perms_users INT NOT NULL, ADD perms_self INT NOT NULL, ADD perms_system_config INT NOT NULL, ADD perms_system_database INT NOT NULL, ADD perms_parts BIGINT NOT NULL, ADD perms_parts_name SMALLINT NOT NULL, ADD perms_parts_description SMALLINT NOT NULL, ADD perms_parts_footprint SMALLINT NOT NULL, ADD perms_parts_manufacturer SMALLINT NOT NULL, ADD perms_parts_comment SMALLINT NOT NULL, ADD perms_parts_order SMALLINT NOT NULL, ADD perms_parts_orderdetails SMALLINT NOT NULL, ADD perms_parts_prices SMALLINT NOT NULL, ADD perms_parts_attachements SMALLINT NOT NULL, ADD perms_devices INT NOT NULL, ADD perms_devices_parts INT NOT NULL, ADD perms_storelocations INT NOT NULL, ADD perms_footprints INT NOT NULL, ADD perms_categories INT NOT NULL, ADD perms_suppliers INT NOT NULL, ADD perms_manufacturers INT NOT NULL, ADD perms_attachement_types INT NOT NULL, ADD perms_tools INT NOT NULL, ADD perms_labels INT NOT NULL, ADD perms_parts_category SMALLINT NOT NULL, ADD perms_parts_minamount SMALLINT NOT NULL, ADD perms_parts_lots SMALLINT NOT NULL, ADD perms_parts_tags SMALLINT NOT NULL, ADD perms_parts_unit SMALLINT NOT NULL, ADD perms_parts_mass SMALLINT NOT NULL, ADD perms_parts_status SMALLINT NOT NULL, ADD perms_parts_mpn SMALLINT NOT NULL, ADD perms_currencies INT NOT NULL, ADD perms_measurement_units INT NOT NULL, ADD perms_parts_parameters SMALLINT NOT NULL, DROP permissions_data');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sqLiteUp(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added FROM groups');
|
||||||
|
$this->addSql('DROP TABLE groups');
|
||||||
|
$this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json)
|
||||||
|
, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D39706DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added FROM __temp__groups');
|
||||||
|
$this->addSql('DROP TABLE __temp__groups');
|
||||||
|
$this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)');
|
||||||
|
$this->addSql('CREATE INDEX group_idx_name ON groups (name)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_F06D39706DEDCEC2 ON groups (id_preview_attachement)');
|
||||||
|
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added FROM users');
|
||||||
|
$this->addSql('DROP TABLE users');
|
||||||
|
$this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json)
|
||||||
|
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json)
|
||||||
|
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json)
|
||||||
|
, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E96DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added) SELECT id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added FROM __temp__users');
|
||||||
|
$this->addSql('DROP TABLE __temp__users');
|
||||||
|
$this->addSql('CREATE INDEX user_idx_username ON users (name)');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_1483A5E96DEDCEC2 ON users (id_preview_attachement)');
|
||||||
|
|
||||||
|
|
||||||
|
$this->addDataMigrationAndWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sqLiteDown(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added FROM "groups"');
|
||||||
|
$this->addSql('DROP TABLE "groups"');
|
||||||
|
$this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, perms_system INTEGER NOT NULL, perms_groups INTEGER NOT NULL, perms_users INTEGER NOT NULL, perms_self INTEGER NOT NULL, perms_system_config INTEGER NOT NULL, perms_system_database INTEGER NOT NULL, perms_parts BIGINT NOT NULL, perms_parts_name SMALLINT NOT NULL, perms_parts_category SMALLINT NOT NULL, perms_parts_description SMALLINT NOT NULL, perms_parts_minamount SMALLINT NOT NULL, perms_parts_footprint SMALLINT NOT NULL, perms_parts_lots SMALLINT NOT NULL, perms_parts_tags SMALLINT NOT NULL, perms_parts_unit SMALLINT NOT NULL, perms_parts_mass SMALLINT NOT NULL, perms_parts_manufacturer SMALLINT NOT NULL, perms_parts_status SMALLINT NOT NULL, perms_parts_mpn SMALLINT NOT NULL, perms_parts_comment SMALLINT NOT NULL, perms_parts_order SMALLINT NOT NULL, perms_parts_orderdetails SMALLINT NOT NULL, perms_parts_prices SMALLINT NOT NULL, perms_parts_parameters SMALLINT NOT NULL, perms_parts_attachements SMALLINT NOT NULL, perms_devices INTEGER NOT NULL, perms_devices_parts INTEGER NOT NULL, perms_storelocations INTEGER NOT NULL, perms_footprints INTEGER NOT NULL, perms_categories INTEGER NOT NULL, perms_suppliers INTEGER NOT NULL, perms_manufacturers INTEGER NOT NULL, perms_attachement_types INTEGER NOT NULL, perms_currencies INTEGER NOT NULL, perms_measurement_units INTEGER NOT NULL, perms_tools INTEGER NOT NULL, perms_labels INTEGER NOT NULL, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D39706DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES "attachments" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachement, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added FROM __temp__groups');
|
||||||
|
$this->addSql('DROP TABLE __temp__groups');
|
||||||
|
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_F06D39706DEDCEC2 ON "groups" (id_preview_attachement)');
|
||||||
|
$this->addSql('CREATE INDEX group_idx_name ON "groups" (name)');
|
||||||
|
$this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)');
|
||||||
|
|
||||||
|
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added FROM "users"');
|
||||||
|
$this->addSql('DROP TABLE "users"');
|
||||||
|
$this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachement INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --
|
||||||
|
(DC2Type:json)
|
||||||
|
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --
|
||||||
|
(DC2Type:json)
|
||||||
|
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, perms_system INTEGER NOT NULL, perms_groups INTEGER NOT NULL, perms_users INTEGER NOT NULL, perms_self INTEGER NOT NULL, perms_system_config INTEGER NOT NULL, perms_system_database INTEGER NOT NULL, perms_parts BIGINT NOT NULL, perms_parts_name SMALLINT NOT NULL, perms_parts_category SMALLINT NOT NULL, perms_parts_description SMALLINT NOT NULL, perms_parts_minamount SMALLINT NOT NULL, perms_parts_footprint SMALLINT NOT NULL, perms_parts_lots SMALLINT NOT NULL, perms_parts_tags SMALLINT NOT NULL, perms_parts_unit SMALLINT NOT NULL, perms_parts_mass SMALLINT NOT NULL, perms_parts_manufacturer SMALLINT NOT NULL, perms_parts_status SMALLINT NOT NULL, perms_parts_mpn SMALLINT NOT NULL, perms_parts_comment SMALLINT NOT NULL, perms_parts_order SMALLINT NOT NULL, perms_parts_orderdetails SMALLINT NOT NULL, perms_parts_prices SMALLINT NOT NULL, perms_parts_parameters SMALLINT NOT NULL, perms_parts_attachements SMALLINT NOT NULL, perms_devices INTEGER NOT NULL, perms_devices_parts INTEGER NOT NULL, perms_storelocations INTEGER NOT NULL, perms_footprints INTEGER NOT NULL, perms_categories INTEGER NOT NULL, perms_suppliers INTEGER NOT NULL, perms_manufacturers INTEGER NOT NULL, perms_attachement_types INTEGER NOT NULL, perms_currencies INTEGER NOT NULL, perms_measurement_units INTEGER NOT NULL, perms_tools INTEGER NOT NULL, perms_labels INTEGER NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E96DEDCEC2 FOREIGN KEY (id_preview_attachement) REFERENCES "attachments" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||||
|
$this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added) SELECT id, group_id, currency_id, id_preview_attachement, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added FROM __temp__users');
|
||||||
|
$this->addSql('DROP TABLE __temp__users');
|
||||||
|
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_1483A5E96DEDCEC2 ON "users" (id_preview_attachement)');
|
||||||
|
$this->addSql('CREATE INDEX user_idx_username ON "users" (name)');
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,7 +56,7 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||||
|
|
||||||
class SetPasswordCommand extends Command
|
class SetPasswordCommand extends Command
|
||||||
{
|
{
|
||||||
protected static $defaultName = 'partdb:users:set-password|app:set-password|users:set-password';
|
protected static $defaultName = 'partdb:users:set-password|app:set-password|users:set-password|partdb:user:set-password';
|
||||||
|
|
||||||
protected EntityManagerInterface $entityManager;
|
protected EntityManagerInterface $entityManager;
|
||||||
protected UserPasswordHasherInterface $encoder;
|
protected UserPasswordHasherInterface $encoder;
|
||||||
|
@ -77,7 +77,6 @@ class SetPasswordCommand extends Command
|
||||||
->setDescription('Sets the password of a user')
|
->setDescription('Sets the password of a user')
|
||||||
->setHelp('This password allows you to set the password of a user, without knowing the old password.')
|
->setHelp('This password allows you to set the password of a user, without knowing the old password.')
|
||||||
->addArgument('user', InputArgument::REQUIRED, 'The name of the user')
|
->addArgument('user', InputArgument::REQUIRED, 'The name of the user')
|
||||||
|
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
92
src/Command/User/UserEnableCommand.php
Normal file
92
src/Command/User/UserEnableCommand.php
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Command\User;
|
||||||
|
|
||||||
|
use App\Entity\UserSystem\User;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
class UserEnableCommand extends Command
|
||||||
|
{
|
||||||
|
protected static $defaultName = 'partdb:users:enable|partdb:user:enable';
|
||||||
|
|
||||||
|
protected EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager, string $name = null)
|
||||||
|
{
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
|
||||||
|
parent::__construct($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setDescription('Enables/Disable the login of one or more users')
|
||||||
|
->setHelp('This allows you to allow or prevent the login of certain user. Use the --disable option to disable the login for the given users')
|
||||||
|
->addArgument('users', InputArgument::IS_ARRAY, 'The usernames of the users to use')
|
||||||
|
->addOption('all', 'a', InputOption::VALUE_NONE, 'Enable/Disable all users')
|
||||||
|
->addOption('disable', 'd', InputOption::VALUE_NONE, 'Disable the login of the given users')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$usernames = $input->getArgument('users');
|
||||||
|
$all_users = $input->getOption('all');
|
||||||
|
$disabling = $input->getOption('disable');
|
||||||
|
|
||||||
|
if(!$all_users && empty($usernames)) {
|
||||||
|
$io->error('No users given! You have to pass atleast one username or use the --all option to use all users!');
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repo = $this->entityManager->getRepository(User::class);
|
||||||
|
|
||||||
|
$users = [];
|
||||||
|
if($all_users) { //If we requested to change all users at once, then get all users from repo
|
||||||
|
$users = $repo->findAll();
|
||||||
|
} else { //Otherwise, fetch the users from DB
|
||||||
|
foreach ($usernames as $username) {
|
||||||
|
$user = $repo->findByEmailOrName($username);
|
||||||
|
if ($user === null) {
|
||||||
|
$io->error('No user found with username: '.$username);
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
$users[] = $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($disabling) {
|
||||||
|
$io->note('The following users will be disabled:');
|
||||||
|
} else {
|
||||||
|
$io->note('The following users will be enabled:');
|
||||||
|
}
|
||||||
|
$io->table(['Username', 'Enabled/Disabled'],
|
||||||
|
array_map(function(User $user) {
|
||||||
|
return [$user->getFullName(true), $user->isDisabled() ? 'Disabled' : 'Enabled'];
|
||||||
|
}, $users));
|
||||||
|
|
||||||
|
if(!$io->confirm('Do you want to continue?')) {
|
||||||
|
$io->warning('Aborting!');
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$user->setDisabled($disabling);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save the results
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$io->success('Successfully changed the state of the users!');
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ class UserListCommand extends Command
|
||||||
$io->title('Users:');
|
$io->title('Users:');
|
||||||
|
|
||||||
$table = new Table($output);
|
$table = new Table($output);
|
||||||
$table->setHeaders(['ID', 'Username', 'Name', 'Email', 'Group']);
|
$table->setHeaders(['ID', 'Username', 'Name', 'Email', 'Group', 'Login Disabled']);
|
||||||
|
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
$table->addRow([
|
$table->addRow([
|
||||||
|
@ -52,6 +52,7 @@ class UserListCommand extends Command
|
||||||
$user->getFullName(),
|
$user->getFullName(),
|
||||||
$user->getEmail(),
|
$user->getEmail(),
|
||||||
$user->getGroup() !== null ? $user->getGroup()->getName() . ' (ID: ' . $user->getGroup()->getID() . ')' : 'No group',
|
$user->getGroup() !== null ? $user->getGroup()->getName() . ' (ID: ' . $user->getGroup()->getID() . ')' : 'No group',
|
||||||
|
$user->isDisabled() ? 'Yes' : 'No',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
204
src/Command/User/UsersPermissionsCommand.php
Normal file
204
src/Command/User/UsersPermissionsCommand.php
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Command\User;
|
||||||
|
|
||||||
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Repository\UserRepository;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Helper\Table;
|
||||||
|
use Symfony\Component\Console\Helper\TableSeparator;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
class UsersPermissionsCommand extends Command
|
||||||
|
{
|
||||||
|
protected static $defaultName = 'partdb:users:permissions|partdb:user:permissions';
|
||||||
|
protected static $defaultDescription = 'View and edit the permissions of a given user';
|
||||||
|
|
||||||
|
protected EntityManagerInterface $entityManager;
|
||||||
|
protected UserRepository $userRepository;
|
||||||
|
protected PermissionManager $permissionResolver;
|
||||||
|
protected TranslatorInterface $translator;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager, PermissionManager $permissionResolver, TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
$this->userRepository = $entityManager->getRepository(User::class);
|
||||||
|
$this->permissionResolver = $permissionResolver;
|
||||||
|
$this->translator = $translator;
|
||||||
|
|
||||||
|
parent::__construct(self::$defaultName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addArgument('user', InputArgument::REQUIRED, 'The username of the user to view')
|
||||||
|
->addOption('noInherit', null, InputOption::VALUE_NONE, 'Do not inherit permissions from groups')
|
||||||
|
->addOption('edit', '', InputOption::VALUE_NONE, 'Edit the permissions of the user')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$username = $input->getArgument('user');
|
||||||
|
$edit_mode = $input->getOption('edit');
|
||||||
|
$inherit = !$input->getOption('noInherit') && !$edit_mode; //Show the non inherited perms in edit mode
|
||||||
|
|
||||||
|
//Find user
|
||||||
|
$io->note('Finding user with username: ' . $username);
|
||||||
|
$user = $this->userRepository->findByEmailOrName($username);
|
||||||
|
if ($user === null) {
|
||||||
|
$io->error('No user found with username: ' . $username);
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->note(sprintf('Found user %s with ID %d', $user->getFullName(true), $user->getId()));
|
||||||
|
|
||||||
|
$edit_mapping = $this->renderPermissionTable($output, $user, $inherit);
|
||||||
|
|
||||||
|
while($edit_mode) {
|
||||||
|
$index_to_edit = $io->ask('Which permission do you want to edit? Enter the index (e.g. 2-4) to edit, * for all permissions or "q" to quit', 'q');
|
||||||
|
if ($index_to_edit === 'q') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($edit_mapping[$index_to_edit]) && $index_to_edit !== '*') {
|
||||||
|
$io->error('Invalid index');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($index_to_edit === '*') {
|
||||||
|
$io->warning('You are about to edit all permissions. This will overwrite all permissions!');
|
||||||
|
} else {
|
||||||
|
[$perm_to_edit, $op_to_edit] = $edit_mapping[$index_to_edit];
|
||||||
|
$io->note('Editing permission ' . $perm_to_edit . ' with operation <options=bold>' . $op_to_edit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$new_value_str = $io->ask('Enter the new value for the permission (A = allow, D = disallow, I = inherit)');
|
||||||
|
switch (strtolower($new_value_str)) {
|
||||||
|
case 'a':
|
||||||
|
case 'allow':
|
||||||
|
$new_value = true;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
case 'disallow':
|
||||||
|
$new_value = false;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
case 'inherit':
|
||||||
|
$new_value = null;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$io->error('Invalid value');
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($index_to_edit === '*') {
|
||||||
|
$this->permissionResolver->setAllPermissions($user, $new_value);
|
||||||
|
$io->success('Permission updated successfully');
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
break; //Show the new table
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($op_to_edit, $perm_to_edit)) {
|
||||||
|
$this->permissionResolver->setPermission($user, $perm_to_edit, $op_to_edit, $new_value);
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException('Erorr while editing permission');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ensure that all operations are set accordingly
|
||||||
|
$this->permissionResolver->ensureCorrectSetOperations($user);
|
||||||
|
$io->success('Permission updated successfully');
|
||||||
|
|
||||||
|
//Save to DB
|
||||||
|
$this->entityManager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($edit_mode) {
|
||||||
|
$io->note('New permissions:');
|
||||||
|
$this->renderPermissionTable($output, $user, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderPermissionTable(OutputInterface $output, User $user, bool $inherit): array
|
||||||
|
{
|
||||||
|
//We fill this with index and perm/op combination for later use
|
||||||
|
$edit_mapping = [];
|
||||||
|
|
||||||
|
$table = new Table($output);
|
||||||
|
|
||||||
|
$perms = $this->permissionResolver->getPermissionStructure()['perms'];
|
||||||
|
|
||||||
|
if ($inherit) {
|
||||||
|
$table->setHeaderTitle('Inherited Permissions for '.$user->getFullName(true));
|
||||||
|
} else {
|
||||||
|
$table->setHeaderTitle('Non Inherited Permissions for '.$user->getFullName(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->setHeaders(['', 'Permission', 'Operation', 'Value']);
|
||||||
|
|
||||||
|
$perm_index = '1';
|
||||||
|
|
||||||
|
foreach ($perms as $perm_name => $perm_obj) {
|
||||||
|
$op_index = 1;
|
||||||
|
foreach ($perm_obj['operations'] as $operation_name => $operation_obj) {
|
||||||
|
|
||||||
|
$index = sprintf('%d-%d', $perm_index, $op_index);
|
||||||
|
|
||||||
|
$table->addRow([
|
||||||
|
$index,
|
||||||
|
$this->translator->trans($perm_obj['label']), //Permission name
|
||||||
|
$this->translator->trans($operation_obj['label']), //Operation name
|
||||||
|
$this->getPermissionValue($user, $perm_name, $operation_name, $inherit),
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Save index and perm/op combination for editing later
|
||||||
|
$edit_mapping[$index] = [
|
||||||
|
$perm_name, $operation_name,
|
||||||
|
];
|
||||||
|
|
||||||
|
$op_index++;
|
||||||
|
}
|
||||||
|
$table->addRow(new TableSeparator());
|
||||||
|
|
||||||
|
$perm_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->render();
|
||||||
|
|
||||||
|
return $edit_mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPermissionValue(User $user, string $permission, string $op, bool $inherit = true): string
|
||||||
|
{
|
||||||
|
if ($inherit) {
|
||||||
|
$permission_value = $this->permissionResolver->inherit($user, $permission, $op);
|
||||||
|
} else {
|
||||||
|
$permission_value = $this->permissionResolver->dontInherit($user, $permission, $op);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($permission_value === true) {
|
||||||
|
return '<fg=green>Allow</>';
|
||||||
|
} else if ($permission_value === false) {
|
||||||
|
return '<fg=red>Disallow</>';
|
||||||
|
} else if ($permission_value === null && !$inherit) {
|
||||||
|
return '<fg=blue>Inherit</>';
|
||||||
|
} else if ($permission_value === null && $inherit) {
|
||||||
|
return '<fg=red>Disallow (Inherited)</>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '???';
|
||||||
|
}
|
||||||
|
}
|
|
@ -143,7 +143,6 @@ abstract class BaseAdminController extends AbstractController
|
||||||
protected function revertElementIfNeeded(AbstractDBElement $entity, ?string $timestamp): ?DateTime
|
protected function revertElementIfNeeded(AbstractDBElement $entity, ?string $timestamp): ?DateTime
|
||||||
{
|
{
|
||||||
if (null !== $timestamp) {
|
if (null !== $timestamp) {
|
||||||
$this->denyAccessUnlessGranted('@tools.timetravel');
|
|
||||||
$this->denyAccessUnlessGranted('show_history', $entity);
|
$this->denyAccessUnlessGranted('show_history', $entity);
|
||||||
//If the timestamp only contains numbers interpret it as unix timestamp
|
//If the timestamp only contains numbers interpret it as unix timestamp
|
||||||
if (ctype_digit($timestamp)) {
|
if (ctype_digit($timestamp)) {
|
||||||
|
|
|
@ -131,7 +131,7 @@ class AttachmentFileController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function attachmentsTable(Request $request, DataTableFactory $dataTableFactory, NodesListBuilder $nodesListBuilder)
|
public function attachmentsTable(Request $request, DataTableFactory $dataTableFactory, NodesListBuilder $nodesListBuilder)
|
||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted('read', new PartAttachment());
|
$this->denyAccessUnlessGranted('@attachments.list_attachments');
|
||||||
|
|
||||||
$formRequest = clone $request;
|
$formRequest = clone $request;
|
||||||
$formRequest->setMethod('GET');
|
$formRequest->setMethod('GET');
|
||||||
|
|
|
@ -51,6 +51,7 @@ use App\Form\AdminPages\GroupAdminForm;
|
||||||
use App\Services\EntityExporter;
|
use App\Services\EntityExporter;
|
||||||
use App\Services\EntityImporter;
|
use App\Services\EntityImporter;
|
||||||
use App\Services\StructuralElementRecursionHelper;
|
use App\Services\StructuralElementRecursionHelper;
|
||||||
|
use App\Services\UserSystem\PermissionPresetsHelper;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
@ -73,8 +74,27 @@ class GroupController extends BaseAdminController
|
||||||
* @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="group_edit")
|
* @Route("/{id}/edit/{timestamp}", requirements={"id"="\d+"}, name="group_edit")
|
||||||
* @Route("/{id}/", requirements={"id"="\d+"})
|
* @Route("/{id}/", requirements={"id"="\d+"})
|
||||||
*/
|
*/
|
||||||
public function edit(Group $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
|
public function edit(Group $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, ?string $timestamp = null): Response
|
||||||
{
|
{
|
||||||
|
//Handle permissions presets
|
||||||
|
if ($request->request->has('permission_preset')) {
|
||||||
|
$this->denyAccessUnlessGranted('edit_permissions', $entity);
|
||||||
|
if ($this->isCsrfTokenValid('group'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
|
$preset = $request->request->get('permission_preset');
|
||||||
|
|
||||||
|
$permissionPresetsHelper->applyPreset($entity, $preset);
|
||||||
|
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'user.edit.permission_success');
|
||||||
|
|
||||||
|
//We need to stop the execution here, or our permissions changes will be overwritten by the form values
|
||||||
|
return $this->redirectToRoute('group_edit', ['id' => $entity->getID()]);
|
||||||
|
} else {
|
||||||
|
$this->addFlash('danger', 'csfr_invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $this->_edit($entity, $request, $em, $timestamp);
|
return $this->_edit($entity, $request, $em, $timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,6 @@ class PartController extends AbstractController
|
||||||
|
|
||||||
$timeTravel_timestamp = null;
|
$timeTravel_timestamp = null;
|
||||||
if (null !== $timestamp) {
|
if (null !== $timestamp) {
|
||||||
$this->denyAccessUnlessGranted('@tools.timetravel');
|
|
||||||
$this->denyAccessUnlessGranted('show_history', $part);
|
$this->denyAccessUnlessGranted('show_history', $part);
|
||||||
//If the timestamp only contains numbers interpret it as unix timestamp
|
//If the timestamp only contains numbers interpret it as unix timestamp
|
||||||
if (ctype_digit($timestamp)) {
|
if (ctype_digit($timestamp)) {
|
||||||
|
|
|
@ -175,6 +175,8 @@ class PartListsController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function showCategory(Category $category, Request $request)
|
public function showCategory(Category $category, Request $request)
|
||||||
{
|
{
|
||||||
|
$this->denyAccessUnlessGranted('@categories.read');
|
||||||
|
|
||||||
return $this->showListWithFilter($request,
|
return $this->showListWithFilter($request,
|
||||||
'Parts/lists/category_list.html.twig',
|
'Parts/lists/category_list.html.twig',
|
||||||
function (PartFilter $filter) use ($category) {
|
function (PartFilter $filter) use ($category) {
|
||||||
|
@ -195,6 +197,8 @@ class PartListsController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function showFootprint(Footprint $footprint, Request $request)
|
public function showFootprint(Footprint $footprint, Request $request)
|
||||||
{
|
{
|
||||||
|
$this->denyAccessUnlessGranted('@footprints.read');
|
||||||
|
|
||||||
return $this->showListWithFilter($request,
|
return $this->showListWithFilter($request,
|
||||||
'Parts/lists/footprint_list.html.twig',
|
'Parts/lists/footprint_list.html.twig',
|
||||||
function (PartFilter $filter) use ($footprint) {
|
function (PartFilter $filter) use ($footprint) {
|
||||||
|
@ -215,6 +219,8 @@ class PartListsController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function showManufacturer(Manufacturer $manufacturer, Request $request)
|
public function showManufacturer(Manufacturer $manufacturer, Request $request)
|
||||||
{
|
{
|
||||||
|
$this->denyAccessUnlessGranted('@manufacturers.read');
|
||||||
|
|
||||||
return $this->showListWithFilter($request,
|
return $this->showListWithFilter($request,
|
||||||
'Parts/lists/manufacturer_list.html.twig',
|
'Parts/lists/manufacturer_list.html.twig',
|
||||||
function (PartFilter $filter) use ($manufacturer) {
|
function (PartFilter $filter) use ($manufacturer) {
|
||||||
|
@ -235,6 +241,8 @@ class PartListsController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function showStorelocation(Storelocation $storelocation, Request $request)
|
public function showStorelocation(Storelocation $storelocation, Request $request)
|
||||||
{
|
{
|
||||||
|
$this->denyAccessUnlessGranted('@storelocations.read');
|
||||||
|
|
||||||
return $this->showListWithFilter($request,
|
return $this->showListWithFilter($request,
|
||||||
'Parts/lists/store_location_list.html.twig',
|
'Parts/lists/store_location_list.html.twig',
|
||||||
function (PartFilter $filter) use ($storelocation) {
|
function (PartFilter $filter) use ($storelocation) {
|
||||||
|
@ -255,6 +263,8 @@ class PartListsController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function showSupplier(Supplier $supplier, Request $request)
|
public function showSupplier(Supplier $supplier, Request $request)
|
||||||
{
|
{
|
||||||
|
$this->denyAccessUnlessGranted('@suppliers.read');
|
||||||
|
|
||||||
return $this->showListWithFilter($request,
|
return $this->showListWithFilter($request,
|
||||||
'Parts/lists/supplier_list.html.twig',
|
'Parts/lists/supplier_list.html.twig',
|
||||||
function (PartFilter $filter) use ($supplier) {
|
function (PartFilter $filter) use ($supplier) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace App\Controller;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Events\SecurityEvent;
|
use App\Events\SecurityEvent;
|
||||||
use App\Events\SecurityEvents;
|
use App\Events\SecurityEvents;
|
||||||
use App\Services\PasswordResetManager;
|
use App\Services\UserSystem\PasswordResetManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
|
@ -33,6 +33,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/select_api")
|
* @Route("/select_api")
|
||||||
|
*
|
||||||
|
* This endpoint is used by the select2 library to dynamically load data (used in the multiselect action helper in parts lists)
|
||||||
*/
|
*/
|
||||||
class SelectAPIController extends AbstractController
|
class SelectAPIController extends AbstractController
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Services\GitVersionInfo;
|
||||||
|
use App\Services\Misc\DBInfoHelper;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
@ -20,4 +23,47 @@ class ToolsController extends AbstractController
|
||||||
|
|
||||||
return $this->render('Tools/ReelCalculator/main.html.twig');
|
return $this->render('Tools/ReelCalculator/main.html.twig');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/server_infos", name="tools_server_infos")
|
||||||
|
*/
|
||||||
|
public function systemInfos(GitVersionInfo $versionInfo, DBInfoHelper $DBInfoHelper): Response
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('@system.server_infos');
|
||||||
|
|
||||||
|
return $this->render('Tools/ServerInfos/main.html.twig', [
|
||||||
|
//Part-DB section
|
||||||
|
'git_branch' => $versionInfo->getGitBranchName(),
|
||||||
|
'git_commit' => $versionInfo->getGitCommitHash(),
|
||||||
|
'default_locale' => $this->getParameter('partdb.locale'),
|
||||||
|
'default_timezone' => $this->getParameter('partdb.timezone'),
|
||||||
|
'default_currency' => $this->getParameter('partdb.default_currency'),
|
||||||
|
'default_theme' => $this->getParameter('partdb.global_theme'),
|
||||||
|
'enabled_locales' => $this->getParameter('partdb.locale_menu'),
|
||||||
|
'demo_mode' => $this->getParameter('partdb.demo_mode'),
|
||||||
|
'gpdr_compliance' => $this->getParameter('partdb.gpdr_compliance'),
|
||||||
|
'use_gravatar' => $this->getParameter('partdb.users.use_gravatar'),
|
||||||
|
'email_password_reset' => $this->getParameter('partdb.users.email_pw_reset'),
|
||||||
|
'enviroment' => $this->getParameter('kernel.environment'),
|
||||||
|
'is_debug' => $this->getParameter('kernel.debug'),
|
||||||
|
'email_sender' => $this->getParameter('partdb.mail.sender_email'),
|
||||||
|
'email_sender_name' => $this->getParameter('partdb.mail.sender_name'),
|
||||||
|
'allow_attachments_downloads' => $this->getParameter('partdb.attachments.allow_downloads'),
|
||||||
|
'detailed_error_pages' => $this->getParameter('partdb.error_pages.show_help'),
|
||||||
|
'error_page_admin_email' => $this->getParameter('partdb.error_pages.admin_email'),
|
||||||
|
|
||||||
|
//PHP section
|
||||||
|
'php_version' => PHP_VERSION,
|
||||||
|
'php_uname' => php_uname('a'),
|
||||||
|
'php_sapi' => PHP_SAPI,
|
||||||
|
'php_extensions' => array_merge(get_loaded_extensions()),
|
||||||
|
'php_opcache_enabled' => ini_get('opcache.enable'),
|
||||||
|
'php_upload_max_filesize' => ini_get('upload_max_filesize'),
|
||||||
|
'php_post_max_size' => ini_get('post_max_size'),
|
||||||
|
|
||||||
|
//DB section
|
||||||
|
'db_type' => $DBInfoHelper->getDatabaseType() ?? 'Unknown',
|
||||||
|
'db_version' => $DBInfoHelper->getDatabaseVersion() ?? 'Unknown',
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,11 @@ class TreeController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function categoryTree(?Category $category = null): JsonResponse
|
public function categoryTree(?Category $category = null): JsonResponse
|
||||||
{
|
{
|
||||||
|
if ($this->isGranted('@parts.read') && $this->isGranted('@categories.read')) {
|
||||||
$tree = $this->treeGenerator->getTreeView(Category::class, $category, 'list_parts_root');
|
$tree = $this->treeGenerator->getTreeView(Category::class, $category, 'list_parts_root');
|
||||||
|
} else {
|
||||||
|
return new JsonResponse("Access denied", 403);
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonResponse($tree);
|
return new JsonResponse($tree);
|
||||||
}
|
}
|
||||||
|
@ -95,8 +99,11 @@ class TreeController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function footprintTree(?Footprint $footprint = null): JsonResponse
|
public function footprintTree(?Footprint $footprint = null): JsonResponse
|
||||||
{
|
{
|
||||||
|
if ($this->isGranted('@parts.read') && $this->isGranted('@footprints.read')) {
|
||||||
$tree = $this->treeGenerator->getTreeView(Footprint::class, $footprint, 'list_parts_root');
|
$tree = $this->treeGenerator->getTreeView(Footprint::class, $footprint, 'list_parts_root');
|
||||||
|
} else {
|
||||||
|
return new JsonResponse("Access denied", 403);
|
||||||
|
}
|
||||||
return new JsonResponse($tree);
|
return new JsonResponse($tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +113,11 @@ class TreeController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function locationTree(?Storelocation $location = null): JsonResponse
|
public function locationTree(?Storelocation $location = null): JsonResponse
|
||||||
{
|
{
|
||||||
|
if ($this->isGranted('@parts.read') && $this->isGranted('@storelocations.read')) {
|
||||||
$tree = $this->treeGenerator->getTreeView(Storelocation::class, $location, 'list_parts_root');
|
$tree = $this->treeGenerator->getTreeView(Storelocation::class, $location, 'list_parts_root');
|
||||||
|
} else {
|
||||||
|
return new JsonResponse("Access denied", 403);
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonResponse($tree);
|
return new JsonResponse($tree);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +128,11 @@ class TreeController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function manufacturerTree(?Manufacturer $manufacturer = null): JsonResponse
|
public function manufacturerTree(?Manufacturer $manufacturer = null): JsonResponse
|
||||||
{
|
{
|
||||||
|
if ($this->isGranted('@parts.read') && $this->isGranted('@manufacturers.read')) {
|
||||||
$tree = $this->treeGenerator->getTreeView(Manufacturer::class, $manufacturer, 'list_parts_root');
|
$tree = $this->treeGenerator->getTreeView(Manufacturer::class, $manufacturer, 'list_parts_root');
|
||||||
|
} else {
|
||||||
|
return new JsonResponse("Access denied", 403);
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonResponse($tree);
|
return new JsonResponse($tree);
|
||||||
}
|
}
|
||||||
|
@ -128,7 +143,11 @@ class TreeController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function supplierTree(?Supplier $supplier = null): JsonResponse
|
public function supplierTree(?Supplier $supplier = null): JsonResponse
|
||||||
{
|
{
|
||||||
|
if ($this->isGranted('@parts.read') && $this->isGranted('@suppliers.read')) {
|
||||||
$tree = $this->treeGenerator->getTreeView(Supplier::class, $supplier, 'list_parts_root');
|
$tree = $this->treeGenerator->getTreeView(Supplier::class, $supplier, 'list_parts_root');
|
||||||
|
} else {
|
||||||
|
return new JsonResponse("Access denied", 403);
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonResponse($tree);
|
return new JsonResponse($tree);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +158,11 @@ class TreeController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function deviceTree(?Device $device = null): JsonResponse
|
public function deviceTree(?Device $device = null): JsonResponse
|
||||||
{
|
{
|
||||||
|
if ($this->isGranted('@devices.read')) {
|
||||||
$tree = $this->treeGenerator->getTreeView(Device::class, $device, 'devices');
|
$tree = $this->treeGenerator->getTreeView(Device::class, $device, 'devices');
|
||||||
|
} else {
|
||||||
|
return new JsonResponse("Access denied", 403);
|
||||||
|
}
|
||||||
|
|
||||||
return new JsonResponse($tree);
|
return new JsonResponse($tree);
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,11 @@ class TypeaheadController extends AbstractController
|
||||||
public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse
|
public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse
|
||||||
{
|
{
|
||||||
$class = $this->typeToParameterClass($type);
|
$class = $this->typeToParameterClass($type);
|
||||||
|
|
||||||
|
$test_obj = new $class();
|
||||||
|
//Ensure user has the correct permissions
|
||||||
|
$this->denyAccessUnlessGranted('read', $test_obj);
|
||||||
|
|
||||||
/** @var ParameterRepository $repository */
|
/** @var ParameterRepository $repository */
|
||||||
$repository = $entityManager->getRepository($class);
|
$repository = $entityManager->getRepository($class);
|
||||||
|
|
||||||
|
@ -169,6 +174,8 @@ class TypeaheadController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function tags(string $query, TagFinder $finder): JsonResponse
|
public function tags(string $query, TagFinder $finder): JsonResponse
|
||||||
{
|
{
|
||||||
|
$this->denyAccessUnlessGranted('@parts.read');
|
||||||
|
|
||||||
$array = $finder->searchTags($query);
|
$array = $finder->searchTags($query);
|
||||||
|
|
||||||
$normalizers = [
|
$normalizers = [
|
||||||
|
|
|
@ -54,6 +54,7 @@ use App\Form\UserAdminForm;
|
||||||
use App\Services\EntityExporter;
|
use App\Services\EntityExporter;
|
||||||
use App\Services\EntityImporter;
|
use App\Services\EntityImporter;
|
||||||
use App\Services\StructuralElementRecursionHelper;
|
use App\Services\StructuralElementRecursionHelper;
|
||||||
|
use App\Services\UserSystem\PermissionPresetsHelper;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
@ -101,7 +102,7 @@ class UserController extends AdminPages\BaseAdminController
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function edit(User $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
|
public function edit(User $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, ?string $timestamp = null): Response
|
||||||
{
|
{
|
||||||
//Handle 2FA disabling
|
//Handle 2FA disabling
|
||||||
|
|
||||||
|
@ -132,6 +133,25 @@ class UserController extends AdminPages\BaseAdminController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Handle permissions presets
|
||||||
|
if ($request->request->has('permission_preset')) {
|
||||||
|
$this->denyAccessUnlessGranted('edit_permissions', $entity);
|
||||||
|
if ($this->isCsrfTokenValid('reset_2fa'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
|
$preset = $request->request->get('permission_preset');
|
||||||
|
|
||||||
|
$permissionPresetsHelper->applyPreset($entity, $preset);
|
||||||
|
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'user.edit.permission_success');
|
||||||
|
|
||||||
|
//We need to stop the execution here, or our permissions changes will be overwritten by the form values
|
||||||
|
return $this->redirectToRoute('user_edit', ['id' => $entity->getID()]);
|
||||||
|
} else {
|
||||||
|
$this->addFlash('danger', 'csfr_invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $this->_edit($entity, $request, $em, $timestamp);
|
return $this->_edit($entity, $request, $em, $timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ use App\Events\SecurityEvent;
|
||||||
use App\Events\SecurityEvents;
|
use App\Events\SecurityEvents;
|
||||||
use App\Form\TFAGoogleSettingsType;
|
use App\Form\TFAGoogleSettingsType;
|
||||||
use App\Form\UserSettingsType;
|
use App\Form\UserSettingsType;
|
||||||
use App\Services\TFA\BackupCodeManager;
|
use App\Services\UserSystem\TFA\BackupCodeManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticator;
|
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticator;
|
||||||
|
|
|
@ -18,6 +18,8 @@ class WebauthnKeyRegistrationController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function register(Request $request, TFAWebauthnRegistrationHelper $registrationHelper, EntityManagerInterface $em)
|
public function register(Request $request, TFAWebauthnRegistrationHelper $registrationHelper, EntityManagerInterface $em)
|
||||||
{
|
{
|
||||||
|
//When user change its settings, he should be logged in fully.
|
||||||
|
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
|
||||||
|
|
||||||
//If form was submitted, check the auth response
|
//If form was submitted, check the auth response
|
||||||
if ($request->getMethod() === 'POST') {
|
if ($request->getMethod() === 'POST') {
|
||||||
|
@ -51,7 +53,7 @@ class WebauthnKeyRegistrationController extends AbstractController
|
||||||
|
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
'Security/Webauthn/webauthn_register.html.twig',
|
'security/Webauthn/webauthn_register.html.twig',
|
||||||
[
|
[
|
||||||
'registrationRequest' => $registrationHelper->generateRegistrationRequestAsJSON(),
|
'registrationRequest' => $registrationHelper->generateRegistrationRequestAsJSON(),
|
||||||
]
|
]
|
||||||
|
|
|
@ -43,6 +43,8 @@ declare(strict_types=1);
|
||||||
namespace App\DataFixtures;
|
namespace App\DataFixtures;
|
||||||
|
|
||||||
use App\Entity\UserSystem\Group;
|
use App\Entity\UserSystem\Group;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use App\Services\UserSystem\PermissionPresetsHelper;
|
||||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||||
use Doctrine\Persistence\ObjectManager;
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
|
||||||
|
@ -52,138 +54,45 @@ class GroupFixtures extends Fixture
|
||||||
public const USERS = 'group-users';
|
public const USERS = 'group-users';
|
||||||
public const READONLY = 'group-readonly';
|
public const READONLY = 'group-readonly';
|
||||||
|
|
||||||
|
|
||||||
|
private PermissionPresetsHelper $permission_presets;
|
||||||
|
private PermissionManager $permissionManager;
|
||||||
|
|
||||||
|
public function __construct(PermissionPresetsHelper $permissionPresetsHelper, PermissionManager $permissionManager)
|
||||||
|
{
|
||||||
|
$this->permission_presets = $permissionPresetsHelper;
|
||||||
|
$this->permissionManager = $permissionManager;
|
||||||
|
}
|
||||||
|
|
||||||
public function load(ObjectManager $manager): void
|
public function load(ObjectManager $manager): void
|
||||||
{
|
{
|
||||||
$admins = new Group();
|
$admins = new Group();
|
||||||
$admins->setName('admins');
|
$admins->setName('admins');
|
||||||
//Perm values taken from Version 1
|
//Set permissions using preset
|
||||||
$admins->getPermissions()->setRawPermissionValues([
|
$this->permission_presets->applyPreset($admins, PermissionPresetsHelper::PRESET_ALL_ALLOW);
|
||||||
'system' => 21,
|
$this->addDevicesPermissions($admins);
|
||||||
'groups' => 1365,
|
|
||||||
'users' => 87381,
|
|
||||||
'self' => 85,
|
|
||||||
'config' => 85,
|
|
||||||
'database' => 21,
|
|
||||||
'parts' => 1431655765,
|
|
||||||
'parts_name' => 5,
|
|
||||||
'parts_description' => 5,
|
|
||||||
'parts_footprint' => 5,
|
|
||||||
'parts_manufacturer' => 5,
|
|
||||||
'parts_comment' => 5,
|
|
||||||
'parts_order' => 5,
|
|
||||||
'parts_orderdetails' => 341,
|
|
||||||
'parts_prices' => 341,
|
|
||||||
'parts_attachments' => 341,
|
|
||||||
'devices' => 5461,
|
|
||||||
'devices_parts' => 325,
|
|
||||||
'storelocations' => 5461,
|
|
||||||
'footprints' => 5461,
|
|
||||||
'categories' => 5461,
|
|
||||||
'suppliers' => 5461,
|
|
||||||
'manufacturers' => 5461,
|
|
||||||
'attachment_types' => 1365,
|
|
||||||
'tools' => 349525,
|
|
||||||
'labels' => 87381,
|
|
||||||
'parts_category' => 5,
|
|
||||||
'parts_minamount' => 5,
|
|
||||||
'parts_lots' => 85,
|
|
||||||
'parts_tags' => 5,
|
|
||||||
'parts_unit' => 5,
|
|
||||||
'parts_mass' => 5,
|
|
||||||
'parts_status' => 5,
|
|
||||||
'parts_mpn' => 5,
|
|
||||||
'currencies' => 5461,
|
|
||||||
'measurement_units' => 5461,
|
|
||||||
]);
|
|
||||||
$this->setReference(self::ADMINS, $admins);
|
$this->setReference(self::ADMINS, $admins);
|
||||||
$manager->persist($admins);
|
$manager->persist($admins);
|
||||||
|
|
||||||
$readonly = new Group();
|
$readonly = new Group();
|
||||||
$readonly->setName('readonly');
|
$readonly->setName('readonly');
|
||||||
$readonly->getPermissions()->setRawPermissionValues([
|
$this->permission_presets->applyPreset($readonly, PermissionPresetsHelper::PRESET_READ_ONLY);
|
||||||
'system' => 2,
|
|
||||||
'groups' => 2730,
|
|
||||||
'users' => 43690,
|
|
||||||
'self' => 25,
|
|
||||||
'config' => 170,
|
|
||||||
'database' => 42,
|
|
||||||
'parts' => 2778027689,
|
|
||||||
'parts_name' => 9,
|
|
||||||
'parts_description' => 9,
|
|
||||||
'parts_footprint' => 9,
|
|
||||||
'parts_manufacturer' => 9,
|
|
||||||
'parts_comment' => 9,
|
|
||||||
'parts_order' => 9,
|
|
||||||
'parts_orderdetails' => 681,
|
|
||||||
'parts_prices' => 681,
|
|
||||||
'parts_attachments' => 681,
|
|
||||||
'devices' => 1705,
|
|
||||||
'devices_parts' => 649,
|
|
||||||
'storelocations' => 1705,
|
|
||||||
'footprints' => 1705,
|
|
||||||
'categories' => 1705,
|
|
||||||
'suppliers' => 1705,
|
|
||||||
'manufacturers' => 1705,
|
|
||||||
'attachment_types' => 681,
|
|
||||||
'tools' => 87382,
|
|
||||||
'labels' => 173737,
|
|
||||||
'parts_category' => 9,
|
|
||||||
'parts_minamount' => 9,
|
|
||||||
'parts_lots' => 169,
|
|
||||||
'parts_tags' => 9,
|
|
||||||
'parts_unit' => 9,
|
|
||||||
'parts_mass' => 9,
|
|
||||||
'parts_status' => 9,
|
|
||||||
'parts_mpn' => 9,
|
|
||||||
'currencies' => 9897,
|
|
||||||
'measurement_units' => 9897,
|
|
||||||
]);
|
|
||||||
$this->setReference(self::READONLY, $readonly);
|
$this->setReference(self::READONLY, $readonly);
|
||||||
$manager->persist($readonly);
|
$manager->persist($readonly);
|
||||||
|
|
||||||
$users = new Group();
|
$users = new Group();
|
||||||
$users->setName('users');
|
$users->setName('users');
|
||||||
$users->getPermissions()->setRawPermissionValues([
|
$this->permission_presets->applyPreset($users, PermissionPresetsHelper::PRESET_EDITOR);
|
||||||
'system' => 42,
|
$this->addDevicesPermissions($users);
|
||||||
'groups' => 2730,
|
|
||||||
'users' => 43690,
|
|
||||||
'self' => 89,
|
|
||||||
'config' => 105,
|
|
||||||
'database' => 41,
|
|
||||||
'parts' => 1431655765,
|
|
||||||
'parts_name' => 5,
|
|
||||||
'parts_description' => 5,
|
|
||||||
'parts_footprint' => 5,
|
|
||||||
'parts_manufacturer' => 5,
|
|
||||||
'parts_comment' => 5,
|
|
||||||
'parts_order' => 5,
|
|
||||||
'parts_orderdetails' => 341,
|
|
||||||
'parts_prices' => 341,
|
|
||||||
'parts_attachments' => 341,
|
|
||||||
'devices' => 5461,
|
|
||||||
'devices_parts' => 325,
|
|
||||||
'storelocations' => 5461,
|
|
||||||
'footprints' => 5461,
|
|
||||||
'categories' => 5461,
|
|
||||||
'suppliers' => 5461,
|
|
||||||
'manufacturers' => 5461,
|
|
||||||
'attachment_types' => 1365,
|
|
||||||
'tools' => 87381,
|
|
||||||
'labels' => 91477,
|
|
||||||
'parts_category' => 5,
|
|
||||||
'parts_minamount' => 5,
|
|
||||||
'parts_lots' => 85,
|
|
||||||
'parts_tags' => 5,
|
|
||||||
'parts_unit' => 5,
|
|
||||||
'parts_mass' => 5,
|
|
||||||
'parts_status' => 5,
|
|
||||||
'parts_mpn' => 5,
|
|
||||||
'currencies' => 5461,
|
|
||||||
'measurement_units' => 5461,
|
|
||||||
]);
|
|
||||||
$this->setReference(self::USERS, $users);
|
$this->setReference(self::USERS, $users);
|
||||||
$manager->persist($users);
|
$manager->persist($users);
|
||||||
|
|
||||||
$manager->flush();
|
$manager->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addDevicesPermissions(Group $group): void
|
||||||
|
{
|
||||||
|
$this->permissionManager->setAllOperationsOfPermission($group, 'devices', true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,9 +260,7 @@ class LogDataTable implements DataTableTypeInterface
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
'disabled' => function ($value, AbstractLogEntry $context) {
|
'disabled' => function ($value, AbstractLogEntry $context) {
|
||||||
return
|
return !$this->security->isGranted('show_history', $context->getTargetClass());
|
||||||
!$this->security->isGranted('@tools.timetravel')
|
|
||||||
|| !$this->security->isGranted('show_history', $context->getTargetClass());
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -163,20 +163,29 @@ final class PartsDataTable implements DataTableTypeInterface
|
||||||
])
|
])
|
||||||
->add('description', MarkdownColumn::class, [
|
->add('description', MarkdownColumn::class, [
|
||||||
'label' => $this->translator->trans('part.table.description'),
|
'label' => $this->translator->trans('part.table.description'),
|
||||||
])
|
]);
|
||||||
->add('category', EntityColumn::class, [
|
|
||||||
|
if ($this->security->isGranted('@categories.read')) {
|
||||||
|
$dataTable->add('category', EntityColumn::class, [
|
||||||
'label' => $this->translator->trans('part.table.category'),
|
'label' => $this->translator->trans('part.table.category'),
|
||||||
'property' => 'category',
|
'property' => 'category',
|
||||||
])
|
]);
|
||||||
->add('footprint', EntityColumn::class, [
|
}
|
||||||
|
|
||||||
|
if ($this->security->isGranted('@footprints.read')) {
|
||||||
|
$dataTable->add('footprint', EntityColumn::class, [
|
||||||
'property' => 'footprint',
|
'property' => 'footprint',
|
||||||
'label' => $this->translator->trans('part.table.footprint'),
|
'label' => $this->translator->trans('part.table.footprint'),
|
||||||
])
|
]);
|
||||||
->add('manufacturer', EntityColumn::class, [
|
}
|
||||||
|
if ($this->security->isGranted('@manufacturers.read')) {
|
||||||
|
$dataTable->add('manufacturer', EntityColumn::class, [
|
||||||
'property' => 'manufacturer',
|
'property' => 'manufacturer',
|
||||||
'label' => $this->translator->trans('part.table.manufacturer'),
|
'label' => $this->translator->trans('part.table.manufacturer'),
|
||||||
])
|
]);
|
||||||
->add('storelocation', TextColumn::class, [
|
}
|
||||||
|
if ($this->security->isGranted('@storelocations.read')) {
|
||||||
|
$dataTable->add('storelocation', TextColumn::class, [
|
||||||
'label' => $this->translator->trans('part.table.storeLocations'),
|
'label' => $this->translator->trans('part.table.storeLocations'),
|
||||||
'render' => function ($value, Part $context) {
|
'render' => function ($value, Part $context) {
|
||||||
$tmp = [];
|
$tmp = [];
|
||||||
|
@ -194,8 +203,10 @@ final class PartsDataTable implements DataTableTypeInterface
|
||||||
|
|
||||||
return implode('<br>', $tmp);
|
return implode('<br>', $tmp);
|
||||||
},
|
},
|
||||||
])
|
]);
|
||||||
->add('amount', TextColumn::class, [
|
}
|
||||||
|
|
||||||
|
$dataTable->add('amount', TextColumn::class, [
|
||||||
'label' => $this->translator->trans('part.table.amount'),
|
'label' => $this->translator->trans('part.table.amount'),
|
||||||
'render' => function ($value, Part $context) {
|
'render' => function ($value, Part $context) {
|
||||||
$amount = $context->getAmountSum();
|
$amount = $context->getAmountSum();
|
||||||
|
@ -210,13 +221,17 @@ final class PartsDataTable implements DataTableTypeInterface
|
||||||
'render' => function ($value, Part $context) {
|
'render' => function ($value, Part $context) {
|
||||||
return $this->amountFormatter->format($value, $context->getPartUnit());
|
return $this->amountFormatter->format($value, $context->getPartUnit());
|
||||||
},
|
},
|
||||||
])
|
]);
|
||||||
->add('partUnit', TextColumn::class, [
|
|
||||||
|
if ($this->security->isGranted('@footprints.read')) {
|
||||||
|
$dataTable->add('partUnit', TextColumn::class, [
|
||||||
'field' => 'partUnit.name',
|
'field' => 'partUnit.name',
|
||||||
'label' => $this->translator->trans('part.table.partUnit'),
|
'label' => $this->translator->trans('part.table.partUnit'),
|
||||||
'visible' => false,
|
'visible' => false,
|
||||||
])
|
]);
|
||||||
->add('addedDate', LocaleDateTimeColumn::class, [
|
}
|
||||||
|
|
||||||
|
$dataTable->add('addedDate', LocaleDateTimeColumn::class, [
|
||||||
'label' => $this->translator->trans('part.table.addedDate'),
|
'label' => $this->translator->trans('part.table.addedDate'),
|
||||||
'visible' => false,
|
'visible' => false,
|
||||||
])
|
])
|
||||||
|
|
|
@ -37,8 +37,6 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
*
|
*
|
||||||
* @ORM\MappedSuperclass(repositoryClass="App\Repository\DBElementRepository")
|
* @ORM\MappedSuperclass(repositoryClass="App\Repository\DBElementRepository")
|
||||||
*
|
*
|
||||||
* @ORM\EntityListeners({"App\Security\EntityListeners\ElementPermissionListener"})
|
|
||||||
*
|
|
||||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||||
* "attachment_type" = "App\Entity\AttachmentType",
|
* "attachment_type" = "App\Entity\AttachmentType",
|
||||||
* "attachment" = "App\Entity\Attachment",
|
* "attachment" = "App\Entity\Attachment",
|
||||||
|
|
|
@ -45,7 +45,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
*
|
*
|
||||||
* @ORM\MappedSuperclass(repositoryClass="App\Repository\StructuralDBElementRepository")
|
* @ORM\MappedSuperclass(repositoryClass="App\Repository\StructuralDBElementRepository")
|
||||||
*
|
*
|
||||||
* @ORM\EntityListeners({"App\Security\EntityListeners\ElementPermissionListener", "App\EntityListeners\TreeCacheInvalidationListener"})
|
* @ORM\EntityListeners({"App\EntityListeners\TreeCacheInvalidationListener"})
|
||||||
*
|
*
|
||||||
* @UniqueEntity(fields={"name", "parent"}, ignoreNull=false, message="structural.entity.unique_name")
|
* @UniqueEntity(fields={"name", "parent"}, ignoreNull=false, message="structural.entity.unique_name")
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -61,7 +61,6 @@ use App\Entity\Parts\PartTraits\BasicPropertyTrait;
|
||||||
use App\Entity\Parts\PartTraits\InstockTrait;
|
use App\Entity\Parts\PartTraits\InstockTrait;
|
||||||
use App\Entity\Parts\PartTraits\ManufacturerTrait;
|
use App\Entity\Parts\PartTraits\ManufacturerTrait;
|
||||||
use App\Entity\Parts\PartTraits\OrderTrait;
|
use App\Entity\Parts\PartTraits\OrderTrait;
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
@ -103,7 +102,6 @@ class Part extends AttachmentContainingDBElement
|
||||||
protected $parameters;
|
protected $parameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ColumnSecurity(type="datetime")
|
|
||||||
* @ORM\Column(type="datetime", name="datetime_added", options={"default"="CURRENT_TIMESTAMP"})
|
* @ORM\Column(type="datetime", name="datetime_added", options={"default"="CURRENT_TIMESTAMP"})
|
||||||
*/
|
*/
|
||||||
protected ?DateTime $addedDate = null;
|
protected ?DateTime $addedDate = null;
|
||||||
|
@ -116,14 +114,12 @@ class Part extends AttachmentContainingDBElement
|
||||||
/**
|
/**
|
||||||
* @var string The name of this part
|
* @var string The name of this part
|
||||||
* @ORM\Column(type="string")
|
* @ORM\Column(type="string")
|
||||||
* @ColumnSecurity(prefix="name")
|
|
||||||
*/
|
*/
|
||||||
protected string $name = '';
|
protected string $name = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<int, PartAttachment>
|
* @var Collection<int, PartAttachment>
|
||||||
* @ORM\OneToMany(targetEntity="App\Entity\Attachments\PartAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true)
|
* @ORM\OneToMany(targetEntity="App\Entity\Attachments\PartAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||||
* @ColumnSecurity(type="collection", prefix="attachments")
|
|
||||||
* @ORM\OrderBy({"name" = "ASC"})
|
* @ORM\OrderBy({"name" = "ASC"})
|
||||||
* @Assert\Valid()
|
* @Assert\Valid()
|
||||||
*/
|
*/
|
||||||
|
@ -131,7 +127,6 @@ class Part extends AttachmentContainingDBElement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DateTime the date when this element was modified the last time
|
* @var DateTime the date when this element was modified the last time
|
||||||
* @ColumnSecurity(type="datetime")
|
|
||||||
* @ORM\Column(type="datetime", name="last_modified", options={"default"="CURRENT_TIMESTAMP"})
|
* @ORM\Column(type="datetime", name="last_modified", options={"default"="CURRENT_TIMESTAMP"})
|
||||||
*/
|
*/
|
||||||
protected ?DateTime $lastModified = null;
|
protected ?DateTime $lastModified = null;
|
||||||
|
|
|
@ -43,7 +43,6 @@ declare(strict_types=1);
|
||||||
namespace App\Entity\Parts\PartTraits;
|
namespace App\Entity\Parts\PartTraits;
|
||||||
|
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
|
@ -55,21 +54,18 @@ trait AdvancedPropertyTrait
|
||||||
/**
|
/**
|
||||||
* @var bool Determines if this part entry needs review (for example, because it is work in progress)
|
* @var bool Determines if this part entry needs review (for example, because it is work in progress)
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean")
|
||||||
* @ColumnSecurity(type="boolean")
|
|
||||||
*/
|
*/
|
||||||
protected bool $needs_review = false;
|
protected bool $needs_review = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string a comma separated list of tags, associated with the part
|
* @var string a comma separated list of tags, associated with the part
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
* @ColumnSecurity(type="string", prefix="tags", placeholder="")
|
|
||||||
*/
|
*/
|
||||||
protected string $tags = '';
|
protected string $tags = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var float|null how much a single part unit weighs in grams
|
* @var float|null how much a single part unit weighs in grams
|
||||||
* @ORM\Column(type="float", nullable=true)
|
* @ORM\Column(type="float", nullable=true)
|
||||||
* @ColumnSecurity(type="float", placeholder=null)
|
|
||||||
* @Assert\PositiveOrZero()
|
* @Assert\PositiveOrZero()
|
||||||
*/
|
*/
|
||||||
protected ?float $mass = null;
|
protected ?float $mass = null;
|
||||||
|
|
|
@ -44,7 +44,6 @@ namespace App\Entity\Parts\PartTraits;
|
||||||
|
|
||||||
use App\Entity\Parts\Category;
|
use App\Entity\Parts\Category;
|
||||||
use App\Entity\Parts\Footprint;
|
use App\Entity\Parts\Footprint;
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use App\Validator\Constraints\Selectable;
|
use App\Validator\Constraints\Selectable;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
@ -54,14 +53,12 @@ trait BasicPropertyTrait
|
||||||
/**
|
/**
|
||||||
* @var string A text describing what this part does
|
* @var string A text describing what this part does
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
* @ColumnSecurity(prefix="description")
|
|
||||||
*/
|
*/
|
||||||
protected string $description = '';
|
protected string $description = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string A comment/note related to this part
|
* @var string A comment/note related to this part
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
* @ColumnSecurity(prefix="comment")
|
|
||||||
*/
|
*/
|
||||||
protected string $comment = '';
|
protected string $comment = '';
|
||||||
|
|
||||||
|
@ -74,7 +71,6 @@ trait BasicPropertyTrait
|
||||||
/**
|
/**
|
||||||
* @var bool true, if the part is marked as favorite
|
* @var bool true, if the part is marked as favorite
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean")
|
||||||
* @ColumnSecurity(type="boolean")
|
|
||||||
*/
|
*/
|
||||||
protected bool $favorite = false;
|
protected bool $favorite = false;
|
||||||
|
|
||||||
|
@ -83,7 +79,6 @@ trait BasicPropertyTrait
|
||||||
* Every part must have a category.
|
* Every part must have a category.
|
||||||
* @ORM\ManyToOne(targetEntity="Category")
|
* @ORM\ManyToOne(targetEntity="Category")
|
||||||
* @ORM\JoinColumn(name="id_category", referencedColumnName="id", nullable=false)
|
* @ORM\JoinColumn(name="id_category", referencedColumnName="id", nullable=false)
|
||||||
* @ColumnSecurity(prefix="category", type="App\Entity\Parts\Category")
|
|
||||||
* @Selectable()
|
* @Selectable()
|
||||||
* @Assert\NotNull(message="validator.select_valid_category")
|
* @Assert\NotNull(message="validator.select_valid_category")
|
||||||
*/
|
*/
|
||||||
|
@ -93,7 +88,6 @@ trait BasicPropertyTrait
|
||||||
* @var Footprint|null The footprint of this part (e.g. DIP8)
|
* @var Footprint|null The footprint of this part (e.g. DIP8)
|
||||||
* @ORM\ManyToOne(targetEntity="Footprint")
|
* @ORM\ManyToOne(targetEntity="Footprint")
|
||||||
* @ORM\JoinColumn(name="id_footprint", referencedColumnName="id")
|
* @ORM\JoinColumn(name="id_footprint", referencedColumnName="id")
|
||||||
* @ColumnSecurity(prefix="footprint", type="App\Entity\Parts\Footprint")
|
|
||||||
* @Selectable()
|
* @Selectable()
|
||||||
*/
|
*/
|
||||||
protected ?Footprint $footprint = null;
|
protected ?Footprint $footprint = null;
|
||||||
|
|
|
@ -44,7 +44,6 @@ namespace App\Entity\Parts\PartTraits;
|
||||||
|
|
||||||
use App\Entity\Parts\MeasurementUnit;
|
use App\Entity\Parts\MeasurementUnit;
|
||||||
use App\Entity\Parts\PartLot;
|
use App\Entity\Parts\PartLot;
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
@ -58,7 +57,6 @@ trait InstockTrait
|
||||||
* @var Collection|PartLot[] A list of part lots where this part is stored
|
* @var Collection|PartLot[] A list of part lots where this part is stored
|
||||||
* @ORM\OneToMany(targetEntity="PartLot", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
|
* @ORM\OneToMany(targetEntity="PartLot", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||||
* @Assert\Valid()
|
* @Assert\Valid()
|
||||||
* @ColumnSecurity(type="collection", prefix="lots")
|
|
||||||
* @ORM\OrderBy({"amount" = "DESC"})
|
* @ORM\OrderBy({"amount" = "DESC"})
|
||||||
*/
|
*/
|
||||||
protected $partLots;
|
protected $partLots;
|
||||||
|
@ -68,7 +66,6 @@ trait InstockTrait
|
||||||
* Given in the partUnit.
|
* Given in the partUnit.
|
||||||
* @ORM\Column(type="float")
|
* @ORM\Column(type="float")
|
||||||
* @Assert\PositiveOrZero()
|
* @Assert\PositiveOrZero()
|
||||||
* @ColumnSecurity(prefix="minamount", type="integer")
|
|
||||||
*/
|
*/
|
||||||
protected float $minamount = 0;
|
protected float $minamount = 0;
|
||||||
|
|
||||||
|
@ -76,7 +73,6 @@ trait InstockTrait
|
||||||
* @var ?MeasurementUnit the unit in which the part's amount is measured
|
* @var ?MeasurementUnit the unit in which the part's amount is measured
|
||||||
* @ORM\ManyToOne(targetEntity="MeasurementUnit")
|
* @ORM\ManyToOne(targetEntity="MeasurementUnit")
|
||||||
* @ORM\JoinColumn(name="id_part_unit", referencedColumnName="id", nullable=true)
|
* @ORM\JoinColumn(name="id_part_unit", referencedColumnName="id", nullable=true)
|
||||||
* @ColumnSecurity(type="object", prefix="unit")
|
|
||||||
*/
|
*/
|
||||||
protected ?MeasurementUnit $partUnit = null;
|
protected ?MeasurementUnit $partUnit = null;
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ namespace App\Entity\Parts\PartTraits;
|
||||||
|
|
||||||
use App\Entity\Parts\Manufacturer;
|
use App\Entity\Parts\Manufacturer;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use App\Validator\Constraints\Selectable;
|
use App\Validator\Constraints\Selectable;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
@ -58,7 +57,6 @@ trait ManufacturerTrait
|
||||||
* @var Manufacturer|null The manufacturer of this part
|
* @var Manufacturer|null The manufacturer of this part
|
||||||
* @ORM\ManyToOne(targetEntity="Manufacturer")
|
* @ORM\ManyToOne(targetEntity="Manufacturer")
|
||||||
* @ORM\JoinColumn(name="id_manufacturer", referencedColumnName="id")
|
* @ORM\JoinColumn(name="id_manufacturer", referencedColumnName="id")
|
||||||
* @ColumnSecurity(prefix="manufacturer", type="App\Entity\Parts\Manufacturer")
|
|
||||||
* @Selectable()
|
* @Selectable()
|
||||||
*/
|
*/
|
||||||
protected ?Manufacturer $manufacturer = null;
|
protected ?Manufacturer $manufacturer = null;
|
||||||
|
@ -67,14 +65,12 @@ trait ManufacturerTrait
|
||||||
* @var string the url to the part on the manufacturer's homepage
|
* @var string the url to the part on the manufacturer's homepage
|
||||||
* @ORM\Column(type="string")
|
* @ORM\Column(type="string")
|
||||||
* @Assert\Url()
|
* @Assert\Url()
|
||||||
* @ColumnSecurity(prefix="mpn", type="string", placeholder="")
|
|
||||||
*/
|
*/
|
||||||
protected string $manufacturer_product_url = '';
|
protected string $manufacturer_product_url = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string The product number used by the manufacturer. If this is set to "", the name field is used.
|
* @var string The product number used by the manufacturer. If this is set to "", the name field is used.
|
||||||
* @ORM\Column(type="string")
|
* @ORM\Column(type="string")
|
||||||
* @ColumnSecurity(prefix="mpn", type="string", placeholder="")
|
|
||||||
*/
|
*/
|
||||||
protected string $manufacturer_product_number = '';
|
protected string $manufacturer_product_number = '';
|
||||||
|
|
||||||
|
@ -82,7 +78,6 @@ trait ManufacturerTrait
|
||||||
* @var string The production status of this part. Can be one of the specified ones.
|
* @var string The production status of this part. Can be one of the specified ones.
|
||||||
* @ORM\Column(type="string", length=255, nullable=true)
|
* @ORM\Column(type="string", length=255, nullable=true)
|
||||||
* @Assert\Choice({"announced", "active", "nrfnd", "eol", "discontinued", ""})
|
* @Assert\Choice({"announced", "active", "nrfnd", "eol", "discontinued", ""})
|
||||||
* @ColumnSecurity(type="string", prefix="status", placeholder="")
|
|
||||||
*/
|
*/
|
||||||
protected ?string $manufacturing_status = '';
|
protected ?string $manufacturing_status = '';
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ declare(strict_types=1);
|
||||||
namespace App\Entity\Parts\PartTraits;
|
namespace App\Entity\Parts\PartTraits;
|
||||||
|
|
||||||
use App\Entity\PriceInformations\Orderdetail;
|
use App\Entity\PriceInformations\Orderdetail;
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
use function count;
|
use function count;
|
||||||
|
@ -59,7 +58,6 @@ trait OrderTrait
|
||||||
* @var Orderdetail[]|Collection the details about how and where you can order this part
|
* @var Orderdetail[]|Collection the details about how and where you can order this part
|
||||||
* @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
|
* @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||||
* @Assert\Valid()
|
* @Assert\Valid()
|
||||||
* @ColumnSecurity(prefix="orderdetails", type="collection")
|
|
||||||
* @ORM\OrderBy({"supplierpartnr" = "ASC"})
|
* @ORM\OrderBy({"supplierpartnr" = "ASC"})
|
||||||
*/
|
*/
|
||||||
protected $orderdetails;
|
protected $orderdetails;
|
||||||
|
@ -67,14 +65,12 @@ trait OrderTrait
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
* @ORM\Column(type="integer")
|
* @ORM\Column(type="integer")
|
||||||
* @ColumnSecurity(prefix="order", type="integer")
|
|
||||||
*/
|
*/
|
||||||
protected int $order_quantity = 0;
|
protected int $order_quantity = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
* @ORM\Column(type="boolean")
|
* @ORM\Column(type="boolean")
|
||||||
* @ColumnSecurity(prefix="order", type="boolean")
|
|
||||||
*/
|
*/
|
||||||
protected bool $manual_order = false;
|
protected bool $manual_order = false;
|
||||||
|
|
||||||
|
@ -82,8 +78,6 @@ trait OrderTrait
|
||||||
* @var Orderdetail
|
* @var Orderdetail
|
||||||
* @ORM\OneToOne(targetEntity="App\Entity\PriceInformations\Orderdetail")
|
* @ORM\OneToOne(targetEntity="App\Entity\PriceInformations\Orderdetail")
|
||||||
* @ORM\JoinColumn(name="order_orderdetails_id", referencedColumnName="id")
|
* @ORM\JoinColumn(name="order_orderdetails_id", referencedColumnName="id")
|
||||||
*
|
|
||||||
* @ColumnSecurity(prefix="order", type="object")
|
|
||||||
*/
|
*/
|
||||||
protected ?Orderdetail $order_orderdetail = null;
|
protected ?Orderdetail $order_orderdetail = null;
|
||||||
|
|
||||||
|
|
|
@ -95,11 +95,12 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa
|
||||||
*/
|
*/
|
||||||
protected $attachments;
|
protected $attachments;
|
||||||
|
|
||||||
/** @var PermissionsEmbed
|
/**
|
||||||
* @ORM\Embedded(class="PermissionsEmbed", columnPrefix="perms_")
|
* @var PermissionData
|
||||||
* @ValidPermission()
|
* @ValidPermission()
|
||||||
|
* @ORM\Embedded(class="PermissionData", columnPrefix="permissions_")
|
||||||
*/
|
*/
|
||||||
protected $permissions;
|
protected PermissionData $permissions;
|
||||||
|
|
||||||
/** @var Collection<int, GroupParameter>
|
/** @var Collection<int, GroupParameter>
|
||||||
* @ORM\OneToMany(targetEntity="App\Entity\Parameters\GroupParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true)
|
* @ORM\OneToMany(targetEntity="App\Entity\Parameters\GroupParameter", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||||
|
@ -111,7 +112,7 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->permissions = new PermissionsEmbed();
|
$this->permissions = new PermissionData();
|
||||||
$this->users = new ArrayCollection();
|
$this->users = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPermissions(): PermissionsEmbed
|
public function getPermissions(): PermissionData
|
||||||
{
|
{
|
||||||
return $this->permissions;
|
return $this->permissions;
|
||||||
}
|
}
|
||||||
|
|
140
src/Entity/UserSystem/PermissionData.php
Normal file
140
src/Entity/UserSystem/PermissionData.php
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity\UserSystem;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store the permissions of a user.
|
||||||
|
* This has to be an embeddable or otherwise doctrine could not track the changes of the underlying data array (which is serialized to JSON in the database)
|
||||||
|
*
|
||||||
|
* @ORM\Embeddable()
|
||||||
|
*/
|
||||||
|
final class PermissionData implements \JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Permission values.
|
||||||
|
*/
|
||||||
|
public const INHERIT = null;
|
||||||
|
public const ALLOW = true;
|
||||||
|
public const DISALLOW = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array This array contains the permission values for each permission
|
||||||
|
* This array contains the permission values for each permission, in the form of:
|
||||||
|
* permission => [
|
||||||
|
* operation => value,
|
||||||
|
* ]
|
||||||
|
* @ORM\Column(type="json", name="data", options={"default": "[]"})
|
||||||
|
*/
|
||||||
|
protected ?array $data = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Permission Data Instance using the given data.
|
||||||
|
* By default, a empty array is used, meaning
|
||||||
|
*/
|
||||||
|
public function __construct(array $data = [])
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a permission value is set for the given permission and operation (meaning there value is not inherit).
|
||||||
|
* @param string $permission
|
||||||
|
* @param string $operation
|
||||||
|
* @return bool True if the permission value is set, false otherwise
|
||||||
|
*/
|
||||||
|
public function isPermissionSet(string $permission, string $operation): bool
|
||||||
|
{
|
||||||
|
return isset($this->data[$permission][$operation]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the permission value for the given permission and operation.
|
||||||
|
* @param string $permission
|
||||||
|
* @param string $operation
|
||||||
|
* @return bool|null True means allow, false means disallow, null means inherit
|
||||||
|
*/
|
||||||
|
public function getPermissionValue(string $permission, string $operation): ?bool
|
||||||
|
{
|
||||||
|
if ($this->isPermissionSet($permission, $operation)) {
|
||||||
|
return $this->data[$permission][$operation];
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the value is not set explicitly, return null (meaning inherit)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the permission value for the given permission and operation.
|
||||||
|
* @param string $permission
|
||||||
|
* @param string $operation
|
||||||
|
* @param bool|null $value
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPermissionValue(string $permission, string $operation, ?bool $value): self
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
//If the value is null, unset the permission value (meaning implicit inherit)
|
||||||
|
unset($this->data[$permission][$operation]);
|
||||||
|
} else {
|
||||||
|
//Otherwise, set the pemission value
|
||||||
|
if(!isset($this->data[$permission])) {
|
||||||
|
$this->data[$permission] = [];
|
||||||
|
}
|
||||||
|
$this->data[$permission][$operation] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the saved permissions and set all operations to inherit (which means they are not defined).
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function resetPermissions(): self
|
||||||
|
{
|
||||||
|
$this->data = [];
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Permission Data Instance using the given JSON encoded data
|
||||||
|
* @param string $json
|
||||||
|
* @return static
|
||||||
|
* @throws \JsonException
|
||||||
|
*/
|
||||||
|
public static function fromJSON(string $json): self
|
||||||
|
{
|
||||||
|
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
|
||||||
|
return new self($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __clone()
|
||||||
|
{
|
||||||
|
$this->data = $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an JSON encodable representation of this object.
|
||||||
|
* @return array|mixed
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
$ret = [];
|
||||||
|
|
||||||
|
//Filter out all empty or null values
|
||||||
|
foreach ($this->data as $permission => $operations) {
|
||||||
|
$ret[$permission] = array_filter($operations, function ($value) {
|
||||||
|
return $value !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
//If the permission has no operations, unset it
|
||||||
|
if (empty($ret[$permission])) {
|
||||||
|
unset($ret[$permission]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,523 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Entity\UserSystem;
|
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This entity represents the permission fields a user or group can have.
|
|
||||||
*
|
|
||||||
* @ORM\Embeddable()
|
|
||||||
*/
|
|
||||||
class PermissionsEmbed
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Permission values.
|
|
||||||
*/
|
|
||||||
public const INHERIT = 0b00;
|
|
||||||
public const ALLOW = 0b01;
|
|
||||||
public const DISALLOW = 0b10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permission strings.
|
|
||||||
*/
|
|
||||||
public const STORELOCATIONS = 'storelocations';
|
|
||||||
public const FOOTRPINTS = 'footprints';
|
|
||||||
public const CATEGORIES = 'categories';
|
|
||||||
public const SUPPLIERS = 'suppliers';
|
|
||||||
public const MANUFACTURERS = 'manufacturers';
|
|
||||||
public const DEVICES = 'devices';
|
|
||||||
public const ATTACHMENT_TYPES = 'attachment_types';
|
|
||||||
public const MEASUREMENT_UNITS = 'measurement_units';
|
|
||||||
public const CURRENCIES = 'currencies';
|
|
||||||
public const TOOLS = 'tools';
|
|
||||||
public const PARTS = 'parts';
|
|
||||||
public const PARTS_NAME = 'parts_name';
|
|
||||||
public const PARTS_DESCRIPTION = 'parts_description';
|
|
||||||
public const PARTS_MINAMOUNT = 'parts_minamount';
|
|
||||||
public const PARTS_FOOTPRINT = 'parts_footprint';
|
|
||||||
public const PARTS_MPN = 'parts_mpn';
|
|
||||||
public const PARTS_STATUS = 'parts_status';
|
|
||||||
public const PARTS_TAGS = 'parts_tags';
|
|
||||||
public const PARTS_UNIT = 'parts_unit';
|
|
||||||
public const PARTS_MASS = 'parts_mass';
|
|
||||||
public const PARTS_LOTS = 'parts_lots';
|
|
||||||
public const PARTS_COMMENT = 'parts_comment';
|
|
||||||
public const PARTS_MANUFACTURER = 'parts_manufacturer';
|
|
||||||
public const PARTS_ORDERDETAILS = 'parts_orderdetails';
|
|
||||||
public const PARTS_PRICES = 'parts_prices';
|
|
||||||
public const PARTS_ATTACHMENTS = 'parts_attachments';
|
|
||||||
public const PARTS_ORDER = 'parts_order';
|
|
||||||
public const GROUPS = 'groups';
|
|
||||||
public const USERS = 'users';
|
|
||||||
public const DATABASE = 'database';
|
|
||||||
public const CONFIG = 'config';
|
|
||||||
public const SYSTEM = 'system';
|
|
||||||
public const DEVICE_PARTS = 'devices_parts';
|
|
||||||
public const SELF = 'self';
|
|
||||||
public const LABELS = 'labels';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $system = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $groups = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $users = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $self = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer", name="system_config")
|
|
||||||
*/
|
|
||||||
protected $config = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer", name="system_database")
|
|
||||||
*/
|
|
||||||
protected $database = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="bigint")
|
|
||||||
*/
|
|
||||||
protected $parts = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_name = 0;
|
|
||||||
|
|
||||||
/** @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_category = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_description = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_minamount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_footprint = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_lots = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_tags = 0;
|
|
||||||
|
|
||||||
/** @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_unit = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_mass = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_manufacturer = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_status = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_mpn = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_comment = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_order = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_orderdetails = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_prices = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint")
|
|
||||||
*/
|
|
||||||
protected $parts_parameters = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="smallint", name="parts_attachements")
|
|
||||||
*/
|
|
||||||
protected $parts_attachments = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $devices = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $devices_parts = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $storelocations = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $footprints = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $categories = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $suppliers = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $manufacturers = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer", name="attachement_types")
|
|
||||||
*/
|
|
||||||
protected $attachment_types = 0;
|
|
||||||
|
|
||||||
/** @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $currencies = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $measurement_units = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $tools = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
* @ORM\Column(type="integer")
|
|
||||||
*/
|
|
||||||
protected $labels = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a permission with the given name is valid for this object.
|
|
||||||
*
|
|
||||||
* @param string $permission_name the name of the permission which should be checked for
|
|
||||||
*
|
|
||||||
* @return bool true if the permission is existing on this object
|
|
||||||
*/
|
|
||||||
public function isValidPermissionName(string $permission_name): bool
|
|
||||||
{
|
|
||||||
return isset($this->{$permission_name});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the bit pair value of the given permission.
|
|
||||||
*
|
|
||||||
* @param string $permission_name the name of the permission, for which the bit pair should be returned
|
|
||||||
* @param int $bit_n the (lower) bit number of the bit pair, which should be read
|
|
||||||
*
|
|
||||||
* @return int The value of the bit pair. Compare to the INHERIT, ALLOW, and DISALLOW consts in this class.
|
|
||||||
*/
|
|
||||||
public function getBitValue(string $permission_name, int $bit_n): int
|
|
||||||
{
|
|
||||||
if (!$this->isValidPermissionName($permission_name)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('No permission with the name "%s" is existing!', $permission_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
$perm_int = (int) $this->{$permission_name};
|
|
||||||
|
|
||||||
return static::readBitPair($perm_int, $bit_n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the operation for the given permission.
|
|
||||||
*
|
|
||||||
* @param string $permission_name the name of the permission, for which the operation should be returned
|
|
||||||
* @param int $bit_n the (lower) bit number of the bit pair for the operation
|
|
||||||
*
|
|
||||||
* @return bool|null The value of the operation. True, if the given operation is allowed, false if disallowed
|
|
||||||
* and null if it should inherit from parent.
|
|
||||||
*/
|
|
||||||
public function getPermissionValue(string $permission_name, int $bit_n): ?bool
|
|
||||||
{
|
|
||||||
$value = $this->getBitValue($permission_name, $bit_n);
|
|
||||||
if (self::ALLOW === $value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::DISALLOW === $value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the given permission and operation.
|
|
||||||
*
|
|
||||||
* @param string $permission_name the name of the permission, for which the bit pair should be written
|
|
||||||
* @param int $bit_n the (lower) bit number of the bit pair, which should be written
|
|
||||||
* @param bool|null $new_value the new value for the operation:
|
|
||||||
* True, if the given operation is allowed, false if disallowed
|
|
||||||
* and null if it should inherit from parent
|
|
||||||
*
|
|
||||||
* @return PermissionsEmbed the instance itself
|
|
||||||
*/
|
|
||||||
public function setPermissionValue(string $permission_name, int $bit_n, ?bool $new_value): self
|
|
||||||
{
|
|
||||||
//Determine which bit value the given value is.
|
|
||||||
if (true === $new_value) {
|
|
||||||
$bit_value = static::ALLOW;
|
|
||||||
} elseif (false === $new_value) {
|
|
||||||
$bit_value = static::DISALLOW;
|
|
||||||
} else {
|
|
||||||
$bit_value = static::INHERIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->setBitValue($permission_name, $bit_n, $bit_value);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the bit value of the given permission and operation.
|
|
||||||
*
|
|
||||||
* @param string $permission_name the name of the permission, for which the bit pair should be written
|
|
||||||
* @param int $bit_n the (lower) bit number of the bit pair, which should be written
|
|
||||||
* @param int $new_value the new (bit) value of the bit pair, which should be written
|
|
||||||
*
|
|
||||||
* @return PermissionsEmbed the instance itself
|
|
||||||
*/
|
|
||||||
public function setBitValue(string $permission_name, int $bit_n, int $new_value): self
|
|
||||||
{
|
|
||||||
if (!$this->isValidPermissionName($permission_name)) {
|
|
||||||
throw new InvalidArgumentException('No permission with the given name is existing!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->{$permission_name} = static::writeBitPair((int) $this->{$permission_name}, $bit_n, $new_value);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the given permission as raw int (all bit at once).
|
|
||||||
*
|
|
||||||
* @param string $permission_name The name of the permission, which should be retrieved.
|
|
||||||
* If this is not existing an exception is thrown.
|
|
||||||
*
|
|
||||||
* @return int the raw permission value
|
|
||||||
*/
|
|
||||||
public function getRawPermissionValue(string $permission_name): int
|
|
||||||
{
|
|
||||||
if (!$this->isValidPermissionName($permission_name)) {
|
|
||||||
throw new InvalidArgumentException('No permission with the given name is existing!');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->{$permission_name};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the given permission to the value.
|
|
||||||
*
|
|
||||||
* @param string $permission_name the name of the permission to that should be set
|
|
||||||
* @param int $value The new value of the permissions
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setRawPermissionValue(string $permission_name, int $value): self
|
|
||||||
{
|
|
||||||
if (!$this->isValidPermissionName($permission_name)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('No permission with the given name %s is existing!', $permission_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->{$permission_name} = $value;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets multiple permissions at once.
|
|
||||||
*
|
|
||||||
* @param array $values An array in the form ['perm_name' => $value], containing the new data
|
|
||||||
* @param array|null $values2 if this array is not null, the first array will treated of list of perm names,
|
|
||||||
* and this array as an array of new values
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setRawPermissionValues(array $values, ?array $values2 = null): self
|
|
||||||
{
|
|
||||||
if (!empty($values2)) {
|
|
||||||
$values = array_combine($values, $values2);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($values as $key => $value) {
|
|
||||||
$this->setRawPermissionValue($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a bit pair from $data.
|
|
||||||
*
|
|
||||||
* @param int|string $data The data from where the bits should be extracted from
|
|
||||||
* @param int $n The number of the lower bit (of the pair) that should be read. Starting from zero.
|
|
||||||
*
|
|
||||||
* @return int the value of the bit pair
|
|
||||||
*/
|
|
||||||
final protected static function readBitPair($data, int $n): int
|
|
||||||
{
|
|
||||||
//Assert::lessThanEq($n, 31, '$n must be smaller than 32, because only a 32bit int is used! Got %s.');
|
|
||||||
if (0 !== $n % 2) {
|
|
||||||
throw new InvalidArgumentException('$n must be dividable by 2, because we address bit pairs here!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$mask = 0b11 << $n; //Create a mask for the data
|
|
||||||
|
|
||||||
return ($data & $mask) >> $n; //Apply mask and shift back
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a bit pair in the given $data and returns it.
|
|
||||||
*
|
|
||||||
* @param int $data The data which should be modified
|
|
||||||
* @param int $n The number of the lower bit of the pair which should be written
|
|
||||||
* @param int $new The new value of the pair
|
|
||||||
*
|
|
||||||
* @return int the new data with the modified pair
|
|
||||||
*/
|
|
||||||
final protected static function writeBitPair(int $data, int $n, int $new): int
|
|
||||||
{
|
|
||||||
//Assert::lessThanEq($n, 31, '$n must be smaller than 32, because only a 32bit int is used! Got %s.');
|
|
||||||
Assert::lessThanEq($new, 3, '$new must be smaller than 3, because a bit pair is written! Got %s.');
|
|
||||||
Assert::greaterThanEq($new, 0, '$new must not be negative, because a bit pair is written! Got %s.');
|
|
||||||
|
|
||||||
if (0 !== $n % 2) {
|
|
||||||
throw new InvalidArgumentException('$n must be dividable by 2, because we address bit pairs here!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$mask = 0b11 << $n; //Mask all bits that should be written
|
|
||||||
$newval = $new << $n; //The new value.
|
|
||||||
return ($data & ~$mask) | ($newval & $mask);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -207,11 +207,6 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||||
*/
|
*/
|
||||||
protected bool $need_pw_change = true;
|
protected bool $need_pw_change = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* //@ORM\Column(type="json").
|
|
||||||
*/
|
|
||||||
//protected $roles = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|null The hashed password
|
* @var string|null The hashed password
|
||||||
* @ORM\Column(type="string", nullable=true)
|
* @ORM\Column(type="string", nullable=true)
|
||||||
|
@ -265,11 +260,12 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||||
*/
|
*/
|
||||||
protected $currency;
|
protected $currency;
|
||||||
|
|
||||||
/** @var PermissionsEmbed
|
/**
|
||||||
* @ORM\Embedded(class="PermissionsEmbed", columnPrefix="perms_")
|
* @var PermissionData
|
||||||
* @ValidPermission()
|
* @ValidPermission()
|
||||||
|
* @ORM\Embedded(class="PermissionData", columnPrefix="permissions_")
|
||||||
*/
|
*/
|
||||||
protected $permissions;
|
protected PermissionData $permissions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DateTime the time until the password reset token is valid
|
* @var DateTime the time until the password reset token is valid
|
||||||
|
@ -280,7 +276,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->permissions = new PermissionsEmbed();
|
$this->permissions = new PermissionData();
|
||||||
$this->u2fKeys = new ArrayCollection();
|
$this->u2fKeys = new ArrayCollection();
|
||||||
$this->webauthn_keys = new ArrayCollection();
|
$this->webauthn_keys = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
@ -427,7 +423,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPermissions(): PermissionsEmbed
|
public function getPermissions(): PermissionData
|
||||||
{
|
{
|
||||||
return $this->permissions;
|
return $this->permissions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ class GroupAdminForm extends BaseEntityAdminForm
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'data' => $builder->getData(),
|
'data' => $builder->getData(),
|
||||||
'disabled' => !$this->security->isGranted('edit_permissions', $entity),
|
'disabled' => !$this->security->isGranted('edit_permissions', $entity),
|
||||||
|
'show_presets' => $this->security->isGranted('edit_permissions', $entity) && !$is_new,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ class AttachmentFormType extends AbstractType
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'attachment.edit.secure_file',
|
'label' => 'attachment.edit.secure_file',
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'disabled' => !$this->security->isGranted('@parts_attachments.show_private'),
|
'disabled' => !$this->security->isGranted('@attachments.show_private'),
|
||||||
'help' => 'attachment.edit.secure_file.help',
|
'help' => 'attachment.edit.secure_file.help',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ class CollectionTypeExtension extends AbstractTypeExtension
|
||||||
|
|
||||||
public static function getExtendedTypes(): iterable
|
public static function getExtendedTypes(): iterable
|
||||||
{
|
{
|
||||||
return [CollectionType::class, WorkaroundCollectionType::class];
|
return [CollectionType::class];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
|
|
@ -50,6 +50,7 @@ use App\Form\Type\StructuralEntityType;
|
||||||
use App\Form\WorkaroundCollectionType;
|
use App\Form\WorkaroundCollectionType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
@ -106,16 +107,15 @@ class OrderdetailType extends AbstractType
|
||||||
}
|
}
|
||||||
|
|
||||||
//Attachment section
|
//Attachment section
|
||||||
$event->getForm()->add('pricedetails', WorkaroundCollectionType::class, [
|
$event->getForm()->add('pricedetails', CollectionType::class, [
|
||||||
'entry_type' => PricedetailType::class,
|
'entry_type' => PricedetailType::class,
|
||||||
'allow_add' => $this->security->isGranted('@parts_prices.create'),
|
'allow_add' => true,
|
||||||
'allow_delete' => $this->security->isGranted('@parts_prices.delete'),
|
'allow_delete' => true,
|
||||||
'label' => false,
|
'label' => false,
|
||||||
'reindex_enable' => true,
|
'reindex_enable' => true,
|
||||||
'prototype_data' => $dummy_pricedetail,
|
'prototype_data' => $dummy_pricedetail,
|
||||||
'by_reference' => false,
|
'by_reference' => false,
|
||||||
'entry_options' => [
|
'entry_options' => [
|
||||||
'disabled' => !$this->security->isGranted('@parts_prices.edit'),
|
|
||||||
'measurement_unit' => $options['measurement_unit'],
|
'measurement_unit' => $options['measurement_unit'],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -103,7 +103,6 @@ class PartBaseType extends AbstractType
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'placeholder' => 'part.edit.name.placeholder',
|
'placeholder' => 'part.edit.name.placeholder',
|
||||||
],
|
],
|
||||||
'disabled' => !$this->security->isGranted('name.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('description', RichTextEditorType::class, [
|
->add('description', RichTextEditorType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
@ -114,7 +113,6 @@ class PartBaseType extends AbstractType
|
||||||
'placeholder' => 'part.edit.description.placeholder',
|
'placeholder' => 'part.edit.description.placeholder',
|
||||||
'rows' => 2,
|
'rows' => 2,
|
||||||
],
|
],
|
||||||
'disabled' => !$this->security->isGranted('description.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('minAmount', SIUnitType::class, [
|
->add('minAmount', SIUnitType::class, [
|
||||||
'attr' => [
|
'attr' => [
|
||||||
|
@ -123,13 +121,11 @@ class PartBaseType extends AbstractType
|
||||||
],
|
],
|
||||||
'label' => 'part.edit.mininstock',
|
'label' => 'part.edit.mininstock',
|
||||||
'measurement_unit' => $part->getPartUnit(),
|
'measurement_unit' => $part->getPartUnit(),
|
||||||
'disabled' => !$this->security->isGranted('minamount.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('category', StructuralEntityType::class, [
|
->add('category', StructuralEntityType::class, [
|
||||||
'class' => Category::class,
|
'class' => Category::class,
|
||||||
'label' => 'part.edit.category',
|
'label' => 'part.edit.category',
|
||||||
'disable_not_selectable' => true,
|
'disable_not_selectable' => true,
|
||||||
'disabled' => !$this->security->isGranted('category.edit', $part),
|
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
|
@ -138,7 +134,6 @@ class PartBaseType extends AbstractType
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'part.edit.footprint',
|
'label' => 'part.edit.footprint',
|
||||||
'disable_not_selectable' => true,
|
'disable_not_selectable' => true,
|
||||||
'disabled' => !$this->security->isGranted('footprint.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('tags', TextType::class, [
|
->add('tags', TextType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
@ -149,7 +144,6 @@ class PartBaseType extends AbstractType
|
||||||
'data-controller' => 'elements--tagsinput',
|
'data-controller' => 'elements--tagsinput',
|
||||||
'data-autocomplete' => $this->urlGenerator->generate('typeahead_tags', ['query' => '__QUERY__']),
|
'data-autocomplete' => $this->urlGenerator->generate('typeahead_tags', ['query' => '__QUERY__']),
|
||||||
],
|
],
|
||||||
'disabled' => !$this->security->isGranted('tags.edit', $part),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Manufacturer section
|
//Manufacturer section
|
||||||
|
@ -158,32 +152,27 @@ class PartBaseType extends AbstractType
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'part.edit.manufacturer.label',
|
'label' => 'part.edit.manufacturer.label',
|
||||||
'disable_not_selectable' => true,
|
'disable_not_selectable' => true,
|
||||||
'disabled' => !$this->security->isGranted('manufacturer.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('manufacturer_product_url', UrlType::class, [
|
->add('manufacturer_product_url', UrlType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'empty_data' => '',
|
'empty_data' => '',
|
||||||
'label' => 'part.edit.manufacturer_url.label',
|
'label' => 'part.edit.manufacturer_url.label',
|
||||||
'disabled' => !$this->security->isGranted('mpn.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('manufacturer_product_number', TextType::class, [
|
->add('manufacturer_product_number', TextType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'empty_data' => '',
|
'empty_data' => '',
|
||||||
'label' => 'part.edit.mpn',
|
'label' => 'part.edit.mpn',
|
||||||
'disabled' => !$this->security->isGranted('mpn.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('manufacturing_status', ChoiceType::class, [
|
->add('manufacturing_status', ChoiceType::class, [
|
||||||
'label' => 'part.edit.manufacturing_status',
|
'label' => 'part.edit.manufacturing_status',
|
||||||
'choices' => $status_choices,
|
'choices' => $status_choices,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'disabled' => !$this->security->isGranted('status.edit', $part),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Advanced section
|
//Advanced section
|
||||||
$builder->add('needsReview', CheckboxType::class, [
|
$builder->add('needsReview', CheckboxType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'part.edit.needs_review',
|
'label' => 'part.edit.needs_review',
|
||||||
'disabled' => !$this->security->isGranted('edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('favorite', CheckboxType::class, [
|
->add('favorite', CheckboxType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
@ -194,14 +183,12 @@ class PartBaseType extends AbstractType
|
||||||
'unit' => 'g',
|
'unit' => 'g',
|
||||||
'label' => 'part.edit.mass',
|
'label' => 'part.edit.mass',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'disabled' => !$this->security->isGranted('mass.edit', $part),
|
|
||||||
])
|
])
|
||||||
->add('partUnit', StructuralEntityType::class, [
|
->add('partUnit', StructuralEntityType::class, [
|
||||||
'class' => MeasurementUnit::class,
|
'class' => MeasurementUnit::class,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'disable_not_selectable' => true,
|
'disable_not_selectable' => true,
|
||||||
'label' => 'part.edit.partUnit',
|
'label' => 'part.edit.partUnit',
|
||||||
'disabled' => !$this->security->isGranted('unit.edit', $part),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Comment section
|
//Comment section
|
||||||
|
@ -212,20 +199,18 @@ class PartBaseType extends AbstractType
|
||||||
'rows' => 4,
|
'rows' => 4,
|
||||||
],
|
],
|
||||||
'mode' => 'markdown-full',
|
'mode' => 'markdown-full',
|
||||||
'disabled' => !$this->security->isGranted('comment.edit', $part),
|
|
||||||
'empty_data' => '',
|
'empty_data' => '',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Part Lots section
|
//Part Lots section
|
||||||
$builder->add('partLots', CollectionType::class, [
|
$builder->add('partLots', CollectionType::class, [
|
||||||
'entry_type' => PartLotType::class,
|
'entry_type' => PartLotType::class,
|
||||||
'allow_add' => $this->security->isGranted('lots.create', $part),
|
'allow_add' => true,
|
||||||
'allow_delete' => $this->security->isGranted('lots.delete', $part),
|
'allow_delete' => true,
|
||||||
'reindex_enable' => true,
|
'reindex_enable' => true,
|
||||||
'label' => false,
|
'label' => false,
|
||||||
'entry_options' => [
|
'entry_options' => [
|
||||||
'measurement_unit' => $part->getPartUnit(),
|
'measurement_unit' => $part->getPartUnit(),
|
||||||
'disabled' => !$this->security->isGranted('lots.edit', $part),
|
|
||||||
],
|
],
|
||||||
'by_reference' => false,
|
'by_reference' => false,
|
||||||
]);
|
]);
|
||||||
|
@ -233,49 +218,45 @@ class PartBaseType extends AbstractType
|
||||||
//Attachment section
|
//Attachment section
|
||||||
$builder->add('attachments', CollectionType::class, [
|
$builder->add('attachments', CollectionType::class, [
|
||||||
'entry_type' => AttachmentFormType::class,
|
'entry_type' => AttachmentFormType::class,
|
||||||
'allow_add' => $this->security->isGranted('attachments.create', $part),
|
'allow_add' => true,
|
||||||
'allow_delete' => $this->security->isGranted('attachments.delete', $part),
|
'allow_delete' => true,
|
||||||
'reindex_enable' => true,
|
'reindex_enable' => true,
|
||||||
'label' => false,
|
'label' => false,
|
||||||
'entry_options' => [
|
'entry_options' => [
|
||||||
'data_class' => PartAttachment::class,
|
'data_class' => PartAttachment::class,
|
||||||
'disabled' => !$this->security->isGranted('attachments.edit', $part),
|
|
||||||
],
|
],
|
||||||
'by_reference' => false,
|
'by_reference' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->add('master_picture_attachment', MasterPictureAttachmentType::class, [
|
$builder->add('master_picture_attachment', MasterPictureAttachmentType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'disabled' => !$this->security->isGranted('attachments.edit', $part),
|
|
||||||
'label' => 'part.edit.master_attachment',
|
'label' => 'part.edit.master_attachment',
|
||||||
'entity' => $part,
|
'entity' => $part,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Orderdetails section
|
//Orderdetails section
|
||||||
$builder->add('orderdetails', WorkaroundCollectionType::class, [
|
$builder->add('orderdetails', CollectionType::class, [
|
||||||
'entry_type' => OrderdetailType::class,
|
'entry_type' => OrderdetailType::class,
|
||||||
'allow_add' => $this->security->isGranted('orderdetails.create', $part),
|
|
||||||
'allow_delete' => $this->security->isGranted('orderdetails.delete', $part),
|
|
||||||
'reindex_enable' => true,
|
'reindex_enable' => true,
|
||||||
|
'allow_add' => true,
|
||||||
|
'allow_delete' => true,
|
||||||
'label' => false,
|
'label' => false,
|
||||||
'by_reference' => false,
|
'by_reference' => false,
|
||||||
'prototype_data' => new Orderdetail(),
|
'prototype_data' => new Orderdetail(),
|
||||||
'entry_options' => [
|
'entry_options' => [
|
||||||
'measurement_unit' => $part->getPartUnit(),
|
'measurement_unit' => $part->getPartUnit(),
|
||||||
'disabled' => !$this->security->isGranted('orderdetails.edit', $part),
|
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->add('parameters', CollectionType::class, [
|
$builder->add('parameters', CollectionType::class, [
|
||||||
'entry_type' => ParameterType::class,
|
'entry_type' => ParameterType::class,
|
||||||
'allow_add' => $this->security->isGranted('parameters.create', $part),
|
'allow_add' => true,
|
||||||
'allow_delete' => $this->security->isGranted('parameters.delete', $part),
|
'allow_delete' => true,
|
||||||
'label' => false,
|
'label' => false,
|
||||||
'reindex_enable' => true,
|
'reindex_enable' => true,
|
||||||
'by_reference' => false,
|
'by_reference' => false,
|
||||||
'prototype_data' => new PartParameter(),
|
'prototype_data' => new PartParameter(),
|
||||||
'entry_options' => [
|
'entry_options' => [
|
||||||
'disabled' => !$this->security->isGranted('parameters.edit', $part),
|
|
||||||
'data_class' => PartParameter::class,
|
'data_class' => PartParameter::class,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -42,7 +42,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Form\Permissions;
|
namespace App\Form\Permissions;
|
||||||
|
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\Options;
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
|
@ -50,10 +50,10 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
class PermissionGroupType extends AbstractType
|
class PermissionGroupType extends AbstractType
|
||||||
{
|
{
|
||||||
protected PermissionResolver $resolver;
|
protected PermissionManager $resolver;
|
||||||
protected array $perm_structure;
|
protected array $perm_structure;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver)
|
public function __construct(PermissionManager $resolver)
|
||||||
{
|
{
|
||||||
$this->resolver = $resolver;
|
$this->resolver = $resolver;
|
||||||
$this->perm_structure = $resolver->getPermissionStructure();
|
$this->perm_structure = $resolver->getPermissionStructure();
|
||||||
|
|
|
@ -43,7 +43,7 @@ declare(strict_types=1);
|
||||||
namespace App\Form\Permissions;
|
namespace App\Form\Permissions;
|
||||||
|
|
||||||
use App\Form\Type\TriStateCheckboxType;
|
use App\Form\Type\TriStateCheckboxType;
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
@ -53,10 +53,10 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
class PermissionType extends AbstractType
|
class PermissionType extends AbstractType
|
||||||
{
|
{
|
||||||
protected PermissionResolver $resolver;
|
protected PermissionManager $resolver;
|
||||||
protected array $perm_structure;
|
protected array $perm_structure;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver)
|
public function __construct(PermissionManager $resolver)
|
||||||
{
|
{
|
||||||
$this->resolver = $resolver;
|
$this->resolver = $resolver;
|
||||||
$this->perm_structure = $resolver->getPermissionStructure();
|
$this->perm_structure = $resolver->getPermissionStructure();
|
||||||
|
@ -97,6 +97,9 @@ class PermissionType extends AbstractType
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'label' => $operation['label'] ?? null,
|
'label' => $operation['label'] ?? null,
|
||||||
'disabled' => $options['disabled'],
|
'disabled' => $options['disabled'],
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'permission-checkbox tristate',
|
||||||
|
],
|
||||||
'label_attr' => [
|
'label_attr' => [
|
||||||
'class' => 'checkbox-inline opacity-100',
|
'class' => 'checkbox-inline opacity-100',
|
||||||
],
|
],
|
||||||
|
|
|
@ -42,7 +42,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Form\Permissions;
|
namespace App\Form\Permissions;
|
||||||
|
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Symfony\Component\Form\DataMapperInterface;
|
use Symfony\Component\Form\DataMapperInterface;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
@ -54,10 +54,10 @@ use Traversable;
|
||||||
*/
|
*/
|
||||||
final class PermissionsMapper implements DataMapperInterface
|
final class PermissionsMapper implements DataMapperInterface
|
||||||
{
|
{
|
||||||
private PermissionResolver $resolver;
|
private PermissionManager $resolver;
|
||||||
private bool $inherit;
|
private bool $inherit;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver, bool $inherit = false)
|
public function __construct(PermissionManager $resolver, bool $inherit = false)
|
||||||
{
|
{
|
||||||
$this->inherit = $inherit;
|
$this->inherit = $inherit;
|
||||||
$this->resolver = $resolver;
|
$this->resolver = $resolver;
|
||||||
|
|
|
@ -42,7 +42,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Form\Permissions;
|
namespace App\Form\Permissions;
|
||||||
|
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use App\Validator\Constraints\NoLockout;
|
use App\Validator\Constraints\NoLockout;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
@ -53,10 +53,10 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
class PermissionsType extends AbstractType
|
class PermissionsType extends AbstractType
|
||||||
{
|
{
|
||||||
protected PermissionResolver $resolver;
|
protected PermissionManager $resolver;
|
||||||
protected array $perm_structure;
|
protected array $perm_structure;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver)
|
public function __construct(PermissionManager $resolver)
|
||||||
{
|
{
|
||||||
$this->resolver = $resolver;
|
$this->resolver = $resolver;
|
||||||
$this->perm_structure = $resolver->getPermissionStructure();
|
$this->perm_structure = $resolver->getPermissionStructure();
|
||||||
|
@ -66,6 +66,7 @@ class PermissionsType extends AbstractType
|
||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'show_legend' => true,
|
'show_legend' => true,
|
||||||
|
'show_presets' => false,
|
||||||
'constraints' => static function (Options $options) {
|
'constraints' => static function (Options $options) {
|
||||||
if (!$options['disabled']) {
|
if (!$options['disabled']) {
|
||||||
return [new NoLockout()];
|
return [new NoLockout()];
|
||||||
|
@ -80,6 +81,7 @@ class PermissionsType extends AbstractType
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||||
{
|
{
|
||||||
$view->vars['show_legend'] = $options['show_legend'];
|
$view->vars['show_legend'] = $options['show_legend'];
|
||||||
|
$view->vars['show_presets'] = $options['show_presets'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
|
|
@ -102,7 +102,7 @@ class UserAdminForm extends AbstractType
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'group.label',
|
'label' => 'group.label',
|
||||||
'disable_not_selectable' => true,
|
'disable_not_selectable' => true,
|
||||||
'disabled' => !$this->security->isGranted('change_group', $entity),
|
'disabled' => !$this->security->isGranted('edit_permissions', $entity),
|
||||||
])
|
])
|
||||||
|
|
||||||
->add('first_name', TextType::class, [
|
->add('first_name', TextType::class, [
|
||||||
|
@ -227,6 +227,7 @@ class UserAdminForm extends AbstractType
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'data' => $builder->getData(),
|
'data' => $builder->getData(),
|
||||||
'disabled' => !$this->security->isGranted('edit_permissions', $entity),
|
'disabled' => !$this->security->isGranted('edit_permissions', $entity),
|
||||||
|
'show_presets' => $this->security->isGranted('edit_permissions', $entity) && !$is_new,
|
||||||
])
|
])
|
||||||
;
|
;
|
||||||
/*->add('comment', CKEditorType::class, ['required' => false,
|
/*->add('comment', CKEditorType::class, ['required' => false,
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
|
||||||
use Symfony\Component\Form\FormInterface;
|
|
||||||
use Symfony\Component\Form\FormView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This a workaround for the issue #37024.
|
|
||||||
*/
|
|
||||||
class WorkaroundCollectionType extends CollectionType
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Use the original implementation for finishView() instead of the one, the one that cause the bug.
|
|
||||||
*/
|
|
||||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
|
||||||
{
|
|
||||||
if ($view->vars['prototype']->vars['multipart']) {
|
|
||||||
$view->vars['multipart'] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -43,6 +43,7 @@ declare(strict_types=1);
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
use App\Entity\Base\AbstractNamedDBElement;
|
use App\Entity\Base\AbstractNamedDBElement;
|
||||||
|
use App\Entity\UserSystem\User;
|
||||||
use App\Helpers\Trees\TreeViewNode;
|
use App\Helpers\Trees\TreeViewNode;
|
||||||
|
|
||||||
class NamedDBElementRepository extends DBElementRepository
|
class NamedDBElementRepository extends DBElementRepository
|
||||||
|
@ -63,6 +64,11 @@ class NamedDBElementRepository extends DBElementRepository
|
||||||
$node = new TreeViewNode($entity->getName(), null, null);
|
$node = new TreeViewNode($entity->getName(), null, null);
|
||||||
$node->setId($entity->getID());
|
$node->setId($entity->getID());
|
||||||
$result[] = $node;
|
$result[] = $node;
|
||||||
|
|
||||||
|
if ($entity instanceof User && $entity->isDisabled()) {
|
||||||
|
//If this is an user, then add a badge when it is disabled
|
||||||
|
$node->setIcon('fa-fw fa-treeview fa-solid fa-user-lock text-muted');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Security\Annotations;
|
|
||||||
|
|
||||||
use App\Entity\Base\AbstractNamedDBElement;
|
|
||||||
use DateTime;
|
|
||||||
use Doctrine\Common\Annotations\Annotation;
|
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
*
|
|
||||||
* @Annotation\Target("PROPERTY")
|
|
||||||
*
|
|
||||||
* With these annotation you can restrict the access to certain coloumns in entities.
|
|
||||||
* The entity which should use this class has to use ElementListener as EntityListener.
|
|
||||||
*/
|
|
||||||
class ColumnSecurity
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string The name of the edit permission
|
|
||||||
*/
|
|
||||||
public string $edit = 'edit';
|
|
||||||
/**
|
|
||||||
* @var string The name of the read permission
|
|
||||||
*/
|
|
||||||
public string $read = 'read';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string A prefix for all permission names (e.g..edit, useful for Parts)
|
|
||||||
*/
|
|
||||||
public string $prefix = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var mixed the placeholder that should be used, when the access to the property is denied
|
|
||||||
*/
|
|
||||||
public $placeholder = null;
|
|
||||||
|
|
||||||
public $subject = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string The name of the property. This is used to determine the default placeholder.
|
|
||||||
* @Annotation\Enum({"integer", "string", "object", "boolean", "datetime", "collection"})
|
|
||||||
*/
|
|
||||||
public $type = 'string';
|
|
||||||
|
|
||||||
public function getReadOperationName(): string
|
|
||||||
{
|
|
||||||
if ('' !== $this->prefix) {
|
|
||||||
return $this->prefix.'.'.$this->read;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->read;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEditOperationName(): string
|
|
||||||
{
|
|
||||||
if ('' !== $this->prefix) {
|
|
||||||
return $this->prefix.'.'.$this->edit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->edit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPlaceholder()
|
|
||||||
{
|
|
||||||
//Check if a class name was specified
|
|
||||||
if (class_exists($this->type)) {
|
|
||||||
$object = new $this->type();
|
|
||||||
if ($object instanceof AbstractNamedDBElement) {
|
|
||||||
if (is_string($this->placeholder) && '' !== $this->placeholder) {
|
|
||||||
$object->setName($this->placeholder);
|
|
||||||
} else {
|
|
||||||
$object->setName('???');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $object;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $this->placeholder) {
|
|
||||||
switch ($this->type) {
|
|
||||||
case 'integer':
|
|
||||||
case 'int':
|
|
||||||
return 0;
|
|
||||||
case 'float':
|
|
||||||
return 0.0;
|
|
||||||
case 'string':
|
|
||||||
return '???';
|
|
||||||
case 'object':
|
|
||||||
return null;
|
|
||||||
case 'collection':
|
|
||||||
return new ArrayCollection();
|
|
||||||
case 'boolean':
|
|
||||||
case 'bool':
|
|
||||||
return false;
|
|
||||||
case 'datetime':
|
|
||||||
return (new DateTime())->setTimestamp(0);
|
|
||||||
default:
|
|
||||||
throw new InvalidArgumentException('Unknown type! You have to specify a placeholder!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->placeholder;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Security\EntityListeners;
|
|
||||||
|
|
||||||
use App\Entity\Base\AbstractDBElement;
|
|
||||||
use App\Entity\UserSystem\User;
|
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use function count;
|
|
||||||
use Doctrine\Common\Annotations\Reader;
|
|
||||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Doctrine\ORM\Event\PreFlushEventArgs;
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
|
||||||
use Doctrine\ORM\Mapping\PostLoad;
|
|
||||||
use function get_class;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use ReflectionClass;
|
|
||||||
use Symfony\Component\Security\Core\Security;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The purpose of this class is to hook into the doctrine entity lifecycle and restrict access to entity informations
|
|
||||||
* configured by ColoumnSecurity Annotation.
|
|
||||||
* If the current programm is running from CLI (like a CLI command), the security checks are disabled.
|
|
||||||
* (Commands should be able to do everything they like).
|
|
||||||
*
|
|
||||||
* If a user does not have access to an coloumn, it will be filled, with a placeholder, after doctrine loading is finished.
|
|
||||||
* The edit process is also catched, so that these placeholders, does not get saved to database.
|
|
||||||
*/
|
|
||||||
class ElementPermissionListener
|
|
||||||
{
|
|
||||||
protected Security $security;
|
|
||||||
protected Reader $reader;
|
|
||||||
protected EntityManagerInterface $em;
|
|
||||||
protected bool $disabled;
|
|
||||||
|
|
||||||
protected array $perm_cache;
|
|
||||||
|
|
||||||
public function __construct(Security $security, Reader $reader, EntityManagerInterface $em)
|
|
||||||
{
|
|
||||||
$this->security = $security;
|
|
||||||
$this->reader = $reader;
|
|
||||||
$this->em = $em;
|
|
||||||
//Disable security when the current program is running from CLI
|
|
||||||
$this->disabled = $this->isRunningFromCLI();
|
|
||||||
$this->perm_cache = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @PostLoad
|
|
||||||
* @ORM\PostUpdate()
|
|
||||||
* This function is called after doctrine filled, the entity properties with db values.
|
|
||||||
* We use this, to check if the user is allowed to access these properties, and if not, we write a placeholder
|
|
||||||
* into the element properties, so that a user only gets non sensitve data.
|
|
||||||
*
|
|
||||||
* This function is also called after an entity was updated, so we dont show the original data to user,
|
|
||||||
* after an update.
|
|
||||||
*/
|
|
||||||
public function postLoadHandler(AbstractDBElement $element, LifecycleEventArgs $event): void
|
|
||||||
{
|
|
||||||
//Do nothing if security is disabled
|
|
||||||
if ($this->disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Read Annotations and properties.
|
|
||||||
$reflectionClass = new ReflectionClass($element);
|
|
||||||
$properties = $reflectionClass->getProperties();
|
|
||||||
|
|
||||||
foreach ($properties as $property) {
|
|
||||||
/** @var ColumnSecurity */
|
|
||||||
$annotation = $this->reader->getPropertyAnnotation(
|
|
||||||
$property,
|
|
||||||
ColumnSecurity::class
|
|
||||||
);
|
|
||||||
|
|
||||||
//Check if user is allowed to read info, otherwise apply placeholder
|
|
||||||
if ((null !== $annotation) && !$this->isGranted('read', $annotation, $element)) {
|
|
||||||
$property->setAccessible(true);
|
|
||||||
$value = $annotation->getPlaceholder();
|
|
||||||
|
|
||||||
//Detach placeholder entities, so we dont get cascade errors
|
|
||||||
if ($value instanceof AbstractDBElement) {
|
|
||||||
$this->em->detach($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
$property->setValue($element, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ORM\PreFlush()
|
|
||||||
* This function is called before flushing. We use it, to remove all placeholders.
|
|
||||||
* We do it here and not in preupdate, because this is called before calculating the changeset,
|
|
||||||
* and so we dont get problems with orphan removal.
|
|
||||||
*/
|
|
||||||
public function preFlushHandler(AbstractDBElement $element, PreFlushEventArgs $eventArgs): void
|
|
||||||
{
|
|
||||||
//Do nothing if security is disabled
|
|
||||||
if ($this->disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$unitOfWork = $eventArgs->getEntityManager()->getUnitOfWork();
|
|
||||||
|
|
||||||
$reflectionClass = new ReflectionClass($element);
|
|
||||||
$properties = $reflectionClass->getProperties();
|
|
||||||
|
|
||||||
$old_data = $unitOfWork->getOriginalEntityData($element);
|
|
||||||
|
|
||||||
foreach ($properties as $property) {
|
|
||||||
$annotation = $this->reader->getPropertyAnnotation(
|
|
||||||
$property,
|
|
||||||
ColumnSecurity::class
|
|
||||||
);
|
|
||||||
|
|
||||||
$changed = false;
|
|
||||||
|
|
||||||
//Only set the field if it has an annotation
|
|
||||||
if (null !== $annotation) {
|
|
||||||
$property->setAccessible(true);
|
|
||||||
|
|
||||||
//If the user is not allowed to edit or read this property, reset all values.
|
|
||||||
//Set value to old value, so that there a no change to this property
|
|
||||||
if ((!$this->isGranted('read', $annotation, $element)
|
|
||||||
|| !$this->isGranted('edit', $annotation, $element)) && isset(
|
|
||||||
$old_data[$property->getName()]
|
|
||||||
)) {
|
|
||||||
$property->setValue($element, $old_data[$property->getName()]);
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($changed) {
|
|
||||||
//Schedule for update, so the post update method will be called
|
|
||||||
$unitOfWork->scheduleForUpdate($element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function checks if the current script is run from web or from a terminal.
|
|
||||||
*
|
|
||||||
* @return bool Returns true if the current programm is running from CLI (terminal)
|
|
||||||
*/
|
|
||||||
protected function isRunningFromCLI(): bool
|
|
||||||
{
|
|
||||||
return empty($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['HTTP_USER_AGENT']) && count($_SERVER['argv']) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if access to the property of the given element is granted.
|
|
||||||
* This function adds an additional cache layer, where the voters are called only once (to improve performance).
|
|
||||||
*
|
|
||||||
* @param string $mode What operation should be checked. Must be 'read' or 'edit'
|
|
||||||
* @param ColumnSecurity $annotation The annotation of the property that should be checked
|
|
||||||
* @param AbstractDBElement $element The element that should for which should be checked
|
|
||||||
*
|
|
||||||
* @return bool True if the user is allowed to read that property
|
|
||||||
*/
|
|
||||||
protected function isGranted(string $mode, ColumnSecurity $annotation, AbstractDBElement $element): bool
|
|
||||||
{
|
|
||||||
if ('read' === $mode) {
|
|
||||||
$operation = $annotation->getReadOperationName();
|
|
||||||
} elseif ('edit' === $mode) {
|
|
||||||
$operation = $annotation->getEditOperationName();
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException('$mode must be either "read" or "edit"!');
|
|
||||||
}
|
|
||||||
|
|
||||||
//Users must always be checked, because its return value can differ if it is the user itself or something else
|
|
||||||
if ($element instanceof User) {
|
|
||||||
return $this->security->isGranted($operation, $element);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if we have already have saved the permission, otherwise save it to cache
|
|
||||||
if (!isset($this->perm_cache[$mode][get_class($element)][$operation])) {
|
|
||||||
$this->perm_cache[$mode][get_class($element)][$operation] = $this->security->isGranted($operation, $element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->perm_cache[$mode][get_class($element)][$operation];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,9 +42,9 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Security\Interfaces;
|
namespace App\Security\Interfaces;
|
||||||
|
|
||||||
use App\Entity\UserSystem\PermissionsEmbed;
|
use App\Entity\UserSystem\PermissionData;
|
||||||
|
|
||||||
interface HasPermissionsInterface
|
interface HasPermissionsInterface
|
||||||
{
|
{
|
||||||
public function getPermissions(): PermissionsEmbed;
|
public function getPermissions(): PermissionData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,14 +44,19 @@ namespace App\Security;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use Symfony\Component\Security\Core\Exception\AccountStatusException;
|
use Symfony\Component\Security\Core\Exception\AccountStatusException;
|
||||||
|
use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException;
|
||||||
use Symfony\Component\Security\Core\Exception\DisabledException;
|
use Symfony\Component\Security\Core\Exception\DisabledException;
|
||||||
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
final class UserChecker implements UserCheckerInterface
|
final class UserChecker implements UserCheckerInterface
|
||||||
{
|
{
|
||||||
public function __construct()
|
private TranslatorInterface $translator;
|
||||||
|
|
||||||
|
public function __construct(TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +82,8 @@ final class UserChecker implements UserCheckerInterface
|
||||||
|
|
||||||
//Check if user is disabled. Then dont allow login
|
//Check if user is disabled. Then dont allow login
|
||||||
if ($user->isDisabled()) {
|
if ($user->isDisabled()) {
|
||||||
throw new DisabledException();
|
//throw new DisabledException();
|
||||||
|
throw new CustomUserMessageAccountStatusException($this->translator->trans('user.login_error.user_disabled'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,22 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\Attachments\Attachment;
|
use App\Entity\Attachments\Attachment;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
use function in_array;
|
use function in_array;
|
||||||
|
|
||||||
class AttachmentVoter extends ExtendedVoter
|
class AttachmentVoter extends ExtendedVoter
|
||||||
{
|
{
|
||||||
|
protected $security;
|
||||||
|
|
||||||
|
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security)
|
||||||
|
{
|
||||||
|
parent::__construct($resolver, $entityManager);
|
||||||
|
$this->security = $security;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to voteOnAttribute, but checking for the anonymous user is already done.
|
* Similar to voteOnAttribute, but checking for the anonymous user is already done.
|
||||||
* The current user (or the anonymous user) is passed by $user.
|
* The current user (or the anonymous user) is passed by $user.
|
||||||
|
@ -56,7 +68,31 @@ class AttachmentVoter extends ExtendedVoter
|
||||||
*/
|
*/
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
return $this->resolver->inherit($user, 'parts_attachments', $attribute) ?? false;
|
//return $this->resolver->inherit($user, 'attachments', $attribute) ?? false;
|
||||||
|
|
||||||
|
//If the attachment has no element (which should not happen), we deny access, as we can not determine if the user is allowed to access the associated element
|
||||||
|
$target_element = $subject->getElement();
|
||||||
|
if (! $subject instanceof Attachment || null === $target_element) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Depending on the operation delegate either to the attachments element or to the attachment permission
|
||||||
|
switch ($attribute) {
|
||||||
|
//We can view the attachment if we can view the element
|
||||||
|
case 'read':
|
||||||
|
case 'view':
|
||||||
|
return $this->security->isGranted('read', $target_element);
|
||||||
|
//We can edit/create/delete the attachment if we can edit the element
|
||||||
|
case 'edit':
|
||||||
|
case 'create':
|
||||||
|
case 'delete':
|
||||||
|
return $this->security->isGranted('edit', $target_element);
|
||||||
|
|
||||||
|
case 'show_private':
|
||||||
|
return $this->resolver->inherit($user, 'attachments', 'show_private') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('Encountered unknown attribute "'.$attribute.'" in AttachmentVoter!');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +106,8 @@ class AttachmentVoter extends ExtendedVoter
|
||||||
protected function supports(string $attribute, $subject): bool
|
protected function supports(string $attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if (is_a($subject, Attachment::class, true)) {
|
if (is_a($subject, Attachment::class, true)) {
|
||||||
return in_array($attribute, $this->resolver->listOperationsForPermission('parts_attachments'), false);
|
//These are the allowed attributes
|
||||||
|
return in_array($attribute, ['read', 'view', 'edit', 'delete', 'create', 'show_private'], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Allow class name as subject
|
//Allow class name as subject
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
@ -55,9 +55,9 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
abstract class ExtendedVoter extends Voter
|
abstract class ExtendedVoter extends Voter
|
||||||
{
|
{
|
||||||
protected EntityManagerInterface $entityManager;
|
protected EntityManagerInterface $entityManager;
|
||||||
protected PermissionResolver $resolver;
|
protected PermissionManager $resolver;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver, EntityManagerInterface $entityManager)
|
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
$this->resolver = $resolver;
|
$this->resolver = $resolver;
|
||||||
$this->entityManager = $entityManager;
|
$this->entityManager = $entityManager;
|
||||||
|
@ -72,7 +72,7 @@ abstract class ExtendedVoter extends Voter
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the user is anonymous, we use the anonymous user.
|
// if the user is anonymous (meaning $user is null), we use the anonymous user.
|
||||||
if (!$user instanceof User) {
|
if (!$user instanceof User) {
|
||||||
/** @var UserRepository $repo */
|
/** @var UserRepository $repo */
|
||||||
$repo = $this->entityManager->getRepository(User::class);
|
$repo = $this->entityManager->getRepository(User::class);
|
||||||
|
|
|
@ -25,30 +25,60 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\PriceInformations\Orderdetail;
|
use App\Entity\PriceInformations\Orderdetail;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
class OrderdetailVoter extends ExtendedVoter
|
class OrderdetailVoter extends ExtendedVoter
|
||||||
{
|
{
|
||||||
/**
|
protected Security $security;
|
||||||
* @var string[] When this permsission are encountered, they are checked on part
|
|
||||||
*/
|
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security)
|
||||||
protected const PART_PERMS = ['show_history', 'revert_element'];
|
{
|
||||||
|
parent::__construct($resolver, $entityManager);
|
||||||
|
$this->security = $security;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
if (in_array($attribute, self::PART_PERMS, true)) {
|
if (! is_a($subject, Orderdetail::class, true)) {
|
||||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
throw new \RuntimeException('This voter can only handle Orderdetail objects!');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolver->inherit($user, 'parts_orderdetails', $attribute) ?? false;
|
switch ($attribute) {
|
||||||
|
case 'read':
|
||||||
|
$operation = 'read';
|
||||||
|
break;
|
||||||
|
case 'edit': //As long as we can edit, we can also edit orderdetails
|
||||||
|
case 'create':
|
||||||
|
case 'delete':
|
||||||
|
$operation = 'edit';
|
||||||
|
break;
|
||||||
|
case 'show_history':
|
||||||
|
$operation = 'show_history';
|
||||||
|
break;
|
||||||
|
case 'revert_element':
|
||||||
|
$operation = 'revert_element';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!');
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we have no part associated use the generic part permission
|
||||||
|
if (is_string($subject) || $subject->getPart() === null) {
|
||||||
|
return $this->resolver->inherit($user, 'parts', $operation) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise vote on the part
|
||||||
|
return $this->security->isGranted($attribute, $subject->getPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if (is_a($subject, Orderdetail::class, true)) {
|
if (is_a($subject, Orderdetail::class, true)) {
|
||||||
return in_array($attribute, array_merge(
|
return in_array($attribute, self::ALLOWED_PERMS, true);
|
||||||
self::PART_PERMS,
|
|
||||||
$this->resolver->listOperationsForPermission('parts_orderdetails')
|
|
||||||
), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
113
src/Security/Voter/ParameterVoter.php
Normal file
113
src/Security/Voter/ParameterVoter.php
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Entity\Parameters\AbstractParameter;
|
||||||
|
use App\Entity\Parameters\AttachmentTypeParameter;
|
||||||
|
use App\Entity\Parameters\CategoryParameter;
|
||||||
|
use App\Entity\Parameters\CurrencyParameter;
|
||||||
|
use App\Entity\Parameters\DeviceParameter;
|
||||||
|
use App\Entity\Parameters\FootprintParameter;
|
||||||
|
use App\Entity\Parameters\GroupParameter;
|
||||||
|
use App\Entity\Parameters\ManufacturerParameter;
|
||||||
|
use App\Entity\Parameters\MeasurementUnitParameter;
|
||||||
|
use App\Entity\Parameters\PartParameter;
|
||||||
|
use App\Entity\Parameters\StorelocationParameter;
|
||||||
|
use App\Entity\Parameters\SupplierParameter;
|
||||||
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use RuntimeException;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class ParameterVoter extends ExtendedVoter
|
||||||
|
{
|
||||||
|
|
||||||
|
protected Security $security;
|
||||||
|
|
||||||
|
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security)
|
||||||
|
{
|
||||||
|
$this->security = $security;
|
||||||
|
parent::__construct($resolver, $entityManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
||||||
|
{
|
||||||
|
//return $this->resolver->inherit($user, 'attachments', $attribute) ?? false;
|
||||||
|
|
||||||
|
if (!$subject instanceof AbstractParameter) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the attachment has no element (which should not happen), we deny access, as we can not determine if the user is allowed to access the associated element
|
||||||
|
$target_element = $subject->getElement();
|
||||||
|
if ($target_element !== null) {
|
||||||
|
//Depending on the operation delegate either to the attachments element or to the attachment permission
|
||||||
|
|
||||||
|
|
||||||
|
switch ($attribute) {
|
||||||
|
//We can view the attachment if we can view the element
|
||||||
|
case 'read':
|
||||||
|
case 'view':
|
||||||
|
$operation = 'read';
|
||||||
|
break;
|
||||||
|
//We can edit/create/delete the attachment if we can edit the element
|
||||||
|
case 'edit':
|
||||||
|
case 'create':
|
||||||
|
case 'delete':
|
||||||
|
$operation = 'edit';
|
||||||
|
break;
|
||||||
|
case 'show_history':
|
||||||
|
$operation = 'show_history';
|
||||||
|
break;
|
||||||
|
case 'revert_element':
|
||||||
|
$operation = 'revert_element';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException('Unknown operation: '.$attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->security->isGranted($operation, $target_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we do not have a concrete element, we delegate to the different categories
|
||||||
|
if ($subject instanceof AttachmentTypeParameter) {
|
||||||
|
$param = 'attachment_types';
|
||||||
|
} elseif ($subject instanceof CategoryParameter) {
|
||||||
|
$param = 'categories';
|
||||||
|
} elseif ($subject instanceof CurrencyParameter) {
|
||||||
|
$param = 'currencies';
|
||||||
|
} elseif ($subject instanceof DeviceParameter) {
|
||||||
|
$param = 'devices';
|
||||||
|
} elseif ($subject instanceof FootprintParameter) {
|
||||||
|
$param = 'footprints';
|
||||||
|
} elseif ($subject instanceof GroupParameter) {
|
||||||
|
$param = 'groups';
|
||||||
|
} elseif ($subject instanceof ManufacturerParameter) {
|
||||||
|
$param = 'manufacturers';
|
||||||
|
} elseif ($subject instanceof MeasurementUnitParameter) {
|
||||||
|
$param = 'measurement_units';
|
||||||
|
} elseif ($subject instanceof PartParameter) {
|
||||||
|
$param = 'parts';
|
||||||
|
} elseif ($subject instanceof StorelocationParameter) {
|
||||||
|
$param = 'storelocations';
|
||||||
|
} elseif ($subject instanceof SupplierParameter) {
|
||||||
|
$param = 'suppliers';
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException('Encountered unknown Parameter type: ' . get_class($subject));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resolver->inherit($user, $param, $attribute) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function supports(string $attribute, $subject)
|
||||||
|
{
|
||||||
|
if (is_a($subject, AbstractParameter::class, true)) {
|
||||||
|
//These are the allowed attributes
|
||||||
|
return in_array($attribute, ['read', 'edit', 'delete', 'create', 'show_history', 'revert_element'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Allow class name as subject
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,30 +25,60 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\Parts\PartLot;
|
use App\Entity\Parts\PartLot;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
class PartLotVoter extends ExtendedVoter
|
class PartLotVoter extends ExtendedVoter
|
||||||
{
|
{
|
||||||
/**
|
protected Security $security;
|
||||||
* @var string[] When this permsission are encountered, they are checked on part
|
|
||||||
*/
|
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security)
|
||||||
protected const PART_PERMS = ['show_history', 'revert_element'];
|
{
|
||||||
|
parent::__construct($resolver, $entityManager);
|
||||||
|
$this->security = $security;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
if (in_array($attribute, self::PART_PERMS, true)) {
|
if (! is_a($subject, PartLot::class, true)) {
|
||||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
throw new \RuntimeException('This voter can only handle PartLot objects!');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolver->inherit($user, 'parts_lots', $attribute) ?? false;
|
switch ($attribute) {
|
||||||
|
case 'read':
|
||||||
|
$operation = 'read';
|
||||||
|
break;
|
||||||
|
case 'edit': //As long as we can edit, we can also edit orderdetails
|
||||||
|
case 'create':
|
||||||
|
case 'delete':
|
||||||
|
$operation = 'edit';
|
||||||
|
break;
|
||||||
|
case 'show_history':
|
||||||
|
$operation = 'show_history';
|
||||||
|
break;
|
||||||
|
case 'revert_element':
|
||||||
|
$operation = 'revert_element';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!');
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we have no part associated use the generic part permission
|
||||||
|
if (is_string($subject) || $subject->getPart() === null) {
|
||||||
|
return $this->resolver->inherit($user, 'parts', $operation) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise vote on the part
|
||||||
|
return $this->security->isGranted($attribute, $subject->getPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if (is_a($subject, PartLot::class, true)) {
|
if (is_a($subject, PartLot::class, true)) {
|
||||||
return in_array($attribute, array_merge(
|
return in_array($attribute, self::ALLOWED_PERMS, true);
|
||||||
self::PART_PERMS,
|
|
||||||
$this->resolver->listOperationsForPermission('parts_lots')
|
|
||||||
), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -57,13 +57,6 @@ class PartVoter extends ExtendedVoter
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if (is_a($subject, Part::class, true)) {
|
if (is_a($subject, Part::class, true)) {
|
||||||
//Check if a sub permission should be checked -> $attribute has format name.edit
|
|
||||||
if (false !== strpos($attribute, '.')) {
|
|
||||||
[$perm, $op] = explode('.', $attribute);
|
|
||||||
|
|
||||||
return $this->resolver->isValidOperation('parts_'.$perm, $op);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->resolver->isValidOperation('parts', $attribute);
|
return $this->resolver->isValidOperation('parts', $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,13 +66,6 @@ class PartVoter extends ExtendedVoter
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
//Check for sub permissions
|
|
||||||
if (false !== strpos($attribute, '.')) {
|
|
||||||
[$perm, $op] = explode('.', $attribute);
|
|
||||||
|
|
||||||
return $this->resolver->inherit($user, 'parts_'.$perm, $op) ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Null concealing operator means, that no
|
//Null concealing operator means, that no
|
||||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,14 @@ class PermissionVoter extends ExtendedVoter
|
||||||
$attribute = ltrim($attribute, '@');
|
$attribute = ltrim($attribute, '@');
|
||||||
[$perm, $op] = explode('.', $attribute);
|
[$perm, $op] = explode('.', $attribute);
|
||||||
|
|
||||||
return $this->resolver->isValidOperation($perm, $op);
|
$valid = $this->resolver->isValidOperation($perm, $op);
|
||||||
|
|
||||||
|
//if an invalid operation is encountered, throw an exception so the developer knows it
|
||||||
|
if(!$valid) {
|
||||||
|
throw new \RuntimeException('Encountered invalid permission operation "'.$op.'" for permission "'.$perm.'"!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -25,30 +25,60 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\PriceInformations\Pricedetail;
|
use App\Entity\PriceInformations\Pricedetail;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
class PricedetailVoter extends ExtendedVoter
|
class PricedetailVoter extends ExtendedVoter
|
||||||
{
|
{
|
||||||
/**
|
protected Security $security;
|
||||||
* @var string[] When this permsission are encountered, they are checked on part
|
|
||||||
*/
|
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, Security $security)
|
||||||
protected const PART_PERMS = ['show_history', 'revert_element'];
|
{
|
||||||
|
parent::__construct($resolver, $entityManager);
|
||||||
|
$this->security = $security;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
if (in_array($attribute, self::PART_PERMS, true)) {
|
if (!is_a($subject, Pricedetail::class, true)) {
|
||||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
throw new \RuntimeException('This voter can only handle Pricedetails objects!');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolver->inherit($user, 'parts_prices', $attribute) ?? false;
|
switch ($attribute) {
|
||||||
|
case 'read':
|
||||||
|
$operation = 'read';
|
||||||
|
break;
|
||||||
|
case 'edit': //As long as we can edit, we can also edit orderdetails
|
||||||
|
case 'create':
|
||||||
|
case 'delete':
|
||||||
|
$operation = 'edit';
|
||||||
|
break;
|
||||||
|
case 'show_history':
|
||||||
|
$operation = 'show_history';
|
||||||
|
break;
|
||||||
|
case 'revert_element':
|
||||||
|
$operation = 'revert_element';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException('Encountered unknown operation "'.$attribute.'"!');
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we have no part associated use the generic part permission
|
||||||
|
if (is_string($subject) || $subject->getOrderdetail() === null || $subject->getOrderdetail()->getPart() === null) {
|
||||||
|
return $this->resolver->inherit($user, 'parts', $operation) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise vote on the part
|
||||||
|
return $this->security->isGranted($attribute, $subject->getOrderdetail()->getPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if (is_a($subject, Pricedetail::class, true)) {
|
if (is_a($subject, Pricedetail::class, true)) {
|
||||||
return in_array($attribute, array_merge(
|
return in_array($attribute, self::ALLOWED_PERMS, true);
|
||||||
self::PART_PERMS,
|
|
||||||
$this->resolver->listOperationsForPermission('parts_prices')
|
|
||||||
), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
58
src/Services/Misc/DBInfoHelper.php
Normal file
58
src/Services/Misc/DBInfoHelper.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Misc;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
|
||||||
|
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service provides db independent information about the database.
|
||||||
|
*/
|
||||||
|
class DBInfoHelper
|
||||||
|
{
|
||||||
|
protected Connection $connection;
|
||||||
|
protected EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
|
{
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
$this->connection = $entityManager->getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the database type of the used database.
|
||||||
|
* @return string|null Returns 'mysql' for MySQL/MariaDB and 'sqlite' for SQLite. Returns null if unknown type
|
||||||
|
*/
|
||||||
|
public function getDatabaseType(): ?string
|
||||||
|
{
|
||||||
|
if ($this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
|
||||||
|
return 'mysql';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||||
|
return 'sqlite';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the database version of the used database.
|
||||||
|
* @return string|null
|
||||||
|
* @throws \Doctrine\DBAL\Exception
|
||||||
|
*/
|
||||||
|
public function getDatabaseVersion(): ?string
|
||||||
|
{
|
||||||
|
if ($this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
|
||||||
|
return $this->connection->fetchOne('SELECT VERSION()');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||||
|
return $this->connection->fetchOne('SELECT sqlite_version()');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,9 +76,11 @@ final class PartsTableActionHandler
|
||||||
|
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'favorite':
|
case 'favorite':
|
||||||
|
$this->denyAccessUnlessGranted('change_favorite', $part);
|
||||||
$part->setFavorite(true);
|
$part->setFavorite(true);
|
||||||
break;
|
break;
|
||||||
case 'unfavorite':
|
case 'unfavorite':
|
||||||
|
$this->denyAccessUnlessGranted('change_favorite', $part);
|
||||||
$part->setFavorite(false);
|
$part->setFavorite(false);
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
|
@ -86,19 +88,19 @@ final class PartsTableActionHandler
|
||||||
$this->entityManager->remove($part);
|
$this->entityManager->remove($part);
|
||||||
break;
|
break;
|
||||||
case 'change_category':
|
case 'change_category':
|
||||||
$this->denyAccessUnlessGranted('category.edit', $part);
|
$this->denyAccessUnlessGranted('@categories.read');
|
||||||
$part->setCategory($this->entityManager->find(Category::class, $target_id));
|
$part->setCategory($this->entityManager->find(Category::class, $target_id));
|
||||||
break;
|
break;
|
||||||
case 'change_footprint':
|
case 'change_footprint':
|
||||||
$this->denyAccessUnlessGranted('footprint.edit', $part);
|
$this->denyAccessUnlessGranted('@footprints.read');
|
||||||
$part->setFootprint(null === $target_id ? null : $this->entityManager->find(Footprint::class, $target_id));
|
$part->setFootprint(null === $target_id ? null : $this->entityManager->find(Footprint::class, $target_id));
|
||||||
break;
|
break;
|
||||||
case 'change_manufacturer':
|
case 'change_manufacturer':
|
||||||
$this->denyAccessUnlessGranted('manufacturer.edit', $part);
|
$this->denyAccessUnlessGranted('@manufacturers.read');
|
||||||
$part->setManufacturer(null === $target_id ? null : $this->entityManager->find(Manufacturer::class, $target_id));
|
$part->setManufacturer(null === $target_id ? null : $this->entityManager->find(Manufacturer::class, $target_id));
|
||||||
break;
|
break;
|
||||||
case 'change_unit':
|
case 'change_unit':
|
||||||
$this->denyAccessUnlessGranted('unit.edit', $part);
|
$this->denyAccessUnlessGranted('@measurement_units.read');
|
||||||
$part->setPartUnit(null === $target_id ? null : $this->entityManager->find(MeasurementUnit::class, $target_id));
|
$part->setPartUnit(null === $target_id ? null : $this->entityManager->find(MeasurementUnit::class, $target_id));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\TranslationExtractor;
|
namespace App\Services\TranslationExtractor;
|
||||||
|
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Symfony\Component\Translation\Extractor\ExtractorInterface;
|
use Symfony\Component\Translation\Extractor\ExtractorInterface;
|
||||||
use Symfony\Component\Translation\MessageCatalogue;
|
use Symfony\Component\Translation\MessageCatalogue;
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ final class PermissionExtractor implements ExtractorInterface
|
||||||
private array $permission_structure;
|
private array $permission_structure;
|
||||||
private bool $finished = false;
|
private bool $finished = false;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver)
|
public function __construct(PermissionManager $resolver)
|
||||||
{
|
{
|
||||||
$this->permission_structure = $resolver->getPermissionStructure();
|
$this->permission_structure = $resolver->getPermissionStructure();
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,12 +242,15 @@ class ToolsTreeBuilder
|
||||||
protected function getShowNodes(): array
|
protected function getShowNodes(): array
|
||||||
{
|
{
|
||||||
$show_nodes = [];
|
$show_nodes = [];
|
||||||
|
|
||||||
|
if ($this->security->isGranted('@parts.read')) {
|
||||||
$show_nodes[] = (new TreeViewNode(
|
$show_nodes[] = (new TreeViewNode(
|
||||||
$this->translator->trans('tree.tools.show.all_parts'),
|
$this->translator->trans('tree.tools.show.all_parts'),
|
||||||
$this->urlGenerator->generate('parts_show_all')
|
$this->urlGenerator->generate('parts_show_all')
|
||||||
))->setIcon('fa-fw fa-treeview fa-solid fa-globe');
|
))->setIcon('fa-fw fa-treeview fa-solid fa-globe');
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->security->isGranted('read', new PartAttachment())) {
|
if ($this->security->isGranted('@attachments.list_attachments')) {
|
||||||
$show_nodes[] = (new TreeViewNode(
|
$show_nodes[] = (new TreeViewNode(
|
||||||
$this->translator->trans('tree.tools.show.all_attachments'),
|
$this->translator->trans('tree.tools.show.all_attachments'),
|
||||||
$this->urlGenerator->generate('attachment_list')
|
$this->urlGenerator->generate('attachment_list')
|
||||||
|
@ -291,6 +294,13 @@ class ToolsTreeBuilder
|
||||||
))->setIcon('fa-fw fa-treeview fa-solid fa-binoculars');
|
))->setIcon('fa-fw fa-treeview fa-solid fa-binoculars');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->security->isGranted('@system.server_infos')) {
|
||||||
|
$nodes[] = (new TreeViewNode(
|
||||||
|
$this->translator->trans('tools.server_infos.title'),
|
||||||
|
$this->urlGenerator->generate('tools_server_infos')
|
||||||
|
))->setIcon('fa-fw fa-treeview fa-solid fa-database');
|
||||||
|
}
|
||||||
|
|
||||||
return $nodes;
|
return $nodes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ declare(strict_types=1);
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services\UserSystem;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
@ -50,7 +50,6 @@ use Symfony\Component\Mailer\MailerInterface;
|
||||||
use Symfony\Component\Mime\Address;
|
use Symfony\Component\Mime\Address;
|
||||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||||
|
|
||||||
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
|
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
|
@ -40,7 +40,7 @@ declare(strict_types=1);
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services\UserSystem;
|
||||||
|
|
||||||
use App\Configuration\PermissionsConfiguration;
|
use App\Configuration\PermissionsConfiguration;
|
||||||
use App\Entity\UserSystem\Group;
|
use App\Entity\UserSystem\Group;
|
||||||
|
@ -52,7 +52,12 @@ use Symfony\Component\Config\Definition\Processor;
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
use Symfony\Component\Config\Resource\FileResource;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class PermissionResolver
|
/**
|
||||||
|
* This class manages the permissions of users and groups.
|
||||||
|
* Permissions are defined in the config/permissions.yaml file, and are parsed and resolved by this class using the
|
||||||
|
* user and hierachical group PermissionData information.
|
||||||
|
*/
|
||||||
|
class PermissionManager
|
||||||
{
|
{
|
||||||
protected $permission_structure;
|
protected $permission_structure;
|
||||||
|
|
||||||
|
@ -81,6 +86,7 @@ class PermissionResolver
|
||||||
* Check if a user/group is allowed to do the specified operation for the permission.
|
* Check if a user/group is allowed to do the specified operation for the permission.
|
||||||
*
|
*
|
||||||
* See permissions.yaml for valid permission operation combinations.
|
* See permissions.yaml for valid permission operation combinations.
|
||||||
|
* This function does not check, if the permission is valid!
|
||||||
*
|
*
|
||||||
* @param HasPermissionsInterface $user the user/group for which the operation should be checked
|
* @param HasPermissionsInterface $user the user/group for which the operation should be checked
|
||||||
* @param string $permission the name of the permission for which should be checked
|
* @param string $permission the name of the permission for which should be checked
|
||||||
|
@ -92,18 +98,13 @@ class PermissionResolver
|
||||||
public function dontInherit(HasPermissionsInterface $user, string $permission, string $operation): ?bool
|
public function dontInherit(HasPermissionsInterface $user, string $permission, string $operation): ?bool
|
||||||
{
|
{
|
||||||
//Get the permissions from the user
|
//Get the permissions from the user
|
||||||
$perm_list = $user->getPermissions();
|
return $user->getPermissions()->getPermissionValue($permission, $operation);
|
||||||
|
|
||||||
//Determine bit number using our configuration
|
|
||||||
$bit = $this->permission_structure['perms'][$permission]['operations'][$operation]['bit'];
|
|
||||||
|
|
||||||
return $perm_list->getPermissionValue($permission, $bit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a user is allowed to do the specified operation for the permission.
|
* Checks if a user is allowed to do the specified operation for the permission.
|
||||||
* In contrast to dontInherit() it tries to resolve the inherit values, of the user, by going upwards in the
|
* In contrast to dontInherit() it tries to resolve to inherit values, of the user, by going upwards in the
|
||||||
* hierachy (user -> group -> parent group -> so on). But even in this case it is possible, that the inherit value
|
* hierarchy (user -> group -> parent group -> so on). But even in this case it is possible, that to inherit value
|
||||||
* could be resolved, and this function returns null.
|
* could be resolved, and this function returns null.
|
||||||
*
|
*
|
||||||
* In that case the voter should set it manually to false by using ?? false.
|
* In that case the voter should set it manually to false by using ?? false.
|
||||||
|
@ -153,10 +154,12 @@ class PermissionResolver
|
||||||
//Get the permissions from the user
|
//Get the permissions from the user
|
||||||
$perm_list = $user->getPermissions();
|
$perm_list = $user->getPermissions();
|
||||||
|
|
||||||
//Determine bit number using our configuration
|
//Check if the permission/operation combination is valid
|
||||||
$bit = $this->permission_structure['perms'][$permission]['operations'][$operation]['bit'];
|
if (! $this->isValidOperation($permission, $operation)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The permission/operation combination "%s.%s" is not valid!', $permission, $operation));
|
||||||
|
}
|
||||||
|
|
||||||
$perm_list->setPermissionValue($permission, $bit, $new_val);
|
$perm_list->setPermissionValue($permission, $operation, $new_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,13 +209,90 @@ class PermissionResolver
|
||||||
isset($this->permission_structure['perms'][$permission]['operations'][$operation]);
|
isset($this->permission_structure['perms'][$permission]['operations'][$operation]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This functions sets all operations mentioned in the alsoSet value of a permission, so that the structure is always valid.
|
||||||
|
* @param HasPermissionsInterface $user
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function ensureCorrectSetOperations(HasPermissionsInterface $user): void
|
||||||
|
{
|
||||||
|
//If we have changed anything on the permission structure due to the alsoSet value, this becomes true, so we
|
||||||
|
//redo the whole process, to ensure that all alsoSet values are set recursively.
|
||||||
|
$anything_changed = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$anything_changed = false; //Reset the variable for the next iteration
|
||||||
|
|
||||||
|
//Check for each permission and operation, for an alsoSet attribute
|
||||||
|
foreach ($this->permission_structure['perms'] as $perm_key => $permission) {
|
||||||
|
foreach ($permission['operations'] as $op_key => $op) {
|
||||||
|
if (!empty($op['alsoSet']) &&
|
||||||
|
true === $this->dontInherit($user, $perm_key, $op_key)) {
|
||||||
|
//Set every op listed in also Set
|
||||||
|
foreach ($op['alsoSet'] as $set_also) {
|
||||||
|
//If the alsoSet value contains a dot then we set the operation of another permission
|
||||||
|
if (false !== strpos($set_also, '.')) {
|
||||||
|
[$set_perm, $set_op] = explode('.', $set_also);
|
||||||
|
} else {
|
||||||
|
//Else we set the operation of the same permission
|
||||||
|
[$set_perm, $set_op] = [$perm_key, $set_also];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if we change the value of the permission
|
||||||
|
if ($this->dontInherit($user, $set_perm, $set_op) !== true) {
|
||||||
|
$this->setPermission($user, $set_perm, $set_op, true);
|
||||||
|
//Mark the change, so we redo the whole process
|
||||||
|
$anything_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while($anything_changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all possible operations of all possible permissions of the given entity to the given value.
|
||||||
|
* @param HasPermissionsInterface $perm_holder
|
||||||
|
* @param bool|null $new_value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setAllPermissions(HasPermissionsInterface $perm_holder, ?bool $new_value): void
|
||||||
|
{
|
||||||
|
foreach ($this->permission_structure['perms'] as $perm_key => $permission) {
|
||||||
|
foreach ($permission['operations'] as $op_key => $op) {
|
||||||
|
$this->setPermission($perm_holder, $perm_key, $op_key, $new_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all operations of the given permissions to the given value.
|
||||||
|
* Please note that you have to call ensureCorrectSetOperations() after this function, to ensure that all alsoSet values are set.
|
||||||
|
*
|
||||||
|
* @param HasPermissionsInterface $perm_holder
|
||||||
|
* @param string $permission
|
||||||
|
* @param bool|null $new_value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setAllOperationsOfPermission(HasPermissionsInterface $perm_holder, string $permission, ?bool $new_value): void
|
||||||
|
{
|
||||||
|
if (!$this->isValidPermission($permission)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('A permission with that name is not existing! Got %s.', $permission));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->permission_structure['perms'][$permission]['operations'] as $op_key => $op) {
|
||||||
|
$this->setPermission($perm_holder, $permission, $op_key, $new_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function generatePermissionStructure()
|
protected function generatePermissionStructure()
|
||||||
{
|
{
|
||||||
$cache = new ConfigCache($this->cache_file, $this->is_debug);
|
$cache = new ConfigCache($this->cache_file, $this->is_debug);
|
||||||
|
|
||||||
//Check if the cache is fresh, else regenerate it.
|
//Check if the cache is fresh, else regenerate it.
|
||||||
if (!$cache->isFresh()) {
|
if (!$cache->isFresh()) {
|
||||||
$permission_file = __DIR__.'/../../config/permissions.yaml';
|
$permission_file = __DIR__.'/../../../config/permissions.yaml';
|
||||||
|
|
||||||
//Read the permission config file...
|
//Read the permission config file...
|
||||||
$config = Yaml::parse(
|
$config = Yaml::parse(
|
151
src/Services/UserSystem/PermissionPresetsHelper.php
Normal file
151
src/Services/UserSystem/PermissionPresetsHelper.php
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\UserSystem;
|
||||||
|
|
||||||
|
use App\Entity\UserSystem\PermissionData;
|
||||||
|
use App\Security\Interfaces\HasPermissionsInterface;
|
||||||
|
|
||||||
|
class PermissionPresetsHelper
|
||||||
|
{
|
||||||
|
public const PRESET_ALL_INHERIT = 'all_inherit';
|
||||||
|
public const PRESET_ALL_FORBID = 'all_forbid';
|
||||||
|
public const PRESET_ALL_ALLOW = 'all_allow';
|
||||||
|
public const PRESET_READ_ONLY = 'read_only';
|
||||||
|
public const PRESET_EDITOR = 'editor';
|
||||||
|
public const PRESET_ADMIN = 'admin';
|
||||||
|
|
||||||
|
private PermissionManager $permissionResolver;
|
||||||
|
|
||||||
|
public function __construct(PermissionManager $permissionResolver)
|
||||||
|
{
|
||||||
|
$this->permissionResolver = $permissionResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the given preset to the permission holding entity (like a user)
|
||||||
|
* The permission data will be reset during the process and then the preset will be applied.
|
||||||
|
*
|
||||||
|
* @param string $preset_name The name of the preset to use
|
||||||
|
* @return HasPermissionsInterface
|
||||||
|
*/
|
||||||
|
public function applyPreset(HasPermissionsInterface $perm_holder, string $preset_name): HasPermissionsInterface
|
||||||
|
{
|
||||||
|
//We need to reset the permission data first (afterwards all values are inherit)
|
||||||
|
$perm_holder->getPermissions()->resetPermissions();
|
||||||
|
|
||||||
|
switch($preset_name) {
|
||||||
|
case self::PRESET_ALL_INHERIT:
|
||||||
|
//Do nothing, all values are inherit after reset
|
||||||
|
break;
|
||||||
|
case self::PRESET_ALL_FORBID:
|
||||||
|
$this->allForbid($perm_holder);
|
||||||
|
break;
|
||||||
|
case self::PRESET_ALL_ALLOW:
|
||||||
|
$this->allAllow($perm_holder);
|
||||||
|
break;
|
||||||
|
case self::PRESET_READ_ONLY:
|
||||||
|
$this->readOnly($perm_holder);
|
||||||
|
break;
|
||||||
|
case self::PRESET_EDITOR:
|
||||||
|
$this->editor($perm_holder);
|
||||||
|
break;
|
||||||
|
case self::PRESET_ADMIN:
|
||||||
|
$this->admin($perm_holder);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \InvalidArgumentException('Unknown permission preset name: '.$preset_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ensure that permissions are valid (alsoSet values are set), this allows us to use the permission inheritance system to keep the presets short
|
||||||
|
$this->permissionResolver->ensureCorrectSetOperations($perm_holder);
|
||||||
|
|
||||||
|
return $perm_holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function admin(HasPermissionsInterface $perm_holder): void
|
||||||
|
{
|
||||||
|
//Apply everything from editor permission
|
||||||
|
$this->editor($perm_holder);
|
||||||
|
|
||||||
|
//Allow user and group access
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'users', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'groups', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Allow access to system log and server infos
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'system', 'show_logs', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'system', 'server_infos', PermissionData::ALLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function editor(HasPermissionsInterface $permHolder): HasPermissionsInterface
|
||||||
|
{
|
||||||
|
//Apply everything from read-only
|
||||||
|
$this->readOnly($permHolder);
|
||||||
|
|
||||||
|
//Set datastructures
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'parts', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'categories', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'storelocations', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'footprints', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'manufacturers', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'attachment_types', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'currencies', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'measurement_units', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'suppliers', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Attachments permissions
|
||||||
|
$this->permissionResolver->setPermission($permHolder, 'attachments', 'show_private', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Labels permissions (allow all except use twig)
|
||||||
|
$this->permissionResolver->setAllOperationsOfPermission($permHolder, 'labels', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setPermission($permHolder,'labels', 'use_twig', PermissionData::INHERIT);
|
||||||
|
|
||||||
|
//Self permissions
|
||||||
|
$this->permissionResolver->setPermission($permHolder, 'self', 'edit_infos', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Various other permissions
|
||||||
|
$this->permissionResolver->setPermission($permHolder, 'tools', 'lastActivity', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
|
||||||
|
return $permHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function readOnly(HasPermissionsInterface $perm_holder): HasPermissionsInterface
|
||||||
|
{
|
||||||
|
//It is sufficient to only set the read operation to allow, read operations for datastructures are inherited
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'parts', 'read', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Set tools permissions
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'tools', 'statistics', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'tools', 'label_scanner', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'tools', 'reel_calculator', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Set attachments permissions
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'attachments', 'list_attachments', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Set user (self) permissions
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'self', 'show_permissions', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Label permissions
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'labels', 'create_labels', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'labels', 'edit_options', PermissionData::ALLOW);
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'labels', 'read_profiles', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Set devices permissions
|
||||||
|
$this->permissionResolver->setPermission($perm_holder, 'devices', 'read', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
return $perm_holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function AllForbid(HasPermissionsInterface $perm_holder): HasPermissionsInterface
|
||||||
|
{
|
||||||
|
$this->permissionResolver->setAllPermissions($perm_holder, PermissionData::DISALLOW);
|
||||||
|
return $perm_holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function AllAllow(HasPermissionsInterface $perm_holder): HasPermissionsInterface
|
||||||
|
{
|
||||||
|
$this->permissionResolver->setAllPermissions($perm_holder, PermissionData::ALLOW);
|
||||||
|
return $perm_holder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ declare(strict_types=1);
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Services\TFA;
|
namespace App\Services\UserSystem\TFA;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
|
@ -40,7 +40,7 @@ declare(strict_types=1);
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Services\TFA;
|
namespace App\Services\UserSystem\TFA;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace App\Validator\Constraints;
|
||||||
|
|
||||||
use App\Entity\UserSystem\Group;
|
use App\Entity\UserSystem\Group;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
@ -53,12 +53,12 @@ use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
|
||||||
class NoLockoutValidator extends ConstraintValidator
|
class NoLockoutValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
protected PermissionResolver $resolver;
|
protected PermissionManager $resolver;
|
||||||
protected array $perm_structure;
|
protected array $perm_structure;
|
||||||
protected Security $security;
|
protected Security $security;
|
||||||
protected EntityManagerInterface $entityManager;
|
protected EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver, Security $security, EntityManagerInterface $entityManager)
|
public function __construct(PermissionManager $resolver, Security $security, EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
$this->resolver = $resolver;
|
$this->resolver = $resolver;
|
||||||
$this->perm_structure = $resolver->getPermissionStructure();
|
$this->perm_structure = $resolver->getPermissionStructure();
|
||||||
|
|
|
@ -43,20 +43,19 @@ declare(strict_types=1);
|
||||||
namespace App\Validator\Constraints;
|
namespace App\Validator\Constraints;
|
||||||
|
|
||||||
use App\Security\Interfaces\HasPermissionsInterface;
|
use App\Security\Interfaces\HasPermissionsInterface;
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Symfony\Component\Validator\ConstraintValidator;
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
|
||||||
class ValidPermissionValidator extends ConstraintValidator
|
class ValidPermissionValidator extends ConstraintValidator
|
||||||
{
|
{
|
||||||
protected PermissionResolver $resolver;
|
protected PermissionManager $resolver;
|
||||||
protected array $perm_structure;
|
protected array $perm_structure;
|
||||||
|
|
||||||
public function __construct(PermissionResolver $resolver)
|
public function __construct(PermissionManager $resolver)
|
||||||
{
|
{
|
||||||
$this->resolver = $resolver;
|
$this->resolver = $resolver;
|
||||||
$this->perm_structure = $resolver->getPermissionStructure();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,17 +73,6 @@ class ValidPermissionValidator extends ConstraintValidator
|
||||||
/** @var HasPermissionsInterface $perm_holder */
|
/** @var HasPermissionsInterface $perm_holder */
|
||||||
$perm_holder = $this->context->getObject();
|
$perm_holder = $this->context->getObject();
|
||||||
|
|
||||||
//Check for each permission and operation, for an alsoSet attribute
|
$this->resolver->ensureCorrectSetOperations($perm_holder);
|
||||||
foreach ($this->perm_structure['perms'] as $perm_key => $permission) {
|
|
||||||
foreach ($permission['operations'] as $op_key => $op) {
|
|
||||||
if (!empty($op['alsoSet']) &&
|
|
||||||
true === $this->resolver->dontInherit($perm_holder, $perm_key, $op_key)) {
|
|
||||||
//Set every op listed in also Set
|
|
||||||
foreach ($op['alsoSet'] as $set_also) {
|
|
||||||
$this->resolver->setPermission($perm_holder, $perm_key, $set_also, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
{% block additional_panes %}
|
{% block additional_panes %}
|
||||||
<div class="tab-pane" id="tab_permissions">
|
<div class="tab-pane" id="tab_permissions">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token('group' ~ entity.id) }}">
|
||||||
{{ form_row(form.permissions) }}
|
{{ form_row(form.permissions) }}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% if multi_checkbox %}
|
{% if multi_checkbox %}
|
||||||
|
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input tristate permission_multicheckbox" id="mulit_check_{{ form.vars.label }}">
|
<input type="checkbox" class="form-check-input tristate permission-checkbox permission_multicheckbox" id="mulit_check_{{ form.vars.label }}">
|
||||||
<label class="form-check-label" for="mulit_check_{{ form.vars.label }}">
|
<label class="form-check-label" for="mulit_check_{{ form.vars.label }}">
|
||||||
<b>{{ form.vars.label | trans }}</b>
|
<b>{{ form.vars.label | trans }}</b>
|
||||||
</label>
|
</label>
|
||||||
|
@ -48,27 +48,47 @@
|
||||||
{% block permissions_row %}
|
{% block permissions_row %}
|
||||||
{{ form_errors(form) }}
|
{{ form_errors(form) }}
|
||||||
|
|
||||||
|
<div class="row mb-2">
|
||||||
{% if show_legend %}
|
{% if show_legend %}
|
||||||
<div class="mb-2">
|
<div class="col">
|
||||||
<label><b>{% trans %}permission.legend.title{% endtrans %}:</b></label>
|
<label><b>{% trans %}permission.legend.title{% endtrans %}:</b></label>
|
||||||
<div>
|
<div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input type="checkbox" class="form-check-input" disabled>
|
<input type="checkbox" class="form-check-input permission-checkbox" disabled>
|
||||||
<label class="form-check-label opacity-100">{% trans %}permission.legend.disallow{% endtrans %}</label>
|
<label class="form-check-label opacity-100">{% trans %}permission.legend.disallow{% endtrans %}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" disabled>
|
<input class="form-check-input permission-checkbox" type="checkbox" checked disabled>
|
||||||
<label class="form-check-label opacity-100">{% trans %}permission.legend.allow{% endtrans %}</label>
|
<label class="form-check-label opacity-100">{% trans %}permission.legend.allow{% endtrans %}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input type="checkbox" class="tristate form-check-input" indeterminate="indeterminate" disabled>
|
<input type="checkbox" class="tristate form-check-input permission-checkbox" indeterminate="indeterminate" value="indeterminate" disabled>
|
||||||
<label class="form-check-label opacity-100">{% trans %}permission.legend.inherit{% endtrans %}</label>
|
<label class="form-check-label opacity-100">{% trans %}permission.legend.inherit{% endtrans %}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if show_presets %}
|
||||||
|
<div class="col text-end">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
{% trans %}permission.preset.button{% endtrans %}
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><button type="submit" name="permission_preset" value="read_only" class="dropdown-item" >{% trans %}permission.preset.read_only{% endtrans%} <br><small class="text-muted">{% trans %}permission.preset.read_only.desc{% endtrans%}</small></button></li>
|
||||||
|
<li><button type="submit" name="permission_preset" value="editor" class="dropdown-item" >{% trans %}permission.preset.editor{% endtrans%} <br><small class="text-muted">{% trans %}permission.preset.editor.desc{% endtrans%}</small></button></li>
|
||||||
|
<li><button type="submit" name="permission_preset" value="admin" class="dropdown-item" >{% trans %}permission.preset.admin{% endtrans%} <br><small class="text-muted">{% trans %}permission.preset.admin.desc{% endtrans%}</small></button></li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><button type="submit" name="permission_preset" value="all_inherit" class="dropdown-item" >{% trans %}permission.preset.all_inherit{% endtrans%}<br><small class="text-muted">{% trans %}permission.preset.all_inherit.desc{% endtrans%}</small></button></li>
|
||||||
|
<li><button type="submit" name="permission_preset" value="all_forbid" class="dropdown-item" >{% trans %}permission.preset.all_forbid{% endtrans%}<br><small class="text-muted">{% trans %}permission.preset.all_forbid.desc{% endtrans%}</small></button></li>
|
||||||
|
<li><button type="submit" name="permission_preset" value="all_allow" class="dropdown-item" >{% trans %}permission.preset.all_allow{% endtrans%}<br><small class="text-muted">{% trans %}permission.preset.all_allow.desc{% endtrans%}</small></button></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
{% for group in form %}
|
{% for group in form %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" {{ collection.create_btn() }}
|
<button type="button" class="btn btn-success" {{ collection.create_btn() }}
|
||||||
{% if not is_granted('lots.create', part) %}disabled{% endif %}>
|
{% if not is_granted('edit', part) %}disabled{% endif %}>
|
||||||
<i class="fas fa-plus-square fa-fw"></i>
|
<i class="fas fa-plus-square fa-fw"></i>
|
||||||
{% trans %}part_lot.create{% endtrans %}
|
{% trans %}part_lot.create{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
<table class="table table-striped table-sm table-responsive-md" id="orderdetails_table" {{ collection.target() }}>
|
<table class="table table-striped table-sm table-responsive-md" id="orderdetails_table" {{ collection.target() }}>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for detail in form.orderdetails %}
|
{% for detail in form.orderdetails %}
|
||||||
{{ form_widget(detail, {'disable_delete' : not is_granted('orderdetails.delete', part)}) }}
|
{{ form_widget(detail) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" {{ collection.create_btn() }} {% if not is_granted('orderdetails.create', part) %}disabled{% endif %}>
|
<button type="button" class="btn btn-success" {{ collection.create_btn() }}>
|
||||||
<i class="fas fa-plus-square fa-fw"></i>
|
<i class="fas fa-plus-square fa-fw"></i>
|
||||||
{% trans %}orderdetail.create{% endtrans %}
|
{% trans %}orderdetail.create{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" {% if not is_granted('parameters.create', part) %}disabled{% endif %} {{ collection.create_btn() }}>
|
<button type="button" class="btn btn-success" {% if not is_granted('edit', part) %}disabled{% endif %} {{ collection.create_btn() }}>
|
||||||
<i class="fas fa-plus-square fa-fw"></i>
|
<i class="fas fa-plus-square fa-fw"></i>
|
||||||
{% trans %}specification.create{% endtrans %}
|
{% trans %}specification.create{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<td>{{ form_widget(form.price_related_quantity, {'attr': {'class': 'form-control-sm'}}) }} {{ form_errors(form.price_related_quantity) }}</td>
|
<td>{{ form_widget(form.price_related_quantity, {'attr': {'class': 'form-control-sm'}}) }} {{ form_errors(form.price_related_quantity) }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-danger order_btn_delete btn-sm" title="{% trans %}orderdetail.delete{% endtrans %}"
|
<button type="button" class="btn btn-danger order_btn_delete btn-sm" title="{% trans %}orderdetail.delete{% endtrans %}"
|
||||||
{{ collection.delete_btn() }} {% if not is_granted('@parts_prices.delete') %}disabled{% endif %}>
|
{{ collection.delete_btn() }}>
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
{{ form_errors(form) }}
|
{{ form_errors(form) }}
|
||||||
|
@ -50,15 +50,14 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" {{ collection.create_pricedetail_btn() }} {% if not is_granted('@parts_prices.create') %}disabled{% endif %}>
|
<button type="button" class="btn btn-success" {{ collection.create_pricedetail_btn() }}>
|
||||||
<i class="fas fa-plus-square fa-fw"></i>
|
<i class="fas fa-plus-square fa-fw"></i>
|
||||||
{% trans %}pricedetail.create{% endtrans %}
|
{% trans %}pricedetail.create{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-danger order_btn_delete" {{ collection.delete_btn() }} title="{% trans %}orderdetail.delete{% endtrans %}"
|
<button type="button" class="btn btn-danger order_btn_delete" {{ collection.delete_btn() }} title="{% trans %}orderdetail.delete{% endtrans %}">
|
||||||
{% if not is_granted('@parts_orderdetails.delete') %}disabled{% endif %}>
|
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
{{ form_errors(form) }}
|
{{ form_errors(form) }}
|
||||||
|
@ -93,8 +92,7 @@
|
||||||
{{ form_widget(form) }}
|
{{ form_widget(form) }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-danger lot_btn_delete" {{ collection.delete_btn() }}
|
<button type="button" class="btn btn-danger lot_btn_delete" {{ collection.delete_btn() }}>
|
||||||
{% if not is_granted('lots.delete', form.parent.parent.vars.data) %}disabled{% endif %}>
|
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
{% trans %}part_lot.delete{% endtrans %}
|
{% trans %}part_lot.delete{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
|
@ -111,7 +109,7 @@
|
||||||
{{ form_widget(form) }}
|
{{ form_widget(form) }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-danger lot_btn_delete" {{ collection.delete_btn() }} {# {% if not is_granted('attachments.delete', part) %}disabled{% endif %}#}>
|
<button type="button" class="btn btn-danger lot_btn_delete" {{ collection.delete_btn() }}>
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
{% trans %}attachment.delete{% endtrans %}
|
{% trans %}attachment.delete{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans %}user.creating_user{% endtrans %}</td>
|
<td>{% trans %}user.creating_user{% endtrans %}</td>
|
||||||
<td>{% if is_granted('show_users', part) %}
|
<td>{% if is_granted('show_history', part) %}
|
||||||
{{ creating_user(part).fullName(true) ?? 'Unknown'|trans }}
|
{{ creating_user(part).fullName(true) ?? 'Unknown'|trans }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans %}accessDenied{% endtrans %}
|
{% trans %}accessDenied{% endtrans %}
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans %}user.last_editing_user{% endtrans %}</td>
|
<td>{% trans %}user.last_editing_user{% endtrans %}</td>
|
||||||
<td>{% if is_granted('show_users', part) %}
|
<td>{% if is_granted('show_history', part) %}
|
||||||
{{ last_editing_user(part).fullName(true) ?? 'Unknown'|trans }}
|
{{ last_editing_user(part).fullName(true) ?? 'Unknown'|trans }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans %}accessDenied{% endtrans %}
|
{% trans %}accessDenied{% endtrans %}
|
||||||
|
|
13
templates/Tools/ServerInfos/_db.html.twig
Normal file
13
templates/Tools/ServerInfos/_db.html.twig
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{% import "helper.twig" as helper %}
|
||||||
|
<table class="table table-sm table-striped table-hover table-bordered">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Database type</td>
|
||||||
|
<td>{{ db_type }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Database Server Version</td>
|
||||||
|
<td>{{ db_version }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
66
templates/Tools/ServerInfos/_partdb.html.twig
Normal file
66
templates/Tools/ServerInfos/_partdb.html.twig
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
{% import "helper.twig" as helper %}
|
||||||
|
|
||||||
|
<table class="table table-sm table-striped table-hover table-bordered">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Part-DB Version</td>
|
||||||
|
<td>{{ shivas_app_version }} {% if git_branch is not empty or git_commit is not empty %}({{ git_branch ?? '' }}/{{ git_commit ?? '' }}){% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Symfony environment</td>
|
||||||
|
<td>{{ enviroment }} (Debug: {{ helper.boolean_badge(is_debug) }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Part-DB Instance name</td>
|
||||||
|
<td>{{ partdb_title }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Default locale</td>
|
||||||
|
<td>{{ default_locale | locale_name }} ({{ default_locale }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Default timezone</td>
|
||||||
|
<td>{{ default_timezone }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Default Currency</td>
|
||||||
|
<td>{{ default_currency | currency_name }} ({{ default_currency }}, {{ default_currency | currency_symbol }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Default theme</td>
|
||||||
|
<td>{{ default_theme | default('bootstrap') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Enabled locales</td>
|
||||||
|
<td>{{ helper.array_to_tags(enabled_locales | map(l => "#{l|locale_name} (#{l})")) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Demo Mode</td>
|
||||||
|
<td>{{ helper.boolean_badge(demo_mode) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>GPDR Compliance Mode</td>
|
||||||
|
<td>{{ helper.boolean_badge(gpdr_compliance) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Use Gravatar</td>
|
||||||
|
<td>{{ helper.boolean_badge(use_gravatar) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Password Reset via Email enabled</td>
|
||||||
|
<td>{{ helper.boolean_badge(email_password_reset) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Configured E-Mail sender</td>
|
||||||
|
<td>{{ email_sender }} ({{ email_sender_name }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Allow server-side download of attachments</td>
|
||||||
|
<td>{{ helper.boolean_badge(allow_attachments_downloads) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Detailed error pages enabled</td>
|
||||||
|
<td>{{ helper.boolean_badge(detailed_error_pages) }} (Admin Contact email: {{ error_page_admin_email }})</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
25
templates/Tools/ServerInfos/_php.html.twig
Normal file
25
templates/Tools/ServerInfos/_php.html.twig
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{% import "helper.twig" as helper %}
|
||||||
|
<table class="table table-sm table-striped table-hover table-bordered">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>PHP version</td>
|
||||||
|
<td>{{ php_version }} (SAPI: {{ php_sapi }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server Operating System</td>
|
||||||
|
<td>{{ php_uname }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Opcache enabled</td>
|
||||||
|
<td>{{ helper.boolean_badge(php_opcache_enabled) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Maximum upload sizee (upload_max_filesize / post_max_size)</td>
|
||||||
|
<td>{{ php_upload_max_filesize }} / {{ php_post_max_size }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PHP extensions</td>
|
||||||
|
<td>{{ helper.array_to_tags(php_extensions) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
30
templates/Tools/ServerInfos/main.html.twig
Normal file
30
templates/Tools/ServerInfos/main.html.twig
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "main_card.html.twig" %}
|
||||||
|
|
||||||
|
{% block title %}{% trans %}tools.server_infos.title{% endtrans %}{% endblock %}
|
||||||
|
|
||||||
|
{% block card_title %}
|
||||||
|
<i class="fas fa-database"></i> {% trans %}tools.server_infos.title{% endtrans %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block card_content %}
|
||||||
|
<nav>
|
||||||
|
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||||
|
<button class="nav-link active" id="server_infos-partdb-tab" data-bs-toggle="tab" data-bs-target="#server_infos-partdb" type="button" role="tab" aria-controls="server_infos-partdb" aria-selected="true"><i class="fa-solid fa-microchip"></i> Part-DB</button>
|
||||||
|
<button class="nav-link" id="server_infos-php-tab" data-bs-toggle="tab" data-bs-target="#server_infos-php" type="button" role="tab" aria-controls="server_infos-php" aria-selected="false"><i class="fa-brands fa-php"></i> PHP</button>
|
||||||
|
<button class="nav-link" id="server_infos-database-tab" data-bs-toggle="tab" data-bs-target="#server_infos-db" type="button" role="tab" aria-controls="server_infos-db" aria-selected="false"><i class="fa-solid fa-database"></i> Database</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="tab-content" id="nav-tabContent">
|
||||||
|
<div class="tab-pane fade show active" id="server_infos-partdb" role="tabpanel" aria-labelledby="server_infos-partdb-tab">
|
||||||
|
{% include "Tools/ServerInfos/_partdb.html.twig" %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="server_infos-php" role="tabpanel" aria-labelledby="server_infos-php-tab">
|
||||||
|
{% include "Tools/ServerInfos/_php.html.twig" %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="server_infos-db" role="tabpanel" aria-labelledby="server_infos-database-tab">
|
||||||
|
{% include "Tools/ServerInfos/_db.html.twig" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Run <code>php bin/console partdb:check-requirements</code> in a terminal in your Part-DB folder to check if there are any recommendations for your system configuration.</p>
|
||||||
|
{% endblock %}
|
|
@ -26,8 +26,9 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
{% if is_granted('@parts.read') %}
|
||||||
{% include "_navbar_search.html.twig" %}
|
{% include "_navbar_search.html.twig" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<ul class="navbar-nav ms-3" id="login-content">
|
<ul class="navbar-nav ms-3" id="login-content">
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" {{ collection.create_btn() }} {% if part_mode and not is_granted('attachments.create', part) %}disabled{% endif %}>
|
<button type="button" class="btn btn-success" {{ collection.create_btn() }} {% if part_mode and not is_granted('edit', part) %}disabled{% endif %}>
|
||||||
<i class="fas fa-plus-square fa-fw"></i>
|
<i class="fas fa-plus-square fa-fw"></i>
|
||||||
{% trans %}attachment.create{% endtrans %}
|
{% trans %}attachment.create{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -35,15 +35,15 @@
|
||||||
<select class="selectpicker" name="action" data-controller="elements--selectpicker" {{ stimulus_action('elements/datatables/parts', 'updateTargetPicker', 'change') }}
|
<select class="selectpicker" name="action" data-controller="elements--selectpicker" {{ stimulus_action('elements/datatables/parts', 'updateTargetPicker', 'change') }}
|
||||||
title="{% trans %}part_list.action.action.title{% endtrans %}" required>
|
title="{% trans %}part_list.action.action.title{% endtrans %}" required>
|
||||||
<optgroup label="{% trans %}part_list.action.action.group.favorite{% endtrans %}">
|
<optgroup label="{% trans %}part_list.action.action.group.favorite{% endtrans %}">
|
||||||
<option {% if not is_granted('@parts.edit') %}disabled{% endif %} value="favorite">{% trans %}part_list.action.action.favorite{% endtrans %}</option>
|
<option {% if not is_granted('@parts.change_favorite') %}disabled{% endif %} value="favorite">{% trans %}part_list.action.action.favorite{% endtrans %}</option>
|
||||||
<option {% if not is_granted('@parts.edit') %}disabled{% endif %} value="unfavorite">{% trans %}part_list.action.action.unfavorite{% endtrans %}</option>
|
<option {% if not is_granted('@parts.change_favorite') %}disabled{% endif %} value="unfavorite">{% trans %}part_list.action.action.unfavorite{% endtrans %}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
|
|
||||||
<optgroup label="{% trans %}part_list.action.action.group.change_field{% endtrans %}">
|
<optgroup label="{% trans %}part_list.action.action.group.change_field{% endtrans %}">
|
||||||
<option {% if not is_granted('@parts_category.edit') %}disabled{% endif %} value="change_category" data-url="{{ path('select_category') }}">{% trans %}part_list.action.action.change_category{% endtrans %}</option>
|
<option {% if not is_granted('@categories.read') %}disabled{% endif %} value="change_category" data-url="{{ path('select_category') }}">{% trans %}part_list.action.action.change_category{% endtrans %}</option>
|
||||||
<option {% if not is_granted('@parts_footprint.edit') %}disabled{% endif %} value="change_footprint" data-url="{{ path('select_footprint') }}">{% trans %}part_list.action.action.change_footprint{% endtrans %}</option>
|
<option {% if not is_granted('@footprints.read') %}disabled{% endif %} value="change_footprint" data-url="{{ path('select_footprint') }}">{% trans %}part_list.action.action.change_footprint{% endtrans %}</option>
|
||||||
<option {% if not is_granted('@parts_manufacturer.edit') %}disabled{% endif %} value="change_manufacturer" data-url="{{ path('select_manufacturer') }}">{% trans %}part_list.action.action.change_manufacturer{% endtrans %}</option>
|
<option {% if not is_granted('@manufacturers.read') %}disabled{% endif %} value="change_manufacturer" data-url="{{ path('select_manufacturer') }}">{% trans %}part_list.action.action.change_manufacturer{% endtrans %}</option>
|
||||||
<option {% if not is_granted('@parts_unit.edit') %}disabled{% endif %} value="change_unit" data-url="{{ path('select_measurement_unit') }}">{% trans %}part_list.action.action.change_unit{% endtrans %}</option>
|
<option {% if not is_granted('@measurement_units.read') %}disabled{% endif %} value="change_unit" data-url="{{ path('select_measurement_unit') }}">{% trans %}part_list.action.action.change_unit{% endtrans %}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
|
|
||||||
<option {% if not is_granted('@parts.delete') %}disabled{% endif %} value="delete">{% trans %}part_list.action.action.delete{% endtrans %}</option>
|
<option {% if not is_granted('@parts.delete') %}disabled{% endif %} value="delete">{% trans %}part_list.action.action.delete{% endtrans %}</option>
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
{# This is left empty, as this will be filled by Javascript #}
|
{# This is left empty, as this will be filled by Javascript #}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-secondary">{% trans %}part_list.action.submit{% endtrans %}</button>
|
<button type="submit" class="btn btn-secondary" {% if not is_granted('@parts.edit') %}disabled{% endif %}>{% trans %}part_list.action.submit{% endtrans %}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div {{ stimulus_target('elements/datatables/parts', 'dt') }}>
|
<div {{ stimulus_target('elements/datatables/parts', 'dt') }}>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{% macro sidebar_dropdown() %}
|
{% macro sidebar_dropdown() %}
|
||||||
{# Format is [mode, route, label] #}
|
{# Format is [mode, route, label, show_condition] #}
|
||||||
{% set data_sources = [
|
{% set data_sources = [
|
||||||
['categories', path('tree_category_root'), 'category.labelp'],
|
['categories', path('tree_category_root'), 'category.labelp', is_granted('@categories.read') and is_granted('@parts.read')],
|
||||||
['locations', path('tree_location_root'), 'storelocation.labelp'],
|
['locations', path('tree_location_root'), 'storelocation.labelp', is_granted('@storelocations.read') and is_granted('@parts.read')],
|
||||||
['footprints', path('tree_footprint_root'), 'footprint.labelp'],
|
['footprints', path('tree_footprint_root'), 'footprint.labelp', is_granted('@footprints.read') and is_granted('@parts.read')],
|
||||||
['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp'],
|
['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read')],
|
||||||
['suppliers', path('tree_supplier_root'), 'supplier.labelp'],
|
['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read')],
|
||||||
['devices', path('tree_device_root'), 'device.labelp'],
|
['devices', path('tree_device_root'), 'device.labelp', is_granted('@devices.read')],
|
||||||
['tools', path('tree_tools'), 'tools.label'],
|
['tools', path('tree_tools'), 'tools.label', true],
|
||||||
] %}
|
] %}
|
||||||
|
|
||||||
<li class="dropdown-header">{% trans %}actions{% endtrans %}</li>
|
<li class="dropdown-header">{% trans %}actions{% endtrans %}</li>
|
||||||
|
@ -17,9 +17,11 @@
|
||||||
<li class="dropdown-header">{% trans %}datasource{% endtrans %}</li>
|
<li class="dropdown-header">{% trans %}datasource{% endtrans %}</li>
|
||||||
|
|
||||||
{% for source in data_sources %}
|
{% for source in data_sources %}
|
||||||
|
{% if source[3] %} {# show_condition #}
|
||||||
<li><button class="tree-btns dropdown-item" data-mode="{{ source[0] }}" data-url="{{ source[1] }}" data-text="{{ source[2] | trans }}"
|
<li><button class="tree-btns dropdown-item" data-mode="{{ source[0] }}" data-url="{{ source[1] }}" data-text="{{ source[2] | trans }}"
|
||||||
{{ stimulus_action('elements/sidebar_tree', 'changeDataSource') }}
|
{{ stimulus_action('elements/sidebar_tree', 'changeDataSource') }}
|
||||||
>{{ source[2] | trans }}</button></li>
|
>{{ source[2] | trans }}</button></li>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@
|
||||||
<div class="input-group input-group-sm mb-2 mt-1">
|
<div class="input-group input-group-sm mb-2 mt-1">
|
||||||
<button class="btn btn-light dropdown-toggle" type="button"
|
<button class="btn btn-light dropdown-toggle" type="button"
|
||||||
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
||||||
><span class="sidebar-title" {{ stimulus_target('elements/sidebar_tree', 'sourceText') }}>Loading...</span></button>
|
><span class="sidebar-title" {{ stimulus_target('elements/sidebar_tree', 'sourceText') }}>Loading... / Access Denied</span></button>
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownCat">
|
<ul class="dropdown-menu" aria-labelledby="dropdownCat">
|
||||||
{{ _self.sidebar_dropdown('tree-categories') }}
|
{{ _self.sidebar_dropdown('tree-categories') }}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -6,6 +6,30 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro array_to_tags(tags, class="badge bg-primary") %}
|
||||||
|
{% for tag in tags %}
|
||||||
|
<span class="{{ class }}">{{ tag | trim }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro bool_icon(bool) %}
|
||||||
|
{% if bool %}
|
||||||
|
<i class="fas fa-check-circle fa-fw" title="{% trans %}Yes{% endtrans %}"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fas fa-times-circle fa-fw" title="{% trans %}No{% endtrans %}"></i>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro boolean_badge(value, class="badge") %}
|
||||||
|
{% if value %}
|
||||||
|
{% set class = class ~ ' bg-success' %}
|
||||||
|
{% else %}
|
||||||
|
{% set class = class ~ ' bg-danger' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span class="{{ class }}">{{ _self.bool_icon(value) }} {{ _self.boolean(value) }}</span>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro string_to_tags(string, class="badge bg-info") %}
|
{% macro string_to_tags(string, class="badge bg-info") %}
|
||||||
{% for tag in string|split(',') %}
|
{% for tag in string|split(',') %}
|
||||||
<a href="{{ path('part_list_tags', {'tag': tag | trim | url_encode}) }}" class="{{ class }}" >{{ tag | trim }}</a>
|
<a href="{{ path('part_list_tags', {'tag': tag | trim | url_encode}) }}" class="{{ class }}" >{{ tag | trim }}</a>
|
||||||
|
@ -106,21 +130,13 @@
|
||||||
</nav>
|
</nav>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro bool_icon(bool) %}
|
|
||||||
{% if bool %}
|
|
||||||
<i class="fas fa-check-circle fa-fw" title="{% trans %}Yes{% endtrans %}"></i>
|
|
||||||
{% else %}
|
|
||||||
<i class="fas fa-times-circle fa-fw" title="{% trans %}No{% endtrans %}"></i>
|
|
||||||
{% endif %}
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro date_user_combination(entity, lastModified, datetime_format = "short") %}
|
{% macro date_user_combination(entity, lastModified, datetime_format = "short") %}
|
||||||
{% if lastModified == true %}
|
{% if lastModified == true %}
|
||||||
{{ entity.lastModified | format_datetime(datetime_format) }}
|
{{ entity.lastModified | format_datetime(datetime_format) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ entity.addedDate | format_datetime(datetime_format) }}
|
{{ entity.addedDate | format_datetime(datetime_format) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_granted('show_users', entity) %}
|
{% if is_granted('show_history', entity) %}
|
||||||
{% if lastModified == true %}
|
{% if lastModified == true %}
|
||||||
{% set user = last_editing_user(entity) %}
|
{% set user = last_editing_user(entity) %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -129,9 +145,9 @@
|
||||||
|
|
||||||
{% if user is not null %}
|
{% if user is not null %}
|
||||||
{% if user.fullName is not empty %}
|
{% if user.fullName is not empty %}
|
||||||
<a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}"><i>({{ user.fullName }})</i></a>
|
(<a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}">{{ user.fullName }}</a>)
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}"><i>(@{{ user.name }})</i></a>
|
(<a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}">@{{ user.name }}</a>)
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -71,13 +71,13 @@ class ApplicationAvailabilityFunctionalTest extends WebTestCase
|
||||||
|
|
||||||
$client->request('GET', $url);
|
$client->request('GET', $url);
|
||||||
|
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful(), 'Request not successful. Status code is '.$client->getResponse()->getStatusCode());
|
$this->assertTrue($client->getResponse()->isSuccessful(), 'Request not successful. Status code is '.$client->getResponse()->getStatusCode() . ' for URL '.$url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function urlProvider(): ?Generator
|
public function urlProvider(): ?Generator
|
||||||
{
|
{
|
||||||
//Homepage
|
//Homepage
|
||||||
//yield ['/'];
|
yield ['/'];
|
||||||
//User related things
|
//User related things
|
||||||
yield ['/user/settings'];
|
yield ['/user/settings'];
|
||||||
yield ['/user/info'];
|
yield ['/user/info'];
|
||||||
|
@ -117,9 +117,21 @@ class ApplicationAvailabilityFunctionalTest extends WebTestCase
|
||||||
//Statistics
|
//Statistics
|
||||||
yield ['/statistics'];
|
yield ['/statistics'];
|
||||||
|
|
||||||
|
//Event log
|
||||||
|
yield ['/log/']; //Slash suffix here is important
|
||||||
|
|
||||||
|
|
||||||
//Typeahead
|
//Typeahead
|
||||||
yield ['/typeahead/builtInResources/search?query=DIP8'];
|
yield ['/typeahead/builtInResources/search?query=DIP8'];
|
||||||
yield ['/typeahead/tags/search/test'];
|
yield ['/typeahead/tags/search/test'];
|
||||||
|
yield ['/typeahead/parameters/part/search/NPN'];
|
||||||
|
yield ['/typeahead/parameters/category/search/NPN'];
|
||||||
|
|
||||||
|
//Select API
|
||||||
|
yield ['/select_api/category'];
|
||||||
|
yield ['/select_api/footprint'];
|
||||||
|
yield ['/select_api/manufacturer'];
|
||||||
|
yield ['/select_api/measurement_unit'];
|
||||||
|
|
||||||
//Label test
|
//Label test
|
||||||
yield ['/scan'];
|
yield ['/scan'];
|
||||||
|
@ -130,5 +142,9 @@ class ApplicationAvailabilityFunctionalTest extends WebTestCase
|
||||||
|
|
||||||
//Tools
|
//Tools
|
||||||
yield ['/tools/reel_calc'];
|
yield ['/tools/reel_calc'];
|
||||||
|
yield ['/tools/server_infos'];
|
||||||
|
|
||||||
|
//Webauthn Register
|
||||||
|
yield ['/webauthn/register'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class DatatablesAvailabilityTest extends WebTestCase
|
||||||
]);
|
]);
|
||||||
$client->catchExceptions(false);
|
$client->catchExceptions(false);
|
||||||
$client->request('GET', $url);
|
$client->request('GET', $url);
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful(), 'Request not successful. Status code is '.$client->getResponse()->getStatusCode());
|
$this->assertTrue($client->getResponse()->isSuccessful(), 'Request not successful. Status code is '.$client->getResponse()->getStatusCode() . ' for URL '.$url);
|
||||||
|
|
||||||
static::ensureKernelShutdown();
|
static::ensureKernelShutdown();
|
||||||
$client = static::createClient([], [
|
$client = static::createClient([], [
|
||||||
|
@ -70,5 +70,9 @@ class DatatablesAvailabilityTest extends WebTestCase
|
||||||
yield ['/log/'];
|
yield ['/log/'];
|
||||||
|
|
||||||
yield ['/attachment/list'];
|
yield ['/attachment/list'];
|
||||||
|
|
||||||
|
//Test using filters
|
||||||
|
yield ['/category/1/parts?part_filter%5Bname%5D%5Boperator%5D=%3D&part_filter%5Bname%5D%5Bvalue%5D=BC547&part_filter%5Bcategory%5D%5Boperator%5D=INCLUDING_CHILDREN&part_filter%5Btags%5D%5Boperator%5D=ANY&part_filter%5Btags%5D%5Bvalue%5D=Test&part_filter%5Bsubmit%5D='];
|
||||||
|
yield ['/category/1/parts?part_filter%5Bcategory%5D%5Boperator%5D=INCLUDING_CHILDREN&part_filter%5Bstorelocation%5D%5Boperator%5D=%3D&part_filter%5Bstorelocation%5D%5Bvalue%5D=1&part_filter%5BattachmentsCount%5D%5Boperator%5D=%3D&part_filter%5BattachmentsCount%5D%5Bvalue1%5D=3&part_filter%5Bsubmit%5D='];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
131
tests/Entity/UserSystem/PermissionDataTest.php
Normal file
131
tests/Entity/UserSystem/PermissionDataTest.php
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Entity\UserSystem;
|
||||||
|
|
||||||
|
use App\Entity\UserSystem\PermissionData;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PermissionDataTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testGetSetIs()
|
||||||
|
{
|
||||||
|
$perm_data = new PermissionData();
|
||||||
|
|
||||||
|
//Empty object should have all permissions set to inherit
|
||||||
|
$this->assertNull($perm_data->getPermissionValue('not_existing', 'not_existing'));
|
||||||
|
$this->assertFalse($perm_data->isPermissionSet('not_existing', 'not_existing'));
|
||||||
|
|
||||||
|
$this->assertNull($perm_data->getPermissionValue('p1', 'op1'));
|
||||||
|
$this->assertNull($perm_data->getPermissionValue('p1', 'op2'));
|
||||||
|
$this->assertNull($perm_data->getPermissionValue('p2', 'op1'));
|
||||||
|
|
||||||
|
//Set values
|
||||||
|
$perm_data->setPermissionValue('p1', 'op1', PermissionData::ALLOW);
|
||||||
|
$perm_data->setPermissionValue('p1', 'op2', PermissionData::DISALLOW);
|
||||||
|
$perm_data->setPermissionValue('p2', 'op1', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
//Check that values were set
|
||||||
|
$this->assertTrue($perm_data->isPermissionSet('p1', 'op1'));
|
||||||
|
$this->assertTrue($perm_data->isPermissionSet('p1', 'op2'));
|
||||||
|
$this->assertTrue($perm_data->isPermissionSet('p2', 'op1'));
|
||||||
|
|
||||||
|
//Check that values are correct
|
||||||
|
$this->assertTrue($perm_data->getPermissionValue('p1', 'op1'));
|
||||||
|
$this->assertFalse($perm_data->getPermissionValue('p1', 'op2'));
|
||||||
|
$this->assertTrue($perm_data->getPermissionValue('p2', 'op1'));
|
||||||
|
|
||||||
|
//Set values to null
|
||||||
|
$perm_data->setPermissionValue('p1', 'op1', null);
|
||||||
|
$this->assertNull($perm_data->getPermissionValue('p1', 'op1'));
|
||||||
|
//Values should be unset now
|
||||||
|
$this->assertFalse($perm_data->isPermissionSet('p1', 'op1'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testJSONSerialization()
|
||||||
|
{
|
||||||
|
$perm_data = new PermissionData();
|
||||||
|
|
||||||
|
$perm_data->setPermissionValue('perm1', 'op1', PermissionData::ALLOW);
|
||||||
|
$perm_data->setPermissionValue('perm1', 'op2', PermissionData::DISALLOW);
|
||||||
|
$perm_data->setPermissionValue('perm1', 'op3', PermissionData::ALLOW);
|
||||||
|
|
||||||
|
$perm_data->setPermissionValue('perm2', 'op1', PermissionData::ALLOW);
|
||||||
|
$perm_data->setPermissionValue('perm2', 'op2', PermissionData::DISALLOW);
|
||||||
|
|
||||||
|
//Ensure that JSON serialization works
|
||||||
|
$this->assertJsonStringEqualsJsonString(json_encode([
|
||||||
|
'perm1' => [
|
||||||
|
'op1' => true,
|
||||||
|
'op2' => false,
|
||||||
|
'op3' => true,
|
||||||
|
],
|
||||||
|
'perm2' => [
|
||||||
|
'op1' => true,
|
||||||
|
'op2' => false,
|
||||||
|
],
|
||||||
|
], JSON_THROW_ON_ERROR), json_encode($perm_data, JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
//Set values to inherit to ensure they do not show up in the json
|
||||||
|
$perm_data->setPermissionValue('perm1', 'op3', null);
|
||||||
|
$perm_data->setPermissionValue('perm2', 'op1', null);
|
||||||
|
$perm_data->setPermissionValue('perm2', 'op2', null);
|
||||||
|
|
||||||
|
//Ensure that JSON serialization works
|
||||||
|
$this->assertJsonStringEqualsJsonString(json_encode([
|
||||||
|
'perm1' => [
|
||||||
|
'op1' => true,
|
||||||
|
'op2' => false,
|
||||||
|
],
|
||||||
|
], JSON_THROW_ON_ERROR), json_encode($perm_data, JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFromJSON()
|
||||||
|
{
|
||||||
|
$json = json_encode([
|
||||||
|
'perm1' => [
|
||||||
|
'op1' => true,
|
||||||
|
'op2' => false,
|
||||||
|
'op3' => true,
|
||||||
|
],
|
||||||
|
'perm2' => [
|
||||||
|
'op1' => true,
|
||||||
|
'op2' => false,
|
||||||
|
],
|
||||||
|
], JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
$perm_data = PermissionData::fromJSON($json);
|
||||||
|
|
||||||
|
//Ensure that values were set correctly
|
||||||
|
$this->assertTrue($perm_data->getPermissionValue('perm1', 'op1'));
|
||||||
|
$this->assertFalse($perm_data->getPermissionValue('perm2', 'op2'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResetPermissions()
|
||||||
|
{
|
||||||
|
$data = new PermissionData();
|
||||||
|
|
||||||
|
$data->setPermissionValue('perm1', 'op1', PermissionData::ALLOW);
|
||||||
|
$data->setPermissionValue('perm1', 'op2', PermissionData::DISALLOW);
|
||||||
|
$data->setPermissionValue('perm1', 'op3', PermissionData::INHERIT);
|
||||||
|
|
||||||
|
//Ensure that values were set correctly
|
||||||
|
$this->assertTrue($data->isPermissionSet('perm1', 'op1'));
|
||||||
|
$this->assertTrue($data->isPermissionSet('perm1', 'op2'));
|
||||||
|
$this->assertFalse($data->isPermissionSet('perm1', 'op3'));
|
||||||
|
|
||||||
|
//Reset the permissions
|
||||||
|
$data->resetPermissions();
|
||||||
|
|
||||||
|
//Afterwards all values must be set to inherit (null)
|
||||||
|
$this->assertNull($data->getPermissionValue('perm1', 'op1'));
|
||||||
|
$this->assertNull($data->getPermissionValue('perm1', 'op2'));
|
||||||
|
$this->assertNull($data->getPermissionValue('perm1', 'op3'));
|
||||||
|
|
||||||
|
//And be undefined
|
||||||
|
$this->assertFalse($data->isPermissionSet('perm1', 'op1'));
|
||||||
|
$this->assertFalse($data->isPermissionSet('perm1', 'op2'));
|
||||||
|
$this->assertFalse($data->isPermissionSet('perm1', 'op3'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,211 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Tests\Entity\UserSystem;
|
|
||||||
|
|
||||||
use App\Entity\UserSystem\PermissionsEmbed;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
use ReflectionClass;
|
|
||||||
|
|
||||||
class PermissionsEmbedTest extends TestCase
|
|
||||||
{
|
|
||||||
public function testGetPermissionValue(): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
//For newly created embedded, all things should be set to inherit => null
|
|
||||||
//Test both normal name and constants
|
|
||||||
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::CONFIG, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::ATTACHMENT_TYPES, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::CATEGORIES, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::DATABASE, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::DEVICE_PARTS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::DEVICES, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::FOOTRPINTS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::GROUPS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::DATABASE, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::LABELS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::MANUFACTURERS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_ATTACHMENTS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_COMMENT, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_DESCRIPTION, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_FOOTPRINT, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_MANUFACTURER, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_MINAMOUNT, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_NAME, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_ORDER, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS_ORDERDETAILS, 0));
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::USERS, 0));
|
|
||||||
|
|
||||||
//Set a value for testing to the part property
|
|
||||||
$reflection = new ReflectionClass($embed);
|
|
||||||
$property = $reflection->getProperty('parts');
|
|
||||||
$property->setAccessible(true);
|
|
||||||
|
|
||||||
$property->setValue($embed, 0b11011000); // 11 01 10 00
|
|
||||||
|
|
||||||
//Test if function is working correctly
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS, 0));
|
|
||||||
$this->assertFalse($embed->getPermissionValue(PermissionsEmbed::PARTS, 2));
|
|
||||||
$this->assertTrue($embed->getPermissionValue(PermissionsEmbed::PARTS, 4));
|
|
||||||
// 11 is reserved, but it should be also treat as INHERIT.
|
|
||||||
$this->assertNull($embed->getPermissionValue(PermissionsEmbed::PARTS, 6));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetBitValue(): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
|
|
||||||
//Set a value for testing to the part property
|
|
||||||
$reflection = new ReflectionClass($embed);
|
|
||||||
$property = $reflection->getProperty('parts');
|
|
||||||
$property->setAccessible(true);
|
|
||||||
|
|
||||||
$property->setValue($embed, 0b11011000); // 11 01 10 00
|
|
||||||
|
|
||||||
//Test if function is working correctly
|
|
||||||
$this->assertSame(PermissionsEmbed::INHERIT, $embed->getBitValue(PermissionsEmbed::PARTS, 0));
|
|
||||||
$this->assertSame(PermissionsEmbed::DISALLOW, $embed->getBitValue(PermissionsEmbed::PARTS, 2));
|
|
||||||
$this->assertSame(PermissionsEmbed::ALLOW, $embed->getBitValue(PermissionsEmbed::PARTS, 4));
|
|
||||||
// 11 is reserved, but it should be also treat as INHERIT.
|
|
||||||
$this->assertSame(0b11, $embed->getBitValue(PermissionsEmbed::PARTS, 6));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInvalidPermissionName(): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
//When encoutering an unknown permission name the class must throw an exception
|
|
||||||
$this->expectException(InvalidArgumentException::class);
|
|
||||||
$embed->getPermissionValue('invalid', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInvalidBit1(): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
//When encoutering an negative bit the class must throw an exception
|
|
||||||
$this->expectException(InvalidArgumentException::class);
|
|
||||||
$embed->getPermissionValue('parts', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInvalidBit2(): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
//When encoutering an odd bit number it must throw an error.
|
|
||||||
$this->expectException(InvalidArgumentException::class);
|
|
||||||
$embed->getPermissionValue('parts', 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStatesBINARY(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'ALLOW' => [PermissionsEmbed::ALLOW],
|
|
||||||
'DISALLOW' => [PermissionsEmbed::DISALLOW],
|
|
||||||
'INHERIT' => [PermissionsEmbed::INHERIT],
|
|
||||||
'0b11' => [0b11],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStatesBOOL(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'ALLOW' => [true],
|
|
||||||
'DISALLOW' => [false],
|
|
||||||
'INHERIT' => [null],
|
|
||||||
'0b11' => [null],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider getStatesBINARY
|
|
||||||
*/
|
|
||||||
public function testTestsetBitValue($value): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
//Check if it returns itself, for chaining.
|
|
||||||
$this->assertSame($embed, $embed->setBitValue(PermissionsEmbed::PARTS, 0, $value));
|
|
||||||
$this->assertSame($value, $embed->getBitValue(PermissionsEmbed::PARTS, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider getStatesBOOL
|
|
||||||
*/
|
|
||||||
public function testSetPermissionValue($value): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
//Check if it returns itself, for chaining.
|
|
||||||
$this->assertSame($embed, $embed->setPermissionValue(PermissionsEmbed::PARTS, 0, $value));
|
|
||||||
$this->assertSame($value, $embed->getPermissionValue(PermissionsEmbed::PARTS, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetRawPermissionValue(): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
$embed->setRawPermissionValue(PermissionsEmbed::PARTS, 10);
|
|
||||||
$this->assertSame(10, $embed->getRawPermissionValue(PermissionsEmbed::PARTS));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetRawPermissionValues(): void
|
|
||||||
{
|
|
||||||
$embed = new PermissionsEmbed();
|
|
||||||
$embed->setRawPermissionValues([
|
|
||||||
PermissionsEmbed::PARTS => 0,
|
|
||||||
PermissionsEmbed::USERS => 100,
|
|
||||||
PermissionsEmbed::CATEGORIES => 1304,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertSame(0, $embed->getRawPermissionValue(PermissionsEmbed::PARTS));
|
|
||||||
$this->assertSame(100, $embed->getRawPermissionValue(PermissionsEmbed::USERS));
|
|
||||||
$this->assertSame(1304, $embed->getRawPermissionValue(PermissionsEmbed::CATEGORIES));
|
|
||||||
|
|
||||||
//Test second method to pass perm names and values
|
|
||||||
$embed->setRawPermissionValues(
|
|
||||||
[PermissionsEmbed::PARTS, PermissionsEmbed::USERS, PermissionsEmbed::CATEGORIES],
|
|
||||||
[0, 100, 1304]
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertSame(0, $embed->getRawPermissionValue(PermissionsEmbed::PARTS));
|
|
||||||
$this->assertSame(100, $embed->getRawPermissionValue(PermissionsEmbed::USERS));
|
|
||||||
$this->assertSame(1304, $embed->getRawPermissionValue(PermissionsEmbed::CATEGORIES));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Tests\Security\Annotations;
|
|
||||||
|
|
||||||
use App\Entity\Attachments\AttachmentType;
|
|
||||||
use App\Security\Annotations\ColumnSecurity;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class ColumnSecurityTest extends TestCase
|
|
||||||
{
|
|
||||||
public function testGetReadOperation(): void
|
|
||||||
{
|
|
||||||
$annotation = new ColumnSecurity();
|
|
||||||
$this->assertSame('read', $annotation->getReadOperationName(), 'A new annotation must return read');
|
|
||||||
$annotation->read = 'overwritten';
|
|
||||||
$this->assertSame('overwritten', $annotation->getReadOperationName());
|
|
||||||
$annotation->prefix = 'prefix';
|
|
||||||
$this->assertSame('prefix.overwritten', $annotation->getReadOperationName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetEditOperation(): void
|
|
||||||
{
|
|
||||||
$annotation = new ColumnSecurity();
|
|
||||||
$this->assertSame('edit', $annotation->getEditOperationName(), 'A new annotation must return read');
|
|
||||||
$annotation->edit = 'overwritten';
|
|
||||||
$this->assertSame('overwritten', $annotation->getEditOperationName());
|
|
||||||
$annotation->prefix = 'prefix';
|
|
||||||
$this->assertSame('prefix.overwritten', $annotation->getEditOperationName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function placeholderScalarDataProvider(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['string', '???'],
|
|
||||||
['integer', 0],
|
|
||||||
['int', 0],
|
|
||||||
['float', 0.0],
|
|
||||||
['object', null],
|
|
||||||
['bool', false],
|
|
||||||
['boolean', false],
|
|
||||||
//['datetime', (new \DateTime())->setTimestamp(0)]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider placeholderScalarDataProvider
|
|
||||||
*
|
|
||||||
* @param $expected_value
|
|
||||||
*/
|
|
||||||
public function testGetPlaceholderScalar(string $type, $expected_value): void
|
|
||||||
{
|
|
||||||
$annotation = new ColumnSecurity();
|
|
||||||
$annotation->type = $type;
|
|
||||||
$this->assertSame($expected_value, $annotation->getPlaceholder());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetPlaceholderSpecifiedValue(): void
|
|
||||||
{
|
|
||||||
$annotation = new ColumnSecurity();
|
|
||||||
$annotation->placeholder = 3434;
|
|
||||||
$this->assertSame(3434, $annotation->getPlaceholder());
|
|
||||||
|
|
||||||
$annotation->placeholder = [323];
|
|
||||||
$this->assertCount(1, $annotation->getPlaceholder());
|
|
||||||
|
|
||||||
//If a placeholder is specified we allow every type
|
|
||||||
$annotation->type = 'type2';
|
|
||||||
$annotation->placeholder = 'invalid';
|
|
||||||
$this->assertSame('invalid', $annotation->getPlaceholder());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetPlaceholderDBElement(): void
|
|
||||||
{
|
|
||||||
$annotation = new ColumnSecurity();
|
|
||||||
$annotation->type = AttachmentType::class;
|
|
||||||
|
|
||||||
/** @var AttachmentType $placeholder */
|
|
||||||
$placeholder = $annotation->getPlaceholder();
|
|
||||||
$this->assertInstanceOf(AttachmentType::class, $placeholder);
|
|
||||||
$this->assertSame('???', $placeholder->getName());
|
|
||||||
|
|
||||||
$annotation->placeholder = 'test';
|
|
||||||
$placeholder = $annotation->getPlaceholder();
|
|
||||||
$this->assertInstanceOf(AttachmentType::class, $placeholder);
|
|
||||||
$this->assertSame('test', $placeholder->getName());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -45,15 +45,18 @@ namespace App\Tests\Security;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Security\UserChecker;
|
use App\Security\UserChecker;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException;
|
||||||
use Symfony\Component\Security\Core\Exception\DisabledException;
|
use Symfony\Component\Security\Core\Exception\DisabledException;
|
||||||
|
|
||||||
class UserCheckerTest extends TestCase
|
class UserCheckerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
protected $service;
|
protected $service;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->service = new UserChecker();
|
self::bootKernel();
|
||||||
|
$this->service = self::getContainer()->get(UserChecker::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowDisabledException(): void
|
public function testThrowDisabledException(): void
|
||||||
|
@ -66,7 +69,7 @@ class UserCheckerTest extends TestCase
|
||||||
|
|
||||||
//An disabled user must throw an exception
|
//An disabled user must throw an exception
|
||||||
$user->setDisabled(true);
|
$user->setDisabled(true);
|
||||||
$this->expectException(DisabledException::class);
|
$this->expectException(CustomUserMessageAccountStatusException::class);
|
||||||
$this->service->checkPostAuth($user);
|
$this->service->checkPostAuth($user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,23 +40,25 @@ declare(strict_types=1);
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Tests\Services;
|
namespace App\Tests\Services\UserSystem;
|
||||||
|
|
||||||
use App\Entity\UserSystem\Group;
|
use App\Entity\UserSystem\Group;
|
||||||
|
use App\Entity\UserSystem\PermissionData;
|
||||||
use App\Entity\UserSystem\PermissionsEmbed;
|
use App\Entity\UserSystem\PermissionsEmbed;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\PermissionResolver;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class PermissionResolverTest extends WebTestCase
|
class PermissionManagerTest extends WebTestCase
|
||||||
{
|
{
|
||||||
protected $user_withoutGroup;
|
protected $user_withoutGroup;
|
||||||
|
|
||||||
protected $user;
|
protected $user;
|
||||||
protected $group;
|
protected $group;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var PermissionResolver
|
* @var PermissionManager
|
||||||
*/
|
*/
|
||||||
protected $service;
|
protected $service;
|
||||||
|
|
||||||
|
@ -66,75 +68,58 @@ class PermissionResolverTest extends WebTestCase
|
||||||
|
|
||||||
//Get an service instance.
|
//Get an service instance.
|
||||||
self::bootKernel();
|
self::bootKernel();
|
||||||
$this->service = self::$container->get(PermissionResolver::class);
|
$this->service = self::getContainer()->get(PermissionManager::class);
|
||||||
|
|
||||||
//Set up a mocked user
|
//Set up a mocked user
|
||||||
$user_embed = new PermissionsEmbed();
|
$user_perms = new PermissionData();
|
||||||
$user_embed->setPermissionValue('parts', 0, true) //read
|
$user_perms->setPermissionValue('parts', 'read', true) //read
|
||||||
->setPermissionValue('parts', 2, false) //edit
|
->setPermissionValue('parts', 'edit', false) //edit
|
||||||
->setPermissionValue('parts', 4, null) //create
|
->setPermissionValue('parts', 'create', null) //create
|
||||||
->setPermissionValue('parts', 30, null) //move
|
->setPermissionValue('parts', 'move', null) //move
|
||||||
->setPermissionValue('parts', 8, null); //delete
|
->setPermissionValue('parts', 'delete', null); //delete
|
||||||
|
|
||||||
$this->user = $this->createMock(User::class);
|
$this->user = $this->createMock(User::class);
|
||||||
$this->user->method('getPermissions')->willReturn($user_embed);
|
$this->user->method('getPermissions')->willReturn($user_perms);
|
||||||
|
|
||||||
$this->user_withoutGroup = $this->createMock(User::class);
|
$this->user_withoutGroup = $this->createMock(User::class);
|
||||||
$this->user_withoutGroup->method('getPermissions')->willReturn($user_embed);
|
$this->user_withoutGroup->method('getPermissions')->willReturn($user_perms);
|
||||||
$this->user_withoutGroup->method('getGroup')->willReturn(null);
|
$this->user_withoutGroup->method('getGroup')->willReturn(null);
|
||||||
|
|
||||||
//Set up a faked group
|
//Set up a faked group
|
||||||
$group1_embed = new PermissionsEmbed();
|
$group1_perms = new PermissionData();
|
||||||
$group1_embed->setPermissionValue('parts', 6, true)
|
$group1_perms
|
||||||
->setPermissionValue('parts', 8, false)
|
->setPermissionValue('parts', 'delete', false)
|
||||||
->setPermissionValue('parts', 10, null)
|
->setPermissionValue('parts', 'search', null)
|
||||||
->setPermissionValue('parts', 0, false)
|
->setPermissionValue('parts', 'read', false)
|
||||||
->setPermissionValue('parts', 30, true)
|
->setPermissionValue('parts', 'show_history', true)
|
||||||
->setPermissionValue('parts', 2, true);
|
->setPermissionValue('parts', 'edit', true);
|
||||||
|
|
||||||
$this->group = $this->createMock(Group::class);
|
$this->group = $this->createMock(Group::class);
|
||||||
$this->group->method('getPermissions')->willReturn($group1_embed);
|
$this->group->method('getPermissions')->willReturn($group1_perms);
|
||||||
|
|
||||||
//Set this group for the user
|
//Set this group for the user
|
||||||
$this->user->method('getGroup')->willReturn($this->group);
|
$this->user->method('getGroup')->willReturn($this->group);
|
||||||
|
|
||||||
//parent group
|
//parent group
|
||||||
$parent_group_embed = new PermissionsEmbed();
|
$parent_group_perms = new PermissionData();
|
||||||
$parent_group_embed->setPermissionValue('parts', 12, true)
|
$parent_group_perms->setPermissionValue('parts', 'all_parts', true)
|
||||||
->setPermissionValue('parts', 14, false)
|
->setPermissionValue('parts', 'no_price_parts', false)
|
||||||
->setPermissionValue('parts', 16, null);
|
->setPermissionValue('parts', 'obsolete_parts', null);
|
||||||
$parent_group = $this->createMock(Group::class);
|
$parent_group = $this->createMock(Group::class);
|
||||||
$parent_group->method('getPermissions')->willReturn($parent_group_embed);
|
$parent_group->method('getPermissions')->willReturn($parent_group_perms);
|
||||||
|
|
||||||
$this->group->method('getParent')->willReturn($parent_group);
|
$this->group->method('getParent')->willReturn($parent_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPermissionNames(): array
|
public function getPermissionNames(): array
|
||||||
{
|
{
|
||||||
//List all possible operation names.
|
//List some permission names
|
||||||
return [
|
return [
|
||||||
[PermissionsEmbed::PARTS],
|
['parts'],
|
||||||
[PermissionsEmbed::USERS],
|
['system'],
|
||||||
[PermissionsEmbed::PARTS_ORDERDETAILS],
|
['footprints'],
|
||||||
[PermissionsEmbed::PARTS_NAME],
|
['suppliers'],
|
||||||
[PermissionsEmbed::PARTS_ORDER],
|
['tools']
|
||||||
[PermissionsEmbed::PARTS_MINAMOUNT],
|
|
||||||
[PermissionsEmbed::PARTS_MANUFACTURER],
|
|
||||||
[PermissionsEmbed::DEVICES],
|
|
||||||
[PermissionsEmbed::PARTS_FOOTPRINT],
|
|
||||||
[PermissionsEmbed::PARTS_DESCRIPTION],
|
|
||||||
[PermissionsEmbed::PARTS_COMMENT],
|
|
||||||
[PermissionsEmbed::PARTS_ATTACHMENTS],
|
|
||||||
[PermissionsEmbed::MANUFACTURERS],
|
|
||||||
[PermissionsEmbed::LABELS],
|
|
||||||
[PermissionsEmbed::DATABASE],
|
|
||||||
[PermissionsEmbed::GROUPS],
|
|
||||||
[PermissionsEmbed::FOOTRPINTS],
|
|
||||||
[PermissionsEmbed::DEVICE_PARTS],
|
|
||||||
[PermissionsEmbed::CATEGORIES],
|
|
||||||
[PermissionsEmbed::PARTS_PRICES],
|
|
||||||
[PermissionsEmbed::ATTACHMENT_TYPES],
|
|
||||||
[PermissionsEmbed::CONFIG],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,4 +199,96 @@ class PermissionResolverTest extends WebTestCase
|
||||||
$this->assertNull($this->service->inherit($this->user_withoutGroup, 'parts', 'show_history'));
|
$this->assertNull($this->service->inherit($this->user_withoutGroup, 'parts', 'show_history'));
|
||||||
$this->assertNull($this->service->inherit($this->user_withoutGroup, 'parts', 'delete'));
|
$this->assertNull($this->service->inherit($this->user_withoutGroup, 'parts', 'delete'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetPermission(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
|
||||||
|
//Set permission to true
|
||||||
|
$this->service->setPermission($user, 'parts', 'read', true);
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertTrue($this->service->inherit($user, 'parts', 'read'));
|
||||||
|
|
||||||
|
//Set permission to false
|
||||||
|
$this->service->setPermission($user, 'parts', 'read', false);
|
||||||
|
$this->assertFalse($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertFalse($this->service->inherit($user, 'parts', 'read'));
|
||||||
|
|
||||||
|
//Set permission to null
|
||||||
|
$this->service->setPermission($user, 'parts', 'read', null);
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertNull($this->service->inherit($user, 'parts', 'read'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetAllPermissions(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
|
||||||
|
//Set all permissions to true
|
||||||
|
$this->service->setAllPermissions($user, true);
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'categories', 'edit'));
|
||||||
|
|
||||||
|
//Set all permissions to false
|
||||||
|
$this->service->setAllPermissions($user, false);
|
||||||
|
$this->assertFalse($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertFalse($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertFalse($this->service->dontInherit($user, 'categories', 'edit'));
|
||||||
|
|
||||||
|
//Set all permissions to null
|
||||||
|
$this->service->setAllPermissions($user, null);
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'categories', 'edit'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetAllOperationsOfPermission(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
|
||||||
|
//Set all operations of permission to true
|
||||||
|
$this->service->setAllOperationsOfPermission($user, 'parts', true);
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'edit'));
|
||||||
|
|
||||||
|
//Set all operations of permission to false
|
||||||
|
$this->service->setAllOperationsOfPermission($user, 'parts', false);
|
||||||
|
$this->assertFalse($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertFalse($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertFalse($this->service->dontInherit($user, 'parts', 'edit'));
|
||||||
|
|
||||||
|
//Set all operations of permission to null
|
||||||
|
$this->service->setAllOperationsOfPermission($user, 'parts', null);
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'parts', 'edit'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEnsureCorrectSetOperations(): void
|
||||||
|
{
|
||||||
|
//Create an empty user (all permissions are inherit)
|
||||||
|
$user = new User();
|
||||||
|
|
||||||
|
//ensure that all permissions are inherit
|
||||||
|
$this->assertNull($this->service->inherit($user, 'parts', 'read'));
|
||||||
|
$this->assertNull($this->service->inherit($user, 'parts', 'edit'));
|
||||||
|
$this->assertNull($this->service->inherit($user, 'categories', 'read'));
|
||||||
|
|
||||||
|
//Set some permissions
|
||||||
|
$this->service->setPermission($user, 'parts', 'create', true);
|
||||||
|
//Until now only the create permission should be set
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertNull($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
|
||||||
|
//Now we call the ensureCorrectSetOperations method
|
||||||
|
$this->service->ensureCorrectSetOperations($user);
|
||||||
|
|
||||||
|
//Now all permissions should be set
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'create'));
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'read'));
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'parts', 'edit'));
|
||||||
|
$this->assertTrue($this->service->dontInherit($user, 'categories', 'read'));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -40,9 +40,9 @@ declare(strict_types=1);
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Tests\Services\TFA;
|
namespace App\Tests\Services\UserSystem\TFA;
|
||||||
|
|
||||||
use App\Services\TFA\BackupCodeGenerator;
|
use App\Services\UserSystem\TFA\BackupCodeGenerator;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
|
@ -40,10 +40,10 @@ declare(strict_types=1);
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Tests\Services\TFA;
|
namespace App\Tests\Services\UserSystem\TFA;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\TFA\BackupCodeManager;
|
use App\Services\UserSystem\TFA\BackupCodeManager;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class BackupCodeManagerTest extends WebTestCase
|
class BackupCodeManagerTest extends WebTestCase
|
File diff suppressed because it is too large
Load diff
11
translations/security.en.xlf
Normal file
11
translations/security.en.xlf
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en" trgLang="en">
|
||||||
|
<file id="security.en">
|
||||||
|
<unit id="aazoCks" name="user.login_error.user_disabled">
|
||||||
|
<segment>
|
||||||
|
<source>user.login_error.user_disabled</source>
|
||||||
|
<target>Your account is disabled! Contact an administrator if you think this is wrong.</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
</file>
|
||||||
|
</xliff>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue