diff --git a/.commitlintrc.json b/.commitlintrc.json index eb7767f7ff..c2b9b5d78c 100644 --- a/.commitlintrc.json +++ b/.commitlintrc.json @@ -19,7 +19,8 @@ "deps", "deps-dev" ] - ] + ], + "body-max-line-length": [2, "always", 200] }, "prompt": { "questions": { diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 6dff45294c..1a697d1329 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -6,6 +6,9 @@ on: jobs: run-commitlint-on-pr: runs-on: ubuntu-latest + if: | + startsWith(github.head_ref, 'dependabot/npm_and_yarn/') != true && + startsWith(github.head_ref, 'renovate/') != true steps: - uses: actions/checkout@v3 with: diff --git a/.releaserc.json b/.releaserc.json index 0074a3daa2..24e63763b9 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -89,6 +89,13 @@ "prepareCmd": "./update-versions.sh ${nextRelease.version} && mvn -B clean install" } ], + [ + "@semantic-release/changelog", + { + "changelogFile": "UPGRADE.md", + "changelogTitle": "Upgrade notes" + } + ], [ "@semantic-release/git", { @@ -104,13 +111,6 @@ "message": "chore(release): ${nextRelease.version} \n\n${nextRelease.notes}" } ], - [ - "@semantic-release/changelog", - { - "changelogFile": "UPGRADE.md", - "changelogTitle": "Upgrade notes" - } - ], [ "@semantic-release/github", { diff --git a/package-lock.json b/package-lock.json index 0ae544af59..451a797f2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,14 +57,14 @@ } }, "node_modules/@commitlint/cli": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.6.7.tgz", - "integrity": "sha512-nzZmfO5KIOupYppn1MsnYX/80I+KDlxiwkks3CJT0XT+t34UgqGi3eSyEuzgcIjPlORk5/GMaAEiys78iLfGMg==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.7.0.tgz", + "integrity": "sha512-28PNJaGuBQZNoz3sd+6uO3b4+5PY+vWzyBfy5JOvFB7QtoZVXf2FYTQs5VO1cn7yAd3y9/0Rx0x6Vx82W/zhuA==", "dev": true, "dependencies": { "@commitlint/format": "^17.4.4", - "@commitlint/lint": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/lint": "^17.7.0", + "@commitlint/load": "^17.7.0", "@commitlint/read": "^17.5.1", "@commitlint/types": "^17.4.4", "execa": "^5.0.0", @@ -81,31 +81,17 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.6.7.tgz", - "integrity": "sha512-4oTpEUC0HRM54QRHBPMOJW1pETp7usxXn9RuNYNWHcmu8wi1mpws95hvS20u2n6HtIkTn0jfn7vHioCm4AGUTw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.7.0.tgz", + "integrity": "sha512-iicqh2o6et+9kWaqsQiEYZzfLbtoWv9uZl8kbI8EGfnc0HeGafQBF7AJ0ylN9D/2kj6txltsdyQs8+2fTMwWEw==", "dev": true, "dependencies": { - "conventional-changelog-conventionalcommits": "^5.0.0" + "conventional-changelog-conventionalcommits": "^6.1.0" }, "engines": { "node": ">=v14" } }, - "node_modules/@commitlint/config-conventional/node_modules/conventional-changelog-conventionalcommits": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", - "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@commitlint/config-validator": { "version": "17.6.7", "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.6.7.tgz", @@ -120,17 +106,17 @@ } }, "node_modules/@commitlint/cz-commitlint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.6.7.tgz", - "integrity": "sha512-mX1gKRJYzOgbBDVpvlDlLJIbF2dReT2L410eTcnV3DK8AJ5vvkhuXXRPN3jyqO2FVbtYzZDht6noHMhPGFDVlA==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.7.0.tgz", + "integrity": "sha512-292rzapJaQ6WjgcY+KnjChQsWFo7cnPcz9qFT5nUWznDCbZG9B8bGNLyOY2OEdNdI/7+E/nAaJp97rXu091uBg==", "dev": true, "dependencies": { "@commitlint/ensure": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/load": "^17.7.0", "@commitlint/types": "^17.4.4", "chalk": "^4.1.0", "lodash.isplainobject": "^4.0.6", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">=v14" @@ -320,27 +306,27 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.6.7.tgz", - "integrity": "sha512-vqyNRqtbq72P2JadaoWiuoLtXIs9SaAWDqdtef6G2zsoXqKFc7vqj1f+thzVgosXG3X/5K9jNp+iYijmvOfc/g==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.7.0.tgz", + "integrity": "sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==", "dev": true, "dependencies": { "@commitlint/types": "^17.4.4", - "semver": "7.5.2" + "semver": "7.5.4" }, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/lint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.6.7.tgz", - "integrity": "sha512-TW+AozfuOFMrHn+jdwtz0IWu8REKFp0eryOvoBp2r8IXNc4KihKB1spAiUB6SFyHD6hVVeolz12aHnJ3Mb+xVQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.7.0.tgz", + "integrity": "sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^17.6.7", - "@commitlint/parse": "^17.6.7", - "@commitlint/rules": "^17.6.7", + "@commitlint/is-ignored": "^17.7.0", + "@commitlint/parse": "^17.7.0", + "@commitlint/rules": "^17.7.0", "@commitlint/types": "^17.4.4" }, "engines": { @@ -348,25 +334,22 @@ } }, "node_modules/@commitlint/load": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.6.7.tgz", - "integrity": "sha512-QZ2rJTbX55BQdYrCm/p6+hh/pFBgC9nTJxfsrK6xRPe2thiQzHN0AQDBqBwAirn6gIkHrjIbCbtAE6kiDYLjrw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.7.0.tgz", + "integrity": "sha512-jHKErVteyENYKkUiESNE6JYbaU4Sx58HXqv09/p7cKHzTtfyperlRvyqqTnxS8bas5Jjg4MP3MDZwdBrgRm8lw==", "dev": true, "dependencies": { "@commitlint/config-validator": "^17.6.7", "@commitlint/execute-rule": "^17.4.0", "@commitlint/resolve-extends": "^17.6.7", "@commitlint/types": "^17.4.4", - "@types/node": "*", "chalk": "^4.1.0", "cosmiconfig": "^8.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4 || ^5.0.0" + "resolve-from": "^5.0.0" }, "engines": { "node": ">=v14" @@ -452,14 +435,14 @@ } }, "node_modules/@commitlint/parse": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.6.7.tgz", - "integrity": "sha512-ibO03BgEns+JJpohpBZYD49mCdSNMg6fTv7vA5yqzEFWkBQk5NWhEBw2yG+Z1UClStIRkMkAYyI2HzoQG9tCQQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz", + "integrity": "sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==", "dev": true, "dependencies": { "@commitlint/types": "^17.4.4", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "conventional-changelog-angular": "^6.0.0", + "conventional-commits-parser": "^4.0.0" }, "engines": { "node": ">=v14" @@ -499,9 +482,9 @@ } }, "node_modules/@commitlint/rules": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.6.7.tgz", - "integrity": "sha512-x/SDwDTN3w3Gr5xkhrIORu96rlKCc8ZLYEMXRqi9+MB33st2mKcGvKa5uJuigHlbl3xm75bAAubATrodVrjguQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz", + "integrity": "sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==", "dev": true, "dependencies": { "@commitlint/ensure": "^17.6.7", @@ -690,43 +673,6 @@ "node": ">=8" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -848,36 +794,6 @@ "semantic-release": ">=20.1.0" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@semantic-release/error": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", @@ -1493,36 +1409,6 @@ "semantic-release": ">=20.1.0" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@semantic-release/release-notes-generator/node_modules/find-up": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", @@ -1725,30 +1611,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, "node_modules/@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -1756,10 +1618,11 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.1.tgz", - "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==", - "dev": true + "version": "20.4.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", + "integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==", + "dev": true, + "peer": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -1767,27 +1630,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", @@ -1871,12 +1713,6 @@ "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2234,6 +2070,85 @@ "node": ">= 12" } }, + "node_modules/commitizen/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/commitizen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/commitizen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/commitizen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/commitizen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commitizen/node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/commitizen/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -2249,6 +2164,41 @@ "node": ">=10" } }, + "node_modules/commitizen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commitizen/node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/commitizen/node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -2270,6 +2220,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/commitizen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/commitizen/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -2297,16 +2271,15 @@ } }, "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", "dev": true, "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/conventional-changelog-conventionalcommits": { @@ -2362,23 +2335,21 @@ } }, "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", "dev": true, "dependencies": { "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" }, "bin": { "conventional-commits-parser": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/core-util-is": { @@ -2388,9 +2359,9 @@ "dev": true }, "node_modules/cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, "dependencies": { "import-fresh": "^3.2.1", @@ -2400,30 +2371,28 @@ }, "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, "node_modules/cosmiconfig-typescript-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", - "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", "dev": true, + "dependencies": { + "jiti": "^1.19.1" + }, "engines": { - "node": ">=12", - "npm": ">=6" + "node": ">=v16" }, "peerDependencies": { "@types/node": "*", - "cosmiconfig": ">=7", - "ts-node": ">=10", - "typescript": ">=3" + "cosmiconfig": ">=8.2", + "typescript": ">=4" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2590,15 +2559,6 @@ "node": ">=8" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3432,9 +3392,9 @@ "dev": true }, "node_modules/inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dev": true, "dependencies": { "ansi-escapes": "^4.2.1", @@ -3451,7 +3411,7 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" }, "engines": { "node": ">=12.0.0" @@ -3565,8 +3525,22 @@ "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/into-stream": { @@ -3766,6 +3740,15 @@ "node": ">= 0.6.0" } }, + "node_modules/jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4127,12 +4110,6 @@ "node": ">=10" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -8010,16 +7987,6 @@ "node": ">=6" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8762,9 +8729,9 @@ } }, "node_modules/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -9208,49 +9175,6 @@ "node": ">=8" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "node_modules/tslib": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", @@ -9274,6 +9198,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9349,12 +9274,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -9390,9 +9309,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9520,15 +9439,6 @@ "node": ">=12" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", @@ -9570,14 +9480,14 @@ } }, "@commitlint/cli": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.6.7.tgz", - "integrity": "sha512-nzZmfO5KIOupYppn1MsnYX/80I+KDlxiwkks3CJT0XT+t34UgqGi3eSyEuzgcIjPlORk5/GMaAEiys78iLfGMg==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.7.0.tgz", + "integrity": "sha512-28PNJaGuBQZNoz3sd+6uO3b4+5PY+vWzyBfy5JOvFB7QtoZVXf2FYTQs5VO1cn7yAd3y9/0Rx0x6Vx82W/zhuA==", "dev": true, "requires": { "@commitlint/format": "^17.4.4", - "@commitlint/lint": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/lint": "^17.7.0", + "@commitlint/load": "^17.7.0", "@commitlint/read": "^17.5.1", "@commitlint/types": "^17.4.4", "execa": "^5.0.0", @@ -9588,25 +9498,12 @@ } }, "@commitlint/config-conventional": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.6.7.tgz", - "integrity": "sha512-4oTpEUC0HRM54QRHBPMOJW1pETp7usxXn9RuNYNWHcmu8wi1mpws95hvS20u2n6HtIkTn0jfn7vHioCm4AGUTw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.7.0.tgz", + "integrity": "sha512-iicqh2o6et+9kWaqsQiEYZzfLbtoWv9uZl8kbI8EGfnc0HeGafQBF7AJ0ylN9D/2kj6txltsdyQs8+2fTMwWEw==", "dev": true, "requires": { - "conventional-changelog-conventionalcommits": "^5.0.0" - }, - "dependencies": { - "conventional-changelog-conventionalcommits": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", - "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - } - } + "conventional-changelog-conventionalcommits": "^6.1.0" } }, "@commitlint/config-validator": { @@ -9620,17 +9517,17 @@ } }, "@commitlint/cz-commitlint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.6.7.tgz", - "integrity": "sha512-mX1gKRJYzOgbBDVpvlDlLJIbF2dReT2L410eTcnV3DK8AJ5vvkhuXXRPN3jyqO2FVbtYzZDht6noHMhPGFDVlA==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/cz-commitlint/-/cz-commitlint-17.7.0.tgz", + "integrity": "sha512-292rzapJaQ6WjgcY+KnjChQsWFo7cnPcz9qFT5nUWznDCbZG9B8bGNLyOY2OEdNdI/7+E/nAaJp97rXu091uBg==", "dev": true, "requires": { "@commitlint/ensure": "^17.6.7", - "@commitlint/load": "^17.6.7", + "@commitlint/load": "^17.7.0", "@commitlint/types": "^17.4.4", "chalk": "^4.1.0", "lodash.isplainobject": "^4.0.6", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "dependencies": { "ansi-styles": { @@ -9766,47 +9663,44 @@ } }, "@commitlint/is-ignored": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.6.7.tgz", - "integrity": "sha512-vqyNRqtbq72P2JadaoWiuoLtXIs9SaAWDqdtef6G2zsoXqKFc7vqj1f+thzVgosXG3X/5K9jNp+iYijmvOfc/g==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.7.0.tgz", + "integrity": "sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==", "dev": true, "requires": { "@commitlint/types": "^17.4.4", - "semver": "7.5.2" + "semver": "7.5.4" } }, "@commitlint/lint": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.6.7.tgz", - "integrity": "sha512-TW+AozfuOFMrHn+jdwtz0IWu8REKFp0eryOvoBp2r8IXNc4KihKB1spAiUB6SFyHD6hVVeolz12aHnJ3Mb+xVQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.7.0.tgz", + "integrity": "sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==", "dev": true, "requires": { - "@commitlint/is-ignored": "^17.6.7", - "@commitlint/parse": "^17.6.7", - "@commitlint/rules": "^17.6.7", + "@commitlint/is-ignored": "^17.7.0", + "@commitlint/parse": "^17.7.0", + "@commitlint/rules": "^17.7.0", "@commitlint/types": "^17.4.4" } }, "@commitlint/load": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.6.7.tgz", - "integrity": "sha512-QZ2rJTbX55BQdYrCm/p6+hh/pFBgC9nTJxfsrK6xRPe2thiQzHN0AQDBqBwAirn6gIkHrjIbCbtAE6kiDYLjrw==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.7.0.tgz", + "integrity": "sha512-jHKErVteyENYKkUiESNE6JYbaU4Sx58HXqv09/p7cKHzTtfyperlRvyqqTnxS8bas5Jjg4MP3MDZwdBrgRm8lw==", "dev": true, "requires": { "@commitlint/config-validator": "^17.6.7", "@commitlint/execute-rule": "^17.4.0", "@commitlint/resolve-extends": "^17.6.7", "@commitlint/types": "^17.4.4", - "@types/node": "*", "chalk": "^4.1.0", "cosmiconfig": "^8.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4 || ^5.0.0" + "resolve-from": "^5.0.0" }, "dependencies": { "ansi-styles": { @@ -9867,14 +9761,14 @@ "dev": true }, "@commitlint/parse": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.6.7.tgz", - "integrity": "sha512-ibO03BgEns+JJpohpBZYD49mCdSNMg6fTv7vA5yqzEFWkBQk5NWhEBw2yG+Z1UClStIRkMkAYyI2HzoQG9tCQQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz", + "integrity": "sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==", "dev": true, "requires": { "@commitlint/types": "^17.4.4", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "conventional-changelog-angular": "^6.0.0", + "conventional-commits-parser": "^4.0.0" } }, "@commitlint/read": { @@ -9905,9 +9799,9 @@ } }, "@commitlint/rules": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.6.7.tgz", - "integrity": "sha512-x/SDwDTN3w3Gr5xkhrIORu96rlKCc8ZLYEMXRqi9+MB33st2mKcGvKa5uJuigHlbl3xm75bAAubATrodVrjguQ==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz", + "integrity": "sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==", "dev": true, "requires": { "@commitlint/ensure": "^17.6.7", @@ -10037,37 +9931,6 @@ } } }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10159,29 +10022,6 @@ "import-from": "^4.0.0", "lodash-es": "^4.17.21", "micromatch": "^4.0.2" - }, - "dependencies": { - "conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "requires": { - "compare-func": "^2.0.0" - } - }, - "conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "requires": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - } - } } }, "@semantic-release/error": { @@ -10602,27 +10442,6 @@ "read-pkg-up": "^10.0.0" }, "dependencies": { - "conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", - "dev": true, - "requires": { - "compare-func": "^2.0.0" - } - }, - "conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", - "dev": true, - "requires": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - } - }, "find-up": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", @@ -10755,30 +10574,6 @@ } } }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, "@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -10786,10 +10581,11 @@ "dev": true }, "@types/node": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.1.tgz", - "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==", - "dev": true + "version": "20.4.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", + "integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==", + "dev": true, + "peer": true }, "@types/normalize-package-data": { "version": "2.4.1", @@ -10797,18 +10593,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, "agent-base": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", @@ -10870,12 +10654,6 @@ "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -11137,6 +10915,58 @@ "strip-json-comments": "3.1.1" }, "dependencies": { + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -11149,6 +10979,35 @@ "universalify": "^2.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -11160,6 +11019,21 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true } } }, @@ -11190,13 +11064,12 @@ } }, "conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", "dev": true, "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "compare-func": "^2.0.0" } }, "conventional-changelog-conventionalcommits": { @@ -11240,17 +11113,15 @@ } }, "conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", "dev": true, "requires": { "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" } }, "core-util-is": { @@ -11260,9 +11131,9 @@ "dev": true }, "cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, "requires": { "import-fresh": "^3.2.1", @@ -11272,17 +11143,13 @@ } }, "cosmiconfig-typescript-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", - "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", "dev": true, - "requires": {} - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "requires": { + "jiti": "^1.19.1" + } }, "cross-spawn": { "version": "7.0.3", @@ -11403,12 +11270,6 @@ "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -12022,9 +11883,9 @@ "dev": true }, "inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -12041,7 +11902,7 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" }, "dependencies": { "ansi-escapes": { @@ -12116,6 +11977,17 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } } } }, @@ -12259,6 +12131,12 @@ "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", "dev": true }, + "jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12559,12 +12437,6 @@ "yallist": "^4.0.0" } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -15177,12 +15049,6 @@ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -15689,9 +15555,9 @@ } }, "semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -16034,27 +15900,6 @@ "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, "tslib": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", @@ -16071,7 +15916,8 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true + "dev": true, + "peer": true }, "uglify-js": { "version": "3.17.4", @@ -16122,12 +15968,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -16157,9 +15997,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wordwrap": { @@ -16258,12 +16098,6 @@ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, "yocto-queue": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java index b46dcbef88..ed55013bc0 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/AttributeRules.java @@ -1,7 +1,9 @@ package cz.metacentrum.perun.core.api; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -12,7 +14,7 @@ public class AttributeRules { private List attributePolicyCollections; - private List criticalActions = new ArrayList<>(); + private Map criticalActions = new HashMap<>(); public AttributeRules() { } @@ -29,11 +31,11 @@ public void setAttributePolicyCollections(List attrib this.attributePolicyCollections = attributePolicyCollections; } - public List getCriticalActions() { + public Map getCriticalActions() { return criticalActions; } - public void setCriticalActions(List criticalActions) { + public void setCriticalActions(Map criticalActions) { this.criticalActions = criticalActions; } diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java index f12d3e198f..eeebb5c51f 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java @@ -99,6 +99,7 @@ public void initBeansUtils() { private int mfaAuthTimeout; private int mfaAuthTimeoutPercentageForceLogIn; private boolean enforceMfa; + private Map> appAllowedRoles = new HashMap<>(); private int idpLoginValidity; private List idpLoginValidityExceptions; private int roleUpdateInterval; @@ -451,6 +452,35 @@ private String getOidcIssuerProperty(String issuer, String suffix) { return value; } + public Map> getAppAllowedRoles() { + return appAllowedRoles; + } + + public void setAppAllowedRoles(List apps) { + for (String app : apps) { + String regex = getAppAllowedRolesProperty(app, "reg"); + if (regex == null) continue; + + String rolesProperty = getAppAllowedRolesProperty(app, "roles"); + if (rolesProperty == null) continue; + + List roles = List.of(rolesProperty.split("\s*,\s*")); + + log.debug("registering application {} by regex={} with roles={}", app, regex, roles); + + this.appAllowedRoles.put(regex, roles); + } + } + + private String getAppAllowedRolesProperty(String app, String suffix) { + String property = "perun.appAllowedRoles." + app + "." + suffix; + String value = properties.getProperty(property); + if (value == null) { + log.error("property {} not found, skipping allowed roles for application {}", property, app); + } + return value; + } + public Map getOidcIssuersExtsourceNames() { return oidcIssuersExtsourceNames; } diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java index 2f916a291a..8a93462d04 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/MembersOrderColumn.java @@ -14,13 +14,20 @@ public enum MembersOrderColumn { NAME( ", users.first_name, users.last_name ", "", + ", users.last_name, users.first_name", query -> "users.last_name " + getLangSql(query) + query.getOrder().getSqlValue() + ", " + "users.first_name " + getLangSql(query) + query.getOrder().getSqlValue() ), ID("", "", query -> "members.id " + query.getOrder().getSqlValue()), - STATUS("","", query -> "members.status " + query.getOrder().getSqlValue()), - GROUP_STATUS("", "", query -> "groups_members.source_group_status " + query.getOrder().getSqlValue()), + STATUS("", + "", + "", + query -> "members.status " + query.getOrder().getSqlValue()), + GROUP_STATUS("", + "", + ", groups_members.group_id, groups_members.source_group_id, groups_members.membership_type, groups_members.source_group_status", + query -> "groups_members.source_group_status " + query.getOrder().getSqlValue()), // 1. user preferred mail, 2. member mail EMAIL( @@ -33,6 +40,7 @@ public enum MembersOrderColumn { "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:preferredMail') ", + ", usrvals.attr_value, memvals.attr_value ", query -> "usrvals.attr_value " + query.getOrder().getSqlValue() + ", " + "memvals.attr_value " + query.getOrder().getSqlValue() ), @@ -48,6 +56,7 @@ public enum MembersOrderColumn { "(select attr_value, user_id, attr_id from user_attr_values) as usrvals " + "on members.user_id=usrvals.user_id and usrvals.attr_id=" + "(select id from attr_names where attr_name='urn:perun:user:attribute-def:def:organization') ", + ", usrvals.attr_value, memvals.attr_value ", query -> "memvals.attr_value " + query.getOrder().getSqlValue() + ", " + "usrvals.attr_value " + query.getOrder().getSqlValue() ); @@ -55,10 +64,19 @@ public enum MembersOrderColumn { private final Function orderBySqlFunction; private final String selectSql; private final String joinSql; + private final String groupbySql; + + MembersOrderColumn(String selectSql, String joinSql, String groupbySql, Function sqlFunction) { + this.selectSql = selectSql; + this.joinSql = joinSql; + this.groupbySql = groupbySql; + this.orderBySqlFunction = sqlFunction; + } MembersOrderColumn(String selectSql, String joinSql, Function sqlFunction) { this.selectSql = selectSql; this.joinSql = joinSql; + this.groupbySql = ""; this.orderBySqlFunction = sqlFunction; } @@ -74,6 +92,10 @@ public String getSqlJoin() { return this.joinSql; } + public String getSqlGroupBy() { + return this.groupbySql; + } + private static String getLangSql(MembersPageQuery query) { return ""; // TODO add support for other languages diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java index 50516b3550..070c30c7c2 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/PerunPrincipal.java @@ -20,6 +20,8 @@ public class PerunPrincipal { private User user; // Contains principal's roles together with objects which specifies the role, e.g. VOADMIN -> list contains VO names private volatile AuthzRoles authzRoles = new AuthzRoles(); + // The PERUNADMIN role is enabled and will not be manually removed + private String referer = ""; // Time of the last update of roles private long rolesUpdatedAt = System.currentTimeMillis(); // Map contains additional attributes, e.g. from authentication system @@ -62,6 +64,12 @@ public PerunPrincipal(String actor, String extSourceName, String extSourceType, this.additionalInformations = additionalInformations; } + public PerunPrincipal(String actor, String extSourceName, String extSourceType, int extSourceLoa, Map additionalInformations, String referer) { + this(actor, extSourceName, extSourceType, extSourceLoa); + this.additionalInformations = additionalInformations; + this.referer = referer; + } + /** * Returns actor string representation. * @return string representing actor @@ -153,6 +161,14 @@ public void setRolesUpdatedAt(long rolesUpdatedAt) { this.rolesUpdatedAt = rolesUpdatedAt; } + public String getReferer() { + return referer; + } + + public void setReferer(String referer) { + this.referer = referer; + } + @Override public int hashCode() { final int prime = 31; diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java index cc627156d7..fe0bc71576 100644 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/RichDestination.java @@ -1,8 +1,6 @@ package cz.metacentrum.perun.core.api; -import cz.metacentrum.perun.core.api.Service; -import java.util.List; -import cz.metacentrum.perun.core.api.BeansUtils; +import java.sql.Timestamp; /** * Destination where services are propagated. @@ -12,6 +10,7 @@ public class RichDestination extends Destination implements Comparable userExtSources, List userAttributes, List memberAttributes) { this(user, member, userExtSources); this.userAttributes = userAttributes; @@ -60,6 +70,14 @@ public void setBlocked(boolean blocked) { this.blocked = blocked; } + public Timestamp getLastSuccessfulPropagation() { + return lastSuccessfulPropagation; + } + + public void setLastSuccessfulPropagation(Timestamp lastSuccessfulPropagation) { + this.lastSuccessfulPropagation = lastSuccessfulPropagation; + } + @Override public int hashCode() { final int prime = 31; @@ -109,6 +127,7 @@ public String serializeToString() { ", facility=<").append(getFacility() == null ? "\\0" : getFacility().serializeToString()).append(">").append( ", service=<").append(getService() == null ? "\\0" : getService().serializeToString()).append(">").append( ", blocked=<").append(isBlocked()).append(">").append( + ", lastSuccessfulPropagation=<").append(getLastSuccessfulPropagation()).append(">").append( ']').toString(); } @@ -123,6 +142,7 @@ public String toString() { ).append("', facility='").append(getFacility() ).append("', service='").append(getService() ).append("', blocked='").append(isBlocked() + ).append("', lastSuccessfulPropagation='").append(getLastSuccessfulPropagation() ).append("']").toString(); } } diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/NotificationMemberMailNotExistsException.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/NotificationMemberMailNotExistsException.java new file mode 100644 index 0000000000..a454f8ca36 --- /dev/null +++ b/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/NotificationMemberMailNotExistsException.java @@ -0,0 +1,23 @@ +package cz.metacentrum.perun.core.api.exceptions; + +/** + * This exception is thrown when the user doesn't have the chosen attribute for mail set, + * where the notification should be sent to. + * + * @author Radoslav Čerhák + * @author Dominik František Bučík + */ +public class NotificationMemberMailNotExistsException extends PerunException { + + public NotificationMemberMailNotExistsException(String message) { + super(message); + } + + public NotificationMemberMailNotExistsException(String message, Throwable cause) { + super(message, cause); + } + + public NotificationMemberMailNotExistsException(Throwable cause) { + super(cause); + } +} diff --git a/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/PasswordResetMailNotExistsException.java b/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/PasswordResetMailNotExistsException.java deleted file mode 100644 index 8154957c12..0000000000 --- a/perun-base/src/main/java/cz/metacentrum/perun/core/api/exceptions/PasswordResetMailNotExistsException.java +++ /dev/null @@ -1,22 +0,0 @@ -package cz.metacentrum.perun.core.api.exceptions; - -/** - * This exception is thrown when the user doesn't have the chosen attribute for mail set, - * where password reset link should be send to. - * - * @author Radoslav Čerhák - */ -public class PasswordResetMailNotExistsException extends PerunException { - - public PasswordResetMailNotExistsException(String message) { - super(message); - } - - public PasswordResetMailNotExistsException(String message, Throwable cause) { - super(message, cause); - } - - public PasswordResetMailNotExistsException(Throwable cause) { - super(cause); - } -} diff --git a/perun-base/src/main/resources/perun-base.xml b/perun-base/src/main/resources/perun-base.xml index c8f9be10d3..f17a957a55 100644 --- a/perun-base/src/main/resources/perun-base.xml +++ b/perun-base/src/main/resources/perun-base.xml @@ -82,6 +82,7 @@ + @@ -169,6 +170,8 @@ https://login.elixir-czech.org/idp/ cz.metacentrum.perun.core.impl.ExtSourceIdp + + true localhost diff --git a/perun-base/src/main/resources/perun-roles.yml b/perun-base/src/main/resources/perun-roles.yml index 4499dd94f6..8cd14c18aa 100644 --- a/perun-base/src/main/resources/perun-roles.yml +++ b/perun-base/src/main/resources/perun-roles.yml @@ -638,6 +638,9 @@ perun_policies: - ENGINE: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility + - RESOURCESELFSERVICE: Facility + - RESOURCEADMIN: Facility + - RESOURCEOBSERVER: Facility - PERUNOBSERVER: - SPREGAPPLICATION: include_policies: @@ -726,6 +729,7 @@ perun_policies: - FACILITYADMIN: - FACILITYOBSERVER: - PERUNOBSERVER: + - SPREGAPPLICATION: - PROXY: include_policies: - default_policy @@ -735,6 +739,7 @@ perun_policies: - FACILITYADMIN: Facility - FACILITYOBSERVER: Facility - PERUNOBSERVER: + - SPREGAPPLICATION: - PROXY: include_policies: - default_policy @@ -3327,6 +3332,18 @@ perun_policies: include_policies: - default_policy + sendUsernameReminderEmail_Member_String_String_String_policy: + policy_roles: + - VOADMIN: Vo + - SPONSORSHIP: Member + - PASSWORDRESETMANAGER: + - PROXY: + include_policies: + - default_policy + mfa_rules: + - MFA: User + - MFA: Member + sendPasswordResetLinkEmail_Member_String_String_String_String_policy: policy_roles: - VOADMIN: Vo @@ -3613,6 +3630,17 @@ perun_policies: include_policies: - default_policy + filter-getMembersPage_policy: + policy_roles: + - GROUPADMIN: + - GROUPOBSERVER: + - GROUPMEMBERSHIPMANAGER: + - PERUNOBSERVER: + - VOADMIN: + - VOOBSERVER: + include_policies: + - default_policy + updateSponsorshipValidity_Member_User_LocalDate: policy_roles: - VOADMIN: Vo @@ -6506,6 +6534,22 @@ perun_policies: include_policies: - default_policy + filter-findRichUsersWithAttributes_policy: + policy_roles: + - SECURITYADMIN: + - FACILITYADMIN: + - FACILITYOBSERVER: + - RESOURCEADMIN: + - RESOURCEOBSERVER: + - GROUPADMIN: + - GROUPOBSERVER: + - GROUPMEMBERSHIPMANAGER: + - VOADMIN: + - VOOBSERVER: + - PERUNOBSERVER: + include_policies: + - default_policy + findRichUsersWithAttributesByExactMatch_String_List_policy: policy_roles: - PERUNOBSERVER: diff --git a/perun-base/src/test/resources/test-roles.yml b/perun-base/src/test/resources/test-roles.yml index d1d24a0e71..8058c49c00 100644 --- a/perun-base/src/test/resources/test-roles.yml +++ b/perun-base/src/test/resources/test-roles.yml @@ -205,5 +205,38 @@ perun_policies: - PROXY: include_policies: [] + test_filter-getMembersPage_policy: + policy_roles: + - GROUPADMIN: Group + - GROUPOBSERVER: Group + - GROUPMEMBERSHIPMANAGER: Group + - PERUNOBSERVER: + - VOADMIN: + - VOOBSERVER: + include_policies: + - default_policy + + test_filter-getMembersPage_policy-vo: + policy_roles: + - GROUPADMIN: Group + - GROUPOBSERVER: Group + - GROUPMEMBERSHIPMANAGER: Group + - PERUNOBSERVER: + - VOADMIN: Vo + - VOOBSERVER: + include_policies: + - default_policy + + test_filter-getMembersPage_policy-voobserver: + policy_roles: + - GROUPADMIN: Group + - GROUPOBSERVER: Group + - GROUPMEMBERSHIPMANAGER: Group + - PERUNOBSERVER: + - VOADMIN: + - VOOBSERVER: Vo + include_policies: + - default_policy + perun_roles_management: {} ... \ No newline at end of file diff --git a/perun-base/src/test/resources/test-schema.sql b/perun-base/src/test/resources/test-schema.sql index 11b55ea733..9dbaab915e 100644 --- a/perun-base/src/test/resources/test-schema.sql +++ b/perun-base/src/test/resources/test-schema.sql @@ -1,4 +1,4 @@ --- database version 3.2.15 (don't forget to update insert statement at the end of file) +-- database version 3.2.17 (don't forget to update insert statement at the end of file) CREATE EXTENSION IF NOT EXISTS "unaccent"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; @@ -331,6 +331,7 @@ create table attribute_policies ( create table attribute_critical_actions ( attr_id integer not null, --identifier of attribute (attr_names.id) action attribute_action not null, --action on attribute (READ/WRITE) + global boolean default false not null, --action is critical globally for all objects constraint attrcritops_pk primary key (attr_id, action), constraint attrcritops_attr_fk foreign key (attr_id) references attr_names (id) on delete cascade ); @@ -1600,6 +1601,8 @@ create table authz ( modified_by_uid integer, authorized_group_id integer, --identifier of whole authorized group security_team_id integer, --identifier of security team + created_at timestamp default statement_timestamp() not null, + created_by varchar default user not null, constraint authz_role_fk foreign key (role_id) references roles(id), constraint authz_user_fk foreign key (user_id) references users(id), constraint authz_authz_group_fk foreign key (authorized_group_id) references groups(id), @@ -1905,7 +1908,7 @@ create index idx_fk_attr_critops ON attribute_critical_actions(attr_id); create index app_state_idx ON application (state); -- set initial Perun DB version -insert into configurations values ('DATABASE VERSION','3.2.15'); +insert into configurations values ('DATABASE VERSION','3.2.17'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); insert into membership_types (id, membership_type, description) values (2, 'INDIRECT', 'Member is added indirectly through UNION relation'); diff --git a/perun-cli/Perun/beans/Attribute.pm b/perun-cli/Perun/beans/Attribute.pm index 0890ee61ff..45dd36950c 100644 --- a/perun-cli/Perun/beans/Attribute.pm +++ b/perun-cli/Perun/beans/Attribute.pm @@ -3,7 +3,6 @@ package Perun::beans::Attribute; use strict; use warnings; use Switch; -use Data::Dumper; use overload '""' => \&toString; @@ -269,19 +268,13 @@ sub getValueAsScalar { case "SCALAR" { return $value } case "ARRAY" { return '["'.join('", "', @$value).'"]' } case "HASH" { - local $Data::Dumper::Terse = 1; - local $Data::Dumper::Indent = 0; - local $Data::Dumper::Useqq = 1; - - { - no warnings 'redefine'; - sub Data::Dumper::qquote { - my $s = shift; - return "'$s'"; - } + my $str = '{'; + foreach my $key (reverse keys %$value) { + $str .= '"'.$key.'" => "'.$value->{$key}.'",'; } - - return Dumper($value); + $str =~ s/,$//; + $str .= '}'; + return $str; } case "JSON::XS::Boolean" { return ($value) ? 'true' : 'false'; @@ -322,11 +315,18 @@ sub setValueFromArray { } } case /^array$/ { - $attribute->setValue( \@_ ); + my @arr = @_; + for (my $i=0; $isetValue( \@arr ); } case "hash" { my %hash = @_; - $attribute->setValue( \%hash ); + for my $key (keys %hash) { + utf8::decode($hash{$key}); + } + $attribute->setValue( \%hash ); } else { die "Unknown attribute type. Type=".$attribute->getType; diff --git a/perun-cli/setAttributeCriticalActions b/perun-cli/setAttributeCriticalActions index e725ebf98a..150dd06dc9 100755 --- a/perun-cli/setAttributeCriticalActions +++ b/perun-cli/setAttributeCriticalActions @@ -6,7 +6,6 @@ use Getopt::Long qw(:config no_ignore_case); use Perun::Agent; use Perun::Common qw(printMessage); use Switch; -use Data::Dumper; sub help { return qq{ diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java index 6a846fa815..a17b281f07 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/AttributesManager.java @@ -4140,12 +4140,13 @@ Map getEntitylessAttributesWithKeys(PerunSession sess, String * @param attr attribute definition * @param action critical action * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects * * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action * @throws PrivilegeException insufficient permissions */ - void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException, PrivilegeException; + void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException, PrivilegeException; /** * Returns list of definitions of IdP attributes that are filled to fedInfo diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java index fc9ad017f7..ee926c9b27 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/api/MembersManager.java @@ -22,8 +22,9 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; -import cz.metacentrum.perun.core.api.exceptions.PasswordResetMailNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.NotificationMemberMailNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.ResourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; @@ -1138,17 +1139,32 @@ public interface MembersManager { Date getNewExtendMembership(PerunSession sess, Member member) throws MemberNotExistsException; /** - * Returns the date to which will be extended member's expiration time. - * - * @param sess - * @param vo - * @param loa - * @return date - * @throws InternalErrorException - * @throws VoNotExistsException - * @throws ExtendMembershipException - */ - Date getNewExtendMembership(PerunSession sess, Vo vo, String loa) throws VoNotExistsException, ExtendMembershipException; + * Returns the date to which will be extended member's expiration time. + * + * @param sess + * @param vo + * @param loa + * @return date + * @throws InternalErrorException + * @throws VoNotExistsException + * @throws ExtendMembershipException + */ + Date getNewExtendMembership(PerunSession sess, Vo vo, String loa) throws VoNotExistsException, ExtendMembershipException; + + /** + * Send mail to user's preferred email address with reminder of the username in the given namespace. + * + * @param sess PerunSession + * @param member Member to get user to send link mail to + * @param namespace namespace to change password in (member must have login in it) + * @param mailAttributeUrn urn of the attribute with stored mail + * @param language language of the message + * @throws InternalErrorException + * @throws PrivilegeException If not VO admin of member + * @throws MemberNotExistsException If member not exists + * @throws NotificationMemberMailNotExistsException If the attribute with stored mail is not filled. + */ + void sendUsernameReminderEmail(PerunSession sess, Member member, String namespace, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException; /** * Send mail to user's preferred email address with link for non-authz password reset. @@ -1163,9 +1179,9 @@ public interface MembersManager { * @throws InternalErrorException * @throws PrivilegeException If not VO admin of member * @throws MemberNotExistsException If member not exists - * @throws PasswordResetMailNotExistsException If the attribute with stored mail is not filled. + * @throws NotificationMemberMailNotExistsException If the attribute with stored mail is not filled. */ - void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException; + void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException; /** * Send mail to user's preferred email address with link for non-authz account activation. @@ -1180,9 +1196,9 @@ public interface MembersManager { * @throws InternalErrorException * @throws PrivilegeException If not VO admin of member * @throws MemberNotExistsException If member not exists - * @throws PasswordResetMailNotExistsException If the attribute with stored mail is not filled. + * @throws NotificationMemberMailNotExistsException If the attribute with stored mail is not filled. */ - void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException; + void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException; /** * Creates a new sponsored Member and its User. @@ -1506,7 +1522,23 @@ List> createSponsoredMembersFromCSV(PerunSession sess, Vo vo * @throws GroupNotExistsException if there is no such query group * @throws PrivilegeException insufficient permission */ - Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException; + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException; + + /** + * Get page of members from the given vo, with the given attributes, based on policy. + * + * @param sess session + * @param vo vo + * @param query query with page information + * @param attrNames attribute names + * @param policy policy to use + * @return page of requested rich members + * @throws VoNotExistsException if there is no such vo + * @throws GroupNotExistsException if there is no such query group + * @throws PrivilegeException insufficient permission + */ + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException; + /** * Update the sponsorship of given member for given sponsor. diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java index 03508e1fc8..9c84a91bc7 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/AttributesManagerBl.java @@ -4819,6 +4819,16 @@ void mergeAttributesValues(PerunSession sess, Member member, List att */ boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action); + /** + * Checks if the action is critical on given attribute for all objects. + * + * @param sess session + * @param attr attribute definition + * @param action critical action + * @return true if action is globally critical, false otherwise + */ + boolean isAttributeActionGloballyCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action); + /** * Returns critical actions on given attribute. * @@ -4836,11 +4846,12 @@ void mergeAttributesValues(PerunSession sess, Member member, List att * @param attr attribute definition * @param action critical action * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects * * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action */ - void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException; + void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException; /** * Returns list of definitions of IdP attributes that are filled to fedInfo diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java index 1f6da2b580..57d915bfe6 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/bl/MembersManagerBl.java @@ -42,6 +42,7 @@ import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; @@ -1465,6 +1466,18 @@ public interface MembersManagerBl { */ List filterOnlyAllowedAttributes(PerunSession sess, List richMembers, Group group, boolean useContext); + /** + * Send mail to user's preferred email address with username for the given namespace. + * + * @param sess PerunSession + * @param member Member to get user to send mail to + * @param namespace Namespace for username/login (member must have login in this namespace) + * @param mailAddress mail address where email will be sent + * @param language language of the message + * @throws InternalErrorException + */ + void sendUsernameReminderEmail(PerunSession sess, Member member, String namespace, String mailAddress, String language); + /** * Send mail to user's preferred email address with link for non-authz password reset. * Correct authz information is stored in link's URL. @@ -1785,7 +1798,19 @@ List> createSponsoredMembersFromCSV(PerunSession sess, Vo vo * @param attrNames attribute names * @return page of requested rich members */ - Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames); + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws PolicyNotExistsException; + + /** + * Get page of members from the given vo, with the given attributes, based on policy. + * + * @param sess session + * @param vo vo + * @param query query with page information + * @param attrNames attribute names + * @param policy policy to use + * @return page of requested rich members + */ + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws PolicyNotExistsException; /** * Update the sponsorship of given member for given sponsor. diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java index 0a436cd1b1..c8f6608fe4 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AttributesManagerBlImpl.java @@ -147,6 +147,7 @@ import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import static cz.metacentrum.perun.core.api.AttributeAction.READ; import static cz.metacentrum.perun.core.api.AttributeAction.WRITE; @@ -2506,7 +2507,7 @@ private AttributeDefinition createAttribute(PerunSession sess, AttributeDefiniti // mark WRITE action on this attribute as critical try { - setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true); + setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true, false); } catch (RelationExistsException | RelationNotExistsException ignored) { } @@ -7989,7 +7990,7 @@ protected void initialize() { attr = new AttributeDefinition(); attr.setNamespace(AttributesManager.NS_USER_ATTR_VIRT); attr.setFriendlyName("userEligibilities"); - attr.setDisplayName("user Eligibilities"); + attr.setDisplayName("User eligibilities"); attr.setType(LinkedHashMap.class.getName()); attr.setDescription("Virtual attribute, which collects all eligibilities user ext source attributes " + "with keys and values (map). Only the highest value is selected for each key."); @@ -8733,7 +8734,14 @@ public List getAttributePolicyCollections(PerunSessio @Override public AttributeRules getAttributeRules(PerunSession sess, int attributeId) { AttributeRules attrRules = new AttributeRules(getAttributesManagerImpl().getAttributePolicyCollections(sess, attributeId)); - attrRules.setCriticalActions(getAttributesManagerImpl().getCriticalAttributeActions(sess, attributeId)); + Map actionMap = getAttributesManagerImpl() + .getCriticalAttributeActions(sess, attributeId) + .stream() + .collect(Collectors.toMap( + attrAction -> attrAction, + attrAction -> getAttributesManagerImpl().isAttributeActionGloballyCritical(sess, attributeId, attrAction) + )); + attrRules.setCriticalActions(actionMap); return attrRules; } @@ -8860,14 +8868,19 @@ public boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition return getAttributesManagerImpl().isAttributeActionCritical(sess, attr, action); } + @Override + public boolean isAttributeActionGloballyCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action) { + return getAttributesManagerImpl().isAttributeActionGloballyCritical(sess, attr.getId(), action); + } + @Override public List getCriticalAttributeActions(PerunSession sess, int attrId) { return getAttributesManagerImpl().getCriticalAttributeActions(sess, attrId); } @Override - public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException { - getAttributesManagerImpl().setAttributeActionCriticality(sess, attr, action, critical); + public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException { + getAttributesManagerImpl().setAttributeActionCriticality(sess, attr, action, critical, global); } @Override diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java index 78339ab1b8..65b6f9713d 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java @@ -99,6 +99,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static cz.metacentrum.perun.core.api.AuthzResolver.MFA_CRITICAL_ATTR; @@ -1697,7 +1698,9 @@ public static boolean isMfaAuthorizedForAttribute(PerunSession sess, AttributeDe return principalMfa || updatePrincipalMfa(sess); } - return principalMfa || !isAnyObjectMfaCritical(sess, objects) || updatePrincipalMfa(sess); + boolean globallyCriticalAction = ((PerunBl) sess.getPerun()).getAttributesManagerBl().isAttributeActionGloballyCritical(sess, attrDef, actionType); + + return principalMfa || (!isAnyObjectMfaCritical(sess, objects) && !globallyCriticalAction) || updatePrincipalMfa(sess); } @@ -2285,6 +2288,16 @@ public static boolean isVoObserver(PerunSession sess) { return sess.getPerunPrincipal().getRoles().hasRole(Role.VOOBSERVER); } + /** + * Returns true if the perun principal inside the perun session is Perun Observer. + * + * @param sess perun session + * @return true if the perun principal is top group creator. + */ + public static boolean isPerunObserver(PerunSession sess) { + return sess.getPerunPrincipal().getRoles().hasRole(Role.PERUNOBSERVER); + } + /** * Returns true if the perun principal inside the perun session is top group creator. * @@ -2305,6 +2318,16 @@ public static boolean isPerunAdmin(PerunSession sess) { return sess.getPerunPrincipal().getRoles().hasRole(Role.PERUNADMIN); } + /** + * Returns true if perun principal is Vo admin or Vo observer of specific Vo. + * @param sess - perun session + * @param vo -specific vo + * @return bolean + **/ + public static boolean isVoAdminOrObserver(PerunSession sess, Vo vo) { + return authzResolverImpl.isVoAdminOrObserver(sess, vo); + } + /** * Get all principal role names. * @@ -2572,6 +2595,19 @@ public static synchronized void refreshAuthz(PerunSession sess) { setAdditionalRoles(sess, roles, user); } + // Remove roles which are not allowed + Map> appAllowedRoles = BeansUtils.getCoreConfig().getAppAllowedRoles(); + for (String reg : appAllowedRoles.keySet()) { + Pattern pattern = Pattern.compile(reg); + if (pattern.matcher(sess.getPerunPrincipal().getReferer()).matches()) { + for (String role : roles.getRolesNames()) { + if (!appAllowedRoles.get(reg).contains(role)) { + roles.remove(role); + } + } + } + } + sess.getPerunPrincipal().setRoles(roles); if (sess.getPerunClient().getType() == PerunClient.Type.OAUTH) { @@ -2593,7 +2629,11 @@ public static synchronized void refreshAuthz(PerunSession sess) { } } - checkMfaForHavingRole(sess, sess.getPerunPrincipal().getRoles()); + if (!serviceRole && (sess.getPerunPrincipal().getUser() == null || !sess.getPerunPrincipal().getUser().isServiceUser())) { + checkMfaForHavingRole(sess, sess.getPerunPrincipal().getRoles()); + } else { + log.debug("skipped MFA role check for {}", serviceRole ? sess.getPerunPrincipal().getActor() : sess.getPerunPrincipal().getUser()); + } log.trace("Refreshed roles: {}", sess.getPerunPrincipal().getRoles()); sess.getPerunPrincipal().setAuthzInitialized(true); @@ -4452,4 +4492,14 @@ private static boolean checkAuthValidityForMFA(PerunSession sess) { private static boolean sessionHasMfa(PerunSession sess) { return sess.getPerunPrincipal().getAdditionalInformations().containsKey(ACR_MFA); } + + /** + * Return id of the role by its name. + * + * @param name - name of the role + * @return - id of the role + */ + public static int getRoleIdByName(String name) { + return authzResolverImpl.getRoleIdByName(name); + } } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java index 4f97105185..df871a1ae1 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/MembersManagerBlImpl.java @@ -82,6 +82,7 @@ import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.RelationExistsException; import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException; import cz.metacentrum.perun.core.api.exceptions.RoleManagementRulesNotExistsException; @@ -108,6 +109,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; +import org.springframework.util.StringUtils; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; @@ -2493,55 +2495,46 @@ private boolean isMemberInGracePeriod(Map membershipExpirationRu } @Override - public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, - String mailAddress, String language) { + public void sendUsernameReminderEmail(PerunSession sess, Member member, final String namespace, String mailAddress, + String language) { User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); - List logins = perunBl.getAttributesManagerBl().getLogins(sess, user); - boolean found = false; - for (Attribute a : logins) { - if (a.getFriendlyNameParameter().equals(namespace)) found = true; - } - if (!found) - throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); + String logAction = "account activation"; - String subject; - try { - Attribute subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - if (subject == null) { - subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for subject will be used - log.error("There is missing attribute with subject template for password reset in specific namespace.", ex); - subject = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); + String login = getUserLogin(sess, user, namespace); + if (!StringUtils.hasText(login)) { + throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); } - String message; - try { - Attribute messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - if (message == null) { - messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzPwdResetMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for message will be used - log.error("There is missing attribute with message template for password reset in specific namespace.", ex); - message = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); + String attrNameBase = "usernameReminderMail"; + String subjectAttrName = attrNameBase + "Subject:" + namespace; + String templateAttrName = attrNameBase + "Template:" + namespace; + String subject = getEmailMessagePartFromEntitylessAttribute(sess, subjectAttrName, language, logAction); + String message = getEmailMessagePartFromEntitylessAttribute(sess, templateAttrName, language, logAction); + + Utils.sendUsernameReminderEmail(user, mailAddress, login, namespace, message, subject); + } + + @Override + public void sendPasswordResetLinkEmail(PerunSession sess, Member member, final String namespace, String url, + String mailAddress, String language) { + + User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); + + String logAction = "account activation"; + + String login = getUserLogin(sess, user, namespace); + if (!StringUtils.hasText(login)) { + throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); } + String attrNameBase = "nonAuthzPwdResetMail"; + String subjectAttrName = attrNameBase + "Subject:" + namespace; + String templateAttrName = attrNameBase + "Template:" + namespace; + String subject = getEmailMessagePartFromEntitylessAttribute(sess, subjectAttrName, language, logAction); + String message = getEmailMessagePartFromEntitylessAttribute(sess, templateAttrName, language, logAction); + int validationWindow = BeansUtils.getCoreConfig().getPwdresetValidationWindow(); LocalDateTime validityTo = LocalDateTime.now().plusHours(validationWindow); @@ -2550,61 +2543,29 @@ public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String } @Override - public void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, - String mailAddress, String language) { + public void sendAccountActivationLinkEmail(PerunSession sess, Member member, final String namespace, String url, + String mailAddress, String language) { + User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); + String logAction = "account activation"; - List logins = perunBl.getAttributesManagerBl().getLogins(sess, user); - boolean found = false; - for (Attribute a : logins) { - if (a.getFriendlyNameParameter().equals(namespace)) found = true; - } - if (!found) + String login = getUserLogin(sess, user, namespace); + if (!StringUtils.hasText(login)) { throw new InternalErrorException(user.toString() + " doesn't have login in namespace: " + namespace); - - String subject; - try { - Attribute subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - if (subject == null) { - subjectTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailSubject:" + namespace); - subject = (String) subjectTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for subject will be used - log.error("There is missing attribute with subject template for account activation in specific namespace.", ex); - subject = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); } - String message; - try { - Attribute messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, language, - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - if (message == null) { - messageTemplateAttribute = perunBl.getAttributesManagerBl().getAttribute(sess, "en", - AttributesManager.NS_ENTITYLESS_ATTR_DEF + ":nonAuthzAccActivationMailTemplate:" + namespace); - message = (String) messageTemplateAttribute.getValue(); - } - } catch (AttributeNotExistsException ex) { - //If attribute not exists, log it and use null instead - default template for message will be used - log.error("There is missing attribute with message template for account activation in specific namespace.", ex); - message = null; - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } + String attrNameBase = "nonAuthzAccActivationMail"; + String subjectAttrName = attrNameBase + "Subject:" + namespace; + String templateAttrName = attrNameBase + "Template:" + namespace; + String subject = getEmailMessagePartFromEntitylessAttribute(sess, subjectAttrName, language, logAction); + String message = getEmailMessagePartFromEntitylessAttribute(sess, templateAttrName, language, logAction); int validationWindow = BeansUtils.getCoreConfig().getAccountActivationValidationWindow(); LocalDateTime validityTo = LocalDateTime.now().plusHours(validationWindow); //IMPORTANT: we are using the same requests for password reset and account activation UUID uuid = getMembersManagerImpl().storePasswordResetRequest(sess, user, namespace, mailAddress, validityTo); - String userLogin = getUserLogin(sess, user, namespace); - Utils.sendAccountActivationEmail(user, mailAddress, userLogin, namespace, url, uuid, message, subject, validityTo); + Utils.sendAccountActivationEmail(user, mailAddress, login, namespace, url, uuid, message, subject, validityTo); } @Override @@ -3032,8 +2993,8 @@ public List findMembers(PerunSession sess, Vo vo, String searchString, b } @Override - public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) { - Paginated paginatedMembers = membersManagerImpl.getMembersPage(sess, vo, query); + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws PolicyNotExistsException { + Paginated paginatedMembers = membersManagerImpl.getMembersPage(sess, vo, query, policy); List richMembers = convertMembersToRichMembers(sess, paginatedMembers.getData()); List attrDefs = new ArrayList<>(); @@ -3059,6 +3020,11 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPag paginatedMembers.getTotalCount()); } + @Override + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws PolicyNotExistsException { + return getMembersPage(sess, vo, query, attrNames, null); + } + @Override public void updateSponsorshipValidity(PerunSession sess, Member sponsoredMember, User sponsor, LocalDate newValidity) throws SponsorshipDoesNotExistException { @@ -3729,4 +3695,46 @@ private String getUserLogin(PerunSession sess, User user, String namespace) { throw new InternalErrorException("Failed to get namespace login for user: " + user, e); } } + + /** + * Resolve email template (subject/message) from an entityless attribute. + * @param sess Perun session + * @param attributeName friendly name of the attribute (namespace of entityless is assigned in the method) + * @param language Language of the template. If fails to find, default EN template will be looked up. + * @return Found template for given language. If fails, tries to find for default EN. If fails, returns null. + */ + private String getEmailMessagePartFromEntitylessAttribute(PerunSession sess, + String attributeName, + String language, + String logAction) + { + String template = null; + try { + attributeName = AttributesManager.NS_ENTITYLESS_ATTR_DEF + ':' + attributeName; + try { + Attribute templateAttribute = perunBl.getAttributesManagerBl().getAttribute( + sess, language, attributeName + ); + template = templateAttribute.valueAsString(); + } catch (AttributeNotExistsException ex) { + //If attribute not exists, log it and use null instead - default template for message will be used + log.error("There is missing attribute with message template for {} in specific namespace.", logAction, ex); + } + if (!StringUtils.hasText(template)) { + try { + Attribute templateAttribute = perunBl.getAttributesManagerBl().getAttribute( + sess, "en", attributeName + ); + template = templateAttribute.valueAsString(); + } catch (AttributeNotExistsException ex) { + //If attribute not exists, log it and use null instead - default template for message will be used + log.error("There is missing attribute with email message template for {} in specific namespace.", + logAction, ex); + } + } + } catch (WrongAttributeAssignmentException ex) { + throw new InternalErrorException(ex); + } + return template; + } } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java index 75c13b2310..c8b1a9985c 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/AttributesManagerEntry.java @@ -4644,7 +4644,7 @@ public GraphDTO getModulesDependenciesGraph(PerunSession session, GraphTextForma } @Override - public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException, PrivilegeException { + public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException, PrivilegeException { Utils.checkPerunSession(sess); // Authorization @@ -4652,7 +4652,7 @@ public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition throw new PrivilegeException("setAttributeActionCriticality"); } - attributesManagerBl.setAttributeActionCriticality(sess, attr, action, critical); + attributesManagerBl.setAttributeActionCriticality(sess, attr, action, critical, global); } @Override diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java index abcff123b7..836a8ad6a1 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/MembersManagerEntry.java @@ -48,8 +48,9 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParentGroupNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordCreationFailedException; -import cz.metacentrum.perun.core.api.exceptions.PasswordResetMailNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.NotificationMemberMailNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordStrengthException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; import cz.metacentrum.perun.core.api.exceptions.ResourceNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; @@ -67,6 +68,7 @@ import cz.metacentrum.perun.core.api.SponsoredUserData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; import java.util.Optional; import java.util.stream.Collectors; @@ -1194,47 +1196,36 @@ public Date getNewExtendMembership(PerunSession sess, Vo vo, String loa) throws } @Override - public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException { - + public void sendUsernameReminderEmail(PerunSession sess, Member member, String namespace, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException { Utils.checkPerunSession(sess); getMembersManagerBl().checkMemberExists(sess, member); // Authorization - if (!AuthzResolver.authorizedInternal(sess, "sendPasswordResetLinkEmail_Member_String_String_String_String_policy", member)) { - throw new PrivilegeException(sess, "sendPasswordResetLinkEmail"); + if (!AuthzResolver.authorizedInternal(sess, "sendUsernameReminderEmail_Member_String_String_String_policy", member)) { + throw new PrivilegeException(sess, "sendUsernameReminder"); } - //check if attribute exists, throws AttributeNotExistsException - Attribute mailAttribute = null; - AttributeDefinition ad = getPerunBl().getAttributesManager().getAttributeDefinition(sess, mailAttributeUrn); + String mailAddress = getMailAddressFromAttribute(sess, member, mailAttributeUrn); + getMembersManagerBl().sendUsernameReminderEmail(sess, member, namespace, mailAddress, language); + } + @Override + public void sendPasswordResetLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException { - try { - if (ad.getEntity().equals("user")) { - User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, user, mailAttributeUrn); - } - if (ad.getEntity().equals("member")) { - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, member, mailAttributeUrn); - } - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } + Utils.checkPerunSession(sess); + getMembersManagerBl().checkMemberExists(sess, member); - if (mailAttribute == null) { - throw new InternalErrorException("MailAttribute should not be null."); - } - String mailAddress = mailAttribute.valueAsString(); - if (mailAddress == null) { - throw new PasswordResetMailNotExistsException("Member " + member.getId() + " doesn't have the attribute " + - mailAttributeUrn + " set."); + // Authorization + if (!AuthzResolver.authorizedInternal(sess, "sendPasswordResetLinkEmail_Member_String_String_String_String_policy", member)) { + throw new PrivilegeException(sess, "sendPasswordResetLinkEmail"); } + String mailAddress = getMailAddressFromAttribute(sess, member, mailAttributeUrn); getMembersManagerBl().sendPasswordResetLinkEmail(sess, member, namespace, url, mailAddress, language); } @Override - public void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, UserNotExistsException, AttributeNotExistsException, PasswordResetMailNotExistsException { + public void sendAccountActivationLinkEmail(PerunSession sess, Member member, String namespace, String url, String mailAttributeUrn, String language) throws PrivilegeException, MemberNotExistsException, AttributeNotExistsException, NotificationMemberMailNotExistsException { Utils.checkPerunSession(sess); getMembersManagerBl().checkMemberExists(sess, member); @@ -1243,30 +1234,7 @@ public void sendAccountActivationLinkEmail(PerunSession sess, Member member, Str throw new PrivilegeException(sess, "sendAccountActivationLinkEmail"); } - //check if attribute exists, throws AttributeNotExistsException - Attribute mailAttribute = null; - AttributeDefinition attributeDefinition = getPerunBl().getAttributesManager().getAttributeDefinition(sess, mailAttributeUrn); - - try { - if (attributeDefinition.getEntity().equals("user")) { - User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, user, mailAttributeUrn); - } - if (attributeDefinition.getEntity().equals("member")) { - mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, member, mailAttributeUrn); - } - } catch (WrongAttributeAssignmentException ex) { - throw new InternalErrorException(ex); - } - - if (mailAttribute == null) { - throw new InternalErrorException("MailAttribute should not be null."); - } - String mailAddress = mailAttribute.valueAsString(); - if (mailAddress == null) { - throw new PasswordResetMailNotExistsException("Member " + member.getId() + " doesn't have the attribute " + - mailAttributeUrn + " set."); - } + String mailAddress = getMailAddressFromAttribute(sess, member, mailAttributeUrn); getMembersManagerBl().sendAccountActivationLinkEmail(sess, member, namespace, url, mailAddress, language); } @@ -1663,7 +1631,12 @@ public void removeSponsors(PerunSession sess, Member sponsoredMember, List } @Override - public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException { + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException { + return getMembersPage(sess, vo, query, attrNames, null); + } + + @Override + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, List attrNames, String policy) throws VoNotExistsException, PrivilegeException, GroupNotExistsException, PolicyNotExistsException { Utils.checkPerunSession(sess); perunBl.getVosManagerBl().checkVoExists(sess, vo); @@ -1682,7 +1655,7 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPag throw new IllegalArgumentException("Group status cannot be used to sort VO members."); } - Paginated result = membersManagerBl.getMembersPage(sess, vo, query, attrNames); + Paginated result = membersManagerBl.getMembersPage(sess, vo, query, attrNames, policy); if (query.getGroupId() == null) { result.setData(getPerunBl().getMembersManagerBl().filterOnlyAllowedAttributes(sess, result.getData())); @@ -1823,6 +1796,47 @@ private MemberWithSponsors convertMemberToMemberWithSponsors(PerunSession sess, return memberWithSponsors; } + /** + * Extract mail address from the given attribute and given member object. + * @param sess Perun session + * @param member Member object + * @param mailAttributeUrn URN of the attribute which should contain mail address. Assumes the attribute is of + * type string. + * @return Non-blank attribute value which is expected to be mail address + * @throws AttributeNotExistsException If the attribute specified by mailAttributeUrn parameter does not exist + * @throws NotificationMemberMailNotExistsException If the attribute has empty or blank value + */ + private String getMailAddressFromAttribute(PerunSession sess, Member member, String mailAttributeUrn) + throws AttributeNotExistsException, NotificationMemberMailNotExistsException + { + //check if attribute exists, throws AttributeNotExistsException + Attribute mailAttribute = null; + AttributeDefinition ad = getPerunBl().getAttributesManager().getAttributeDefinition(sess, mailAttributeUrn); + + try { + if (ad.getEntity().equals("user")) { + User user = perunBl.getUsersManagerBl().getUserByMember(sess, member); + mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, user, mailAttributeUrn); + } + if (ad.getEntity().equals("member")) { + mailAttribute = getPerunBl().getAttributesManagerBl().getAttribute(sess, member, mailAttributeUrn); + } + } catch (WrongAttributeAssignmentException ex) { + throw new InternalErrorException(ex); + } + + if (mailAttribute == null) { + throw new InternalErrorException("MailAttribute should not be null."); + } + String mailAddress = mailAttribute.valueAsString(); + if (!StringUtils.hasText(mailAddress)) { + throw new NotificationMemberMailNotExistsException( + "Member " + member.getId() + " doesn't have the attribute " + mailAttributeUrn + " set." + ); + } + return mailAddress; + } + /** * Gets the membersManagerBl for this instance. * diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java index 7576dc84d5..2d7a4eaba5 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/entry/UsersManagerEntry.java @@ -1425,8 +1425,37 @@ public List findRichUsersWithAttributes(PerunSession sess, String sear } } - return getPerunBl().getUsersManagerBl().filterOnlyAllowedAttributes(sess, getUsersManagerBl().findRichUsersWithAttributes(sess, searchString, attrNames)); + List users = getPerunBl().getUsersManagerBl().filterOnlyAllowedAttributes(sess, getUsersManagerBl().findRichUsersWithAttributes(sess, searchString, attrNames)); + return filterRichUsers(sess, users); + } + + /** + * Filters rich users to which the principal has access. + * + * @param sess session + * @param users list of all rich users + * @return filtered list of rich users + */ + private List filterRichUsers(PerunSession sess, List users) { + List result = new ArrayList<>(); + String filterPolicy = "filter-findRichUsersWithAttributes_policy"; + + for (RichUser user : users) { + List vos = perunBl.getUsersManagerBl().getVosWhereUserIsMember(sess, user); + List groups = perunBl.getGroupsManagerBl().getUserGroups(sess, user); + List facilities = perunBl.getFacilitiesManagerBl().getAssignedFacilities(sess, user); + List resources = perunBl.getUsersManagerBl().getAllowedResources(sess, user); + + if (AuthzResolver.authorizedInternal(sess, filterPolicy) || + vos.stream().anyMatch(vo -> AuthzResolver.authorizedInternal(sess, filterPolicy, vo)) || + groups.stream().anyMatch(group -> AuthzResolver.authorizedInternal(sess, filterPolicy, group)) || + facilities.stream().anyMatch(facility -> AuthzResolver.authorizedInternal(sess, filterPolicy, facility)) || + resources.stream().anyMatch(resource -> AuthzResolver.authorizedInternal(sess, filterPolicy, resource))) { + result.add(user); + } + } + return result; } @Override diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java index 6baf947a46..7b422bc3db 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AttributesManagerImpl.java @@ -4168,6 +4168,16 @@ public boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition } } + @Override + public boolean isAttributeActionGloballyCritical(PerunSession sess, int attrId, AttributeAction action) { + try { + return 0 < jdbc.queryForInt("select count(*) from attribute_critical_actions where attr_id=? and action=?::attribute_action and global=true", + attrId, action.toString()); + } catch (RuntimeException ex) { + throw new InternalErrorException(ex); + } + } + @Override public List getCriticalAttributeActions(PerunSession sess, int attrId) { try { @@ -4178,13 +4188,18 @@ public List getCriticalAttributeActions(PerunSession sess, int } @Override - public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException { + public void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException { try { if (critical) { - if (isAttributeActionCritical(sess, attr, action)) { + boolean globalCriticalityChanged = isAttributeActionGloballyCritical(sess, attr.getId(), action) != global; + + if (isAttributeActionCritical(sess, attr, action) && !globalCriticalityChanged) { throw new RelationExistsException("Attribute " + attr.getName() + " is already critical on " + action + " action."); } - jdbc.update("insert into attribute_critical_actions (attr_id, action) values (?,?::attribute_action)", attr.getId(), action.toString()); + + jdbc.update("insert into attribute_critical_actions (attr_id, action, global) values (?,?::attribute_action,?) " + + "on conflict(attr_id, action) do update set global=?", + attr.getId(), action.toString(), global, global); } else { if (0 == jdbc.update("delete from attribute_critical_actions where attr_id=? and action=?::attribute_action", attr.getId(), action.toString())) { throw new RelationNotExistsException("Attribute " + attr.getName() + " is not critical on " + action + " action."); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java index fe9ecca4a9..667e5d924c 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/AuthzResolverImpl.java @@ -8,6 +8,7 @@ import cz.metacentrum.perun.core.api.Member; import cz.metacentrum.perun.core.api.MemberGroupStatus; import cz.metacentrum.perun.core.api.Pair; +import cz.metacentrum.perun.core.api.Perun; import cz.metacentrum.perun.core.api.PerunPolicy; import cz.metacentrum.perun.core.api.PerunSession; import cz.metacentrum.perun.core.api.Resource; @@ -27,6 +28,7 @@ import cz.metacentrum.perun.core.api.exceptions.RoleManagementRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.RoleNotSetException; import cz.metacentrum.perun.core.api.exceptions.UserNotAdminException; +import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; import cz.metacentrum.perun.core.implApi.AuthzResolverImplApi; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -38,6 +40,7 @@ import org.springframework.jdbc.core.SingleColumnRowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import javax.sql.DataSource; import java.util.ArrayList; @@ -449,7 +452,8 @@ public void removeAllAuthzForSecurityTeam(PerunSession sess, SecurityTeam securi @Override public void addAdmin(PerunSession sess, Facility facility, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, facility_id) values (?, (select id from roles where name=?), ?)", user.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId()); + jdbc.update("insert into authz (user_id, role_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin of the facility " + facility, e, user, facility); } catch (RuntimeException e) { @@ -460,7 +464,8 @@ public void addAdmin(PerunSession sess, Facility facility, User user) throws Alr @Override public void addAdmin(PerunSession sess, Facility facility, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, facility_id) values (?, (select id from roles where name=?), ?)", group.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId()); + jdbc.update("insert into authz (authorized_group_id, role_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), Role.FACILITYADMIN.toLowerCase(), facility.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already admin of the facility " + facility, e, group, facility); } catch (RuntimeException e) { @@ -493,7 +498,8 @@ public void removeAdmin(PerunSession sess, Facility facility, Group group) throw @Override public void addAdmin(PerunSession sess, Resource resource, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id) values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?, ?)", user.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin of the resource " + resource, e, user, resource); } catch (RuntimeException e) { @@ -504,7 +510,8 @@ public void addAdmin(PerunSession sess, Resource resource, User user) throws Alr @Override public void addAdmin(PerunSession sess, Resource resource, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, resource_id, vo_id, facility_id) values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (authorized_group_id, role_id, resource_id, vo_id, facility_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?, ?)", group.getId(), Role.RESOURCEADMIN.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already admin of the resource " + resource, e, group, resource); } catch (RuntimeException e) { @@ -537,7 +544,8 @@ public void removeAdmin(PerunSession sess, Resource resource, Group group) throw @Override public void addAdmin(PerunSession sess, User sponsoredUser, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, sponsored_user_id) values (?, (select id from roles where name=?), ?)", user.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId()); + jdbc.update("insert into authz (user_id, role_id, sponsored_user_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already sponsor of the sponsoredUser " + sponsoredUser, e, user, sponsoredUser); } catch (RuntimeException e) { @@ -548,7 +556,8 @@ public void addAdmin(PerunSession sess, User sponsoredUser, User user) throws Al @Override public void addAdmin(PerunSession sess, User sponsoredUser, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, sponsored_user_id) values (?, (select id from roles where name=?), ?)", group.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId()); + jdbc.update("insert into authz (authorized_group_id, role_id, sponsored_user_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), Role.SPONSOR.toLowerCase(), sponsoredUser.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already sponsor of the sponsoredUser " + sponsoredUser, e, group, sponsoredUser); } catch (RuntimeException e) { @@ -582,8 +591,9 @@ public void removeAdmin(PerunSession sess, User sponsoredUser, Group group) thro public void addAdmin(PerunSession sess, Group group, User user) throws AlreadyAdminException { try { // Add GROUPADMIN role + groupId and voId - jdbc.update("insert into authz (user_id, role_id, group_id, vo_id) values (?, (select id from roles where name=?), ?, ?)", - user.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId()); + jdbc.update("insert into authz (user_id, role_id, group_id, vo_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?)", + user.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin in group " + group, e, user, group); } catch (RuntimeException e) { @@ -594,8 +604,9 @@ public void addAdmin(PerunSession sess, Group group, User user) throws AlreadyAd @Override public void addAdmin(PerunSession sess, Group group, Group authorizedGroup) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, group_id, vo_id) values (?, (select id from roles where name=?), ?, ?)", - authorizedGroup.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId()); + jdbc.update("insert into authz (authorized_group_id, role_id, group_id, vo_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?, ?)", + authorizedGroup.getId(), Role.GROUPADMIN.toLowerCase(), group.getId(), group.getVoId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + authorizedGroup.getId() + " is already group admin in group " + group, e, authorizedGroup, group); } catch (RuntimeException e) { @@ -630,8 +641,9 @@ public void removeAdmin(PerunSession sess, Group group, Group authorizedGroup) t @Override public void addAdmin(PerunSession sess, SecurityTeam securityTeam, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id, security_team_id) values (?, (select id from roles where name=?), ?)", user.getId(), - Role.SECURITYADMIN.toLowerCase(), securityTeam.getId()); + jdbc.update("insert into authz (user_id, role_id, security_team_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), + Role.SECURITYADMIN.toLowerCase(), securityTeam.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already admin in securityTeam " + securityTeam, e, user, securityTeam); } catch (RuntimeException e) { @@ -642,8 +654,9 @@ public void addAdmin(PerunSession sess, SecurityTeam securityTeam, User user) th @Override public void addAdmin(PerunSession sess, SecurityTeam securityTeam, Group group) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id, security_team_id) values (?, (select id from roles where name=?), ?)", group.getId(), - Role.SECURITYADMIN.toLowerCase(), securityTeam.getId()); + jdbc.update("insert into authz (authorized_group_id, role_id, security_team_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?, ?)", group.getId(), + Role.SECURITYADMIN.toLowerCase(), securityTeam.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already admin in securityTeam " + securityTeam, e, group, securityTeam); } catch (RuntimeException e) { @@ -676,7 +689,8 @@ public void removeAdmin(PerunSession sess, SecurityTeam securityTeam, Group grou @Override public void makeUserPerunAdmin(PerunSession sess, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id) values (?, (select id from roles where name=?))", user.getId(), Role.PERUNADMIN.toLowerCase()); + jdbc.update("insert into authz (user_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", user.getId(), Role.PERUNADMIN.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already perun admin", e, user, Role.PERUNADMIN); } catch (RuntimeException e) { @@ -687,7 +701,8 @@ public void makeUserPerunAdmin(PerunSession sess, User user) throws AlreadyAdmin @Override public void makeUserPerunObserver(PerunSession sess, User user) throws AlreadyAdminException { try { - jdbc.update("insert into authz (user_id, role_id) values (?, (select id from roles where name=?))", user.getId(), Role.PERUNOBSERVER.toLowerCase()); + jdbc.update("insert into authz (user_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", user.getId(), Role.PERUNOBSERVER.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already perun observer", e, user, Role.PERUNOBSERVER); } catch (RuntimeException e) { @@ -698,7 +713,8 @@ public void makeUserPerunObserver(PerunSession sess, User user) throws AlreadyAd @Override public void makeAuthorizedGroupPerunObserver(PerunSession sess, Group authorizedGroup) throws AlreadyAdminException { try { - jdbc.update("insert into authz (authorized_group_id, role_id) values (?, (select id from roles where name=?))", authorizedGroup.getId(), Role.PERUNOBSERVER.toLowerCase()); + jdbc.update("insert into authz (authorized_group_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", authorizedGroup.getId(), Role.PERUNOBSERVER.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + authorizedGroup.getId() + " is already perun observer", e, authorizedGroup, Role.PERUNOBSERVER); } catch (RuntimeException e) { @@ -742,7 +758,8 @@ public void removePerunObserver(PerunSession sess, User user) throws UserNotAdmi @Override public void makeUserCabinetAdmin(PerunSession sess, User user) { try { - jdbc.update("insert into authz (user_id, role_id) values (?, (select id from roles where name=?))", user.getId(), Role.CABINETADMIN.toLowerCase()); + jdbc.update("insert into authz (user_id, role_id, created_by, created_by_uid)" + + " values (?, (select id from roles where name=?), ?, ?)", user.getId(), Role.CABINETADMIN.toLowerCase(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (RuntimeException e) { throw new InternalErrorException(e); } @@ -765,8 +782,8 @@ public void addVoRole(PerunSession sess, String role, Vo vo, User user) throws A throw new IllegalArgumentException("Role " + role + " cannot be set on VO"); } try { - jdbc.update("insert into authz (user_id, role_id, vo_id) values (?, (select id from roles where name=?), ?)", user.getId(), - role.toLowerCase(), vo.getId()); + jdbc.update("insert into authz (user_id, role_id, vo_id, created_by, created_by_uid) values (?, (select id from roles where name=?), ?, ?, ?)", + user.getId(), role.toLowerCase(), vo.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already " + role + " in vo " + vo, e, user, vo, role); } catch (RuntimeException e) { @@ -780,8 +797,9 @@ public void addVoRole(PerunSession sess, String role, Vo vo, Group group) throws throw new IllegalArgumentException("Role " + role + " cannot be set on VO"); } try { - jdbc.update("insert into authz (role_id, vo_id, authorized_group_id) values ((select id from roles where name=?), ?, ?)", - role.toLowerCase(), vo.getId(), group.getId()); + jdbc.update("insert into authz (role_id, vo_id, authorized_group_id, created_by, created_by_uid)" + + " values ((select id from roles where name=?), ?, ?, ?, ?)", + role.toLowerCase(), vo.getId(), group.getId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already " + role + " in vo " + vo, e, group, vo, role); } catch (RuntimeException e) { @@ -857,8 +875,8 @@ public void addResourceRole(PerunSession sess, User user, String role, Resource throw new InternalErrorException("Role " + role + " cannot be set on resource."); } try { - jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id) values (?, (select id from roles where name=?), ?, ?, ?)", user.getId(), - role.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (user_id, role_id, resource_id, vo_id, facility_id, created_by, created_by_uid) values (?, (select id from roles where name=?), ?, ?, ?, ?, ?)", user.getId(), + role.toLowerCase(), resource.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("User id=" + user.getId() + " is already " + role + " in resource " + resource, e, user, resource, role); } catch (RuntimeException e) { @@ -872,8 +890,9 @@ public void addResourceRole(PerunSession sess, Group group, String role, Resourc throw new IllegalArgumentException("Role " + role + " cannot be set on resource."); } try { - jdbc.update("insert into authz (role_id, resource_id, authorized_group_id, vo_id, facility_id) values ((select id from roles where name=?), ?, ?, ?, ?)", - role.toLowerCase(), resource.getId(), group.getId(), resource.getVoId(), resource.getFacilityId()); + jdbc.update("insert into authz (role_id, resource_id, authorized_group_id, vo_id, facility_id, created_by, created_by_uid)" + + " values ((select id from roles where name=?), ?, ?, ?, ?, ?, ?)", + role.toLowerCase(), resource.getId(), group.getId(), resource.getVoId(), resource.getFacilityId(), sess.getPerunPrincipal().getActor(), sess.getPerunPrincipal().getUserId()); } catch (DataIntegrityViolationException e) { throw new AlreadyAdminException("Group id=" + group.getId() + " is already " + role + " in resource " + resource, e, group, resource, role); } catch (RuntimeException e) { @@ -988,10 +1007,15 @@ public Integer getRoleId(String role) { @Override public void setRole(PerunSession sess, Map mappingOfValues, String role) throws RoleAlreadySetException { - String query = prepareQueryToSetRole(mappingOfValues); + Map genericMappingOfValues = prepareMappingToSetRole(mappingOfValues); + genericMappingOfValues.put("created_by", sess.getPerunPrincipal().getActor()); + genericMappingOfValues.put("created_by_uid", sess.getPerunPrincipal().getUserId()); + SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbc); + insert.withTableName("authz"); + insert.usingColumns(genericMappingOfValues.keySet().toArray(new String[0])); try { - jdbc.update(query); + insert.execute(genericMappingOfValues); } catch (DataIntegrityViolationException e) { throw new RoleAlreadySetException(role); } catch (RuntimeException e) { @@ -1165,12 +1189,8 @@ private MapSqlParameterSource prepareParametersToGetObjectsByUserRoles(User user * @param mappingOfValues from which will be the query created * @return sql query */ - private String prepareQueryToSetRole(Map mappingOfValues) { - String columnsFromMapping; - String valuesFromMapping; - List columnNames = new ArrayList<>(); - List columnValues = new ArrayList<>(); - + private Map prepareMappingToSetRole(Map mappingOfValues) { + Map genericMappingOfValues = new HashMap<>(); for (String columnName : mappingOfValues.keySet()) { if (columnName == null || mappingOfValues.get(columnName) == null) { @@ -1181,14 +1201,10 @@ private String prepareQueryToSetRole(Map mappingOfValues) { if (!matcher.matches()) { throw new InternalErrorException("Cannot create a query to set role, because column name: " + columnName + " contains forbidden characters. Allowed are only [1-9a-zA-Z_]."); } - columnNames.add(columnName); - columnValues.add(mappingOfValues.get(columnName).toString()); + genericMappingOfValues.put(columnName, mappingOfValues.get(columnName)); } - columnsFromMapping = StringUtils.join(columnNames, ","); - valuesFromMapping = StringUtils.join(columnValues, ","); - - return "insert into authz (" + columnsFromMapping + ") values (" + valuesFromMapping + ")"; + return genericMappingOfValues; } /** @@ -1272,4 +1288,39 @@ private String prepareSelectQueryString(Map mappingOfValues) { return StringUtils.join(listOfConditions, " and "); } + + /** + * Returns role id based on its name + * + * @param name - name of the role + * @return role id + */ + @Override + public int getRoleIdByName(String name) { + try { + return jdbc.queryForInt("SELECT id FROM roles WHERE name=?", name.toLowerCase()); + } catch (RuntimeException e) { + throw new InternalErrorException(e); + } + } + + /** + * Returns true if the user in session is vo admin or vo observer of specific vo + * + * @param sess - session + * @param vo - vo + * @return + */ + @Override + public boolean isVoAdminOrObserver(PerunSession sess, Vo vo) { + try { + var query = jdbc.query("SELECT 1 FROM authz WHERE user_id=? AND vo_id=? AND (role_id=? OR role_id=?)", + (rs, i) -> true, + sess.getPerunPrincipal().getUserId(), vo.getId(), AuthzResolverBlImpl.getRoleIdByName(Role.VOADMIN), AuthzResolverBlImpl.getRoleIdByName(Role.VOOBSERVER)); + return !query.isEmpty(); + } catch (InternalErrorException e) { + log.error("Error during checking if user is vo admin of vo {}", vo, e); + } + return false; + } } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java index 81632502e9..4232ba822a 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/MembersManagerImpl.java @@ -10,7 +10,9 @@ import cz.metacentrum.perun.core.api.NamespaceRules; import cz.metacentrum.perun.core.api.Paginated; import cz.metacentrum.perun.core.api.MembersPageQuery; +import cz.metacentrum.perun.core.api.PerunPolicy; import cz.metacentrum.perun.core.api.RichMember; +import cz.metacentrum.perun.core.api.Role; import cz.metacentrum.perun.core.api.Sponsorship; import cz.metacentrum.perun.core.api.MembershipType; import cz.metacentrum.perun.core.api.Pair; @@ -32,9 +34,11 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.PasswordDeletionFailedException; import cz.metacentrum.perun.core.api.exceptions.PasswordOperationTimeoutException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.bl.DatabaseManagerBl; import cz.metacentrum.perun.core.bl.PerunBl; +import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; import cz.metacentrum.perun.core.implApi.MembersManagerImplApi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -730,8 +734,14 @@ public List findMembers(PerunSession sess, Vo vo, String searchString, b return new ArrayList<>(members); } + + @Override + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query) throws PolicyNotExistsException { + return getMembersPage(sess, vo, query, null); + } + @Override - public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query) { + public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, String policy) throws PolicyNotExistsException { Map> attributesToSearchBy = Utils.getDividedAttributes(); MapSqlParameterSource namedParams = Utils.getMapSqlParameterSourceToSearchUsersOrMembers(query.getSearchString(), attributesToSearchBy); @@ -742,17 +752,28 @@ public Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQue namedParams.addValue("voId", vo.getId()); namedParams.addValue("offset", query.getOffset()); namedParams.addValue("limit", query.getPageSize()); + namedParams.addValue("userId", sess.getPerunPrincipal().getUserId()); String statusesQueryString = getVoStatusSQLConditionForMembersPage(query, namedParams); String groupStatusesQueryString = getGroupStatusSQLConditionForMembersPage(query, namedParams); + String whereBasedOnThePolicy = getWhereConditionBasedOnThePolicy(sess, query, policy, vo); + + String groupByQuery = "GROUP BY members.user_id, members.id"; + if (query.getGroupId() == null) { + groupByQuery += query.getSortColumn().getSqlGroupBy(); + } else { + groupByQuery += ", users.last_name, users.first_name, groups_members.group_id, groups_members.source_group_id, groups_members.membership_type, groups_members.source_group_status"; + } + return namedParameterJdbcTemplate.query( select + - " WHERE members.vo_id = (:voId)" + + whereBasedOnThePolicy + statusesQueryString + groupStatusesQueryString + searchQuery + + groupByQuery+ " ORDER BY " + query.getSortColumn().getSqlOrderBy(query) + " OFFSET (:offset)" + " LIMIT (:limit)" @@ -855,7 +876,8 @@ private String getSQLSelectForMembersPage(MembersPageQuery query) { "SELECT " + memberMappingSelectQuery + " ,count(*) OVER() AS total_count" + query.getSortColumn().getSqlSelect() + - " FROM members JOIN users on members.user_id = users.id " + + " FROM members JOIN users ON members.user_id = users.id " + + getSQLBasedOnPolicy() + query.getSortColumn().getSqlJoin(); String groupSelect = @@ -882,6 +904,52 @@ private String getSQLWhereForMembersPage(MembersPageQuery query, MapSqlParameter return " AND " + Utils.prepareSqlWhereForUserMemberSearch(query.getSearchString(), namedParams, false); } + private String getSQLBasedOnPolicy() { + return "LEFT OUTER JOIN (SELECT groups_members.member_id, authz.role_id, authz.vo_id" + + " FROM groups" + + " JOIN authz ON groups.id = authz.group_id" + + " JOIN groups_members ON groups.id = groups_members.group_id" + + " WHERE authz.user_id = (:userId))" + + " AS members_group ON members.id = members_group.member_id AND members.vo_id = members_group.vo_id"; + } + + private String getWhereConditionBasedOnThePolicy(PerunSession sess, MembersPageQuery query, String otherPolicy, Vo vo) throws PolicyNotExistsException { + String defaultWhereCondition = " WHERE members.vo_id = (:voId)"; + PerunPolicy policy = AuthzResolverImpl.getPerunPolicy("filter-getMembersPage_policy"); + if (otherPolicy != null && !otherPolicy.isEmpty()) { + policy = AuthzResolverImpl.getPerunPolicy(otherPolicy); + } + + // Check if user is VO admin in vo + boolean ignoreGroupRelation = AuthzResolverBlImpl.isPerunAdmin(sess) || AuthzResolverBlImpl.isPerunObserver(sess) || AuthzResolverBlImpl.isVoAdminOrObserver(sess, vo); + if (query.getGroupId() != null || ignoreGroupRelation) { + return defaultWhereCondition; + } + + List roles = new ArrayList<>(); + for (Map role : policy.getPerunRoles()) { + for (Map.Entry entry : role.entrySet()) { + // Do nothing + if (entry.getValue() == null) continue; + if (entry.getValue().equals("Group")) { + int roleId = AuthzResolverBlImpl.getRoleIdByName(entry.getKey()); + if (roleId == -1) { + log.error("Role {} not found in DB.", entry.getKey()); + continue; + } + roles.add("members_group.role_id=" + roleId); + } + } + } + + if (roles.isEmpty() ) { + return defaultWhereCondition; + } + return " WHERE members.vo_id = (:voId) AND (" + + String.join(" OR ", roles) + + ")"; + } + private String getVoStatusSQLConditionForMembersPage(MembersPageQuery query, MapSqlParameterSource namedParams) { String statusesQueryString = ""; if (query.getStatuses() != null && !query.getStatuses().isEmpty()) { @@ -909,5 +977,4 @@ private String getGroupStatusSQLConditionForMembersPage(MembersPageQuery query, } return groupStatusesQueryString; } - } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java index adfb0fbf6f..9563adffe2 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/ServicesManagerImpl.java @@ -37,6 +37,7 @@ import javax.sql.DataSource; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -93,6 +94,15 @@ public ServicesManagerImpl(DataSource perunPool) { serviceDenialMappingSelectQuery + ", " + "facility_service_destinations.propagation_type as f_s_des_propagation_type "; + public final static String richDestinationWithLastSuccessfulPropagationMappingSelectQuery = " " + destinationMappingSelectQuery + ", " + + "facilities.id as facilities_id, facilities.name as facilities_name, facilities.dsc as facilities_dsc, " + + "facilities.created_at as facilities_created_at, facilities.created_by as facilities_created_by, facilities.modified_at as facilities_modified_at, facilities.modified_by as facilities_modified_by, " + + "facilities.modified_by_uid as facilities_modified_by_uid, facilities.created_by_uid as facilities_created_by_uid, " + + serviceMappingSelectQuery + ", " + + serviceDenialMappingSelectQuery + ", " + + "facility_service_destinations.propagation_type as f_s_des_propagation_type, " + + "last_success.success_at as success_at "; + public static final RowMapper SERVICE_MAPPER = (resultSet, i) -> { Service service = new Service(); service.setId(resultSet.getInt("services_id")); @@ -212,7 +222,15 @@ public ServicesManagerImpl(DataSource perunPool) { ServiceDenial serviceDenial = SERVICE_DENIAL_MAPPER.mapRow(resultSet, i); - return new RichDestination(destination, facility, service, serviceDenial != null); + // if success_at column is missing in results, use null value + Timestamp lastSuccessfulPropagation; + try { + lastSuccessfulPropagation = resultSet.getTimestamp("success_at"); + } catch (SQLException ex) { + lastSuccessfulPropagation = null; + } + + return new RichDestination(destination, facility, service, serviceDenial != null, lastSuccessfulPropagation); }; @SuppressWarnings("ConstantConditions") @@ -750,12 +768,19 @@ public List getDestinations(PerunSession perunSession, Facility fac @Override public List getAllRichDestinations(PerunSession perunSession, Facility facility) { try { - return jdbc.query("select " + richDestinationMappingSelectQuery + " from facility_service_destinations " + + return jdbc.query("select " + richDestinationWithLastSuccessfulPropagationMappingSelectQuery + " from facility_service_destinations " + "join destinations on destinations.id=facility_service_destinations.destination_id " + "join services on services.id=facility_service_destinations.service_id " + "join facilities on facilities.id=facility_service_destinations.facility_id " + "left join service_denials on services.id = service_denials.service_id and " + " destinations.id = service_denials.destination_id " + + + "left join (select destination_id, services.id as service_id, facilities.id as facility_id, max(timestamp) as success_at from tasks_results " + + "join services on services.id = (select service_id from tasks where id = tasks_results.task_id) " + + "join facilities on facilities.id = (select facility_id from tasks where id = tasks_results.task_id) " + + "where status = 'DONE' group by destination_id, services.id, facilities.id) as last_success " + + " on last_success.destination_id = facility_service_destinations.destination_id and last_success.service_id = services.id and last_success.facility_id = facilities.id " + + "where facility_service_destinations.facility_id=? order by destinations.destination", RICH_DESTINATION_MAPPER, facility.getId()); } catch (RuntimeException e) { throw new InternalErrorException(e); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java index e51530f784..9f6e74576f 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/Utils.java @@ -244,10 +244,9 @@ public static List extractAdditionalUserExtSources(PerunSessi } /** - * Returns loa of addtional ues, if not stated, returns 0. If integer cannot be parsed from input, ParserException is thrown. + * Returns loa of additional ues, if not stated, returns 0. If integer cannot be parsed from input, ParserException is thrown. * Used in extractAdditionalUserExtSources to get ues LoA. * - * @param login login of subject * @param userExtSourceRaw array containing LoA * @return int LoA */ @@ -627,11 +626,12 @@ public static User createUserFromNameMap(Map name) { /** * Creates a new instance of User with names initialized from parsed rawName. - * Imposes limit on leghts of fields. - * @see #parseCommonName(String) + * Imposes limit on lengths of fields. + * + * @see Utils#parseCommonName(String, boolean) * @param rawName raw name * @param fullNameRequired if true, throw exception if firstName or lastName is missing, do not throw exception otherwise - * @return user + * @return User */ public static User parseUserFromCommonName(String rawName, boolean fullNameRequired) { Map m = parseCommonName(rawName, fullNameRequired); @@ -639,7 +639,7 @@ public static User parseUserFromCommonName(String rawName, boolean fullNameRequi } /** - * @see Utils.parseCommonName(String rawName, boolean fullNameRequired) - where fullNameRequired is false + * @see Utils#parseCommonName(String, boolean) */ public static Map parseCommonName(String rawName) { try { @@ -1071,6 +1071,41 @@ public static void sendValidationEmail(User user, String url, String email, UUID sendEmail(subject, content, email); } + /** + * Sends email with reminder of the username in the specified namespace to the user + * @param user user to send notification for + * @param email user's email to send notification to + * @param login user's login which will be used in message, if tag {@code {login}} is used. + * @param namespace namespace to reset password in + * @param messageTemplate message of the email (uses default if null) + * @param subject subject of the email (uses default if null) + */ + public static void sendUsernameReminderEmail(User user, String email, String login, String namespace, String messageTemplate, String subject) { + String instanceName = BeansUtils.getCoreConfig().getInstanceName(); + + String defaultSubject = "[" + instanceName + "] Username reminder for namespace: " + namespace; + String defaultBody = "Dear " + user.getDisplayName() + ",\n\n" + + "\n\nWe've received request to remind you your username for namespace \"" + namespace + "\"." + + "\nYour username is: " + login + + "\n\nMessage is automatically generated." + + "\n----------------------------------------------------------------" + + "\nPerun - Identity & Access Management System"; + + Map subjectParametersToReplace = new HashMap<>(); + subjectParametersToReplace.put("{instanceName}", instanceName); + subjectParametersToReplace.put("{namespace}", namespace); + subject = prepareSubjectOfEmail(subject, defaultSubject, subjectParametersToReplace); + + Map bodyParametersToReplace = new HashMap<>(); + bodyParametersToReplace.put("{displayName}", user.getDisplayName()); + bodyParametersToReplace.put("{namespace}", namespace); + bodyParametersToReplace.put("{login}", login); + + messageTemplate = prepareBodyOfEmail(messageTemplate, defaultBody, bodyParametersToReplace); + + sendEmail(subject, messageTemplate, email); + } + /** * Sends email with link to non-authz account activation where user can activate his account by setting a password. * @param user user to send notification for diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettings.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettings.java new file mode 100644 index 0000000000..6520c5f626 --- /dev/null +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettings.java @@ -0,0 +1,190 @@ +package cz.metacentrum.perun.core.impl.modules.attributes; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.impl.PerunSessionImpl; +import cz.metacentrum.perun.core.implApi.modules.attributes.UserAttributesModuleAbstract; +import cz.metacentrum.perun.core.implApi.modules.attributes.UserAttributesModuleImplApi; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + + +/** + * Check if value of mfaEnforceSetting attribute is valid + */ +public class urn_perun_user_attribute_def_def_mfaEnforceSettings extends UserAttributesModuleAbstract implements UserAttributesModuleImplApi { + + + /** + * Attribute value should be a valid JSON. + * These specific values are allowed: + * empty string or null + * {"all":true} + * {"include_categories":["str1","str2"]} + * {"include_categories":["str1","str2"],"exclude_rps":["rp1","rp2"]} + * + * @param perunSession PerunSession + * @param user User + * @param attribute Attribute of the user. + * + * @throws WrongAttributeValueException + */ + @Override + public void checkAttributeSyntax(PerunSessionImpl perunSession, User user, Attribute attribute) throws WrongAttributeValueException { + String val = attribute.valueAsString(); + + // Null or empty string are allowed + if (val == null || val.isEmpty()) return; + + // Should be string in valid JSON format + try { + final ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(val); + + // Check "all" + if (root.has("all") && root.size() == 1) return; + + int validNodes = 0; + // Check "include_categories" + if (root.has("include_categories")) { + isStringArrayNode(root.get("include_categories"), "include_categories"); + validNodes += 1; + + // Check "exclude_rps" + if (root.has("exclude_rps")) { + isStringArrayNode(root.get("exclude_rps"), "exclude_rps"); + validNodes += 1; + } + } + + // Check for NO additional nodes + if (root.size() == validNodes) return; + + throw new WrongAttributeValueException( + "Attribute value " + val + " has incorrect format." + + " Allowed values are:" + + " empty string or null," + + " {\"all\":true}," + + " {\"include_categories\":[\"str1\",\"str2\"]}," + + " {\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"rp1\",\"rp2\"]}"); + } catch (JsonProcessingException e) { + throw new WrongAttributeValueException("Attribute value " + val + " is not a valid JSON."); + } + } + + /** + * Checks that node is an array and all values are strings + * + * @param node JsonNode + * @throws WrongAttributeValueException + */ + private void isStringArrayNode(JsonNode node, String name) throws WrongAttributeValueException { + // Check property is valid array + if (node.isArray()) { + // Check all items of array are string like + for (Iterator it = node.elements(); it.hasNext(); ) { + JsonNode value = it.next(); + if (!value.isTextual()) { + throw new WrongAttributeValueException("Property '" + name + "' has non textual value " + value); + } + } + } else { + throw new WrongAttributeValueException("Property '" + name + "' is not an array."); + } + } + + /** + * The following restrictions are placed on the attribute value: + * {"include_categories":["str1","str2"]} str1, str2 is an existing key in the entityless attribute mfaCategories + * {"include_categories":["str1","str2"],"exclude_rps":["rp1","rp2"]} str1, str2 is an existing key in the entityless attribute mfaCategories and rp1, rp2 must exist inside the category + * + * @param perunSession PerunSession + * @param user User + * @param attribute Attribute of the user. + * + * @throws WrongReferenceAttributeValueException + * @throws WrongAttributeAssignmentException + */ + @Override + public void checkAttributeSemantics(PerunSessionImpl perunSession, User user, Attribute attribute) throws WrongReferenceAttributeValueException, WrongAttributeAssignmentException { + String val = attribute.valueAsString(); + if (val == null || val.isEmpty()) return; + + Set includeCategories = null; + Set excludeRps = null; + try { + final ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(val); + + if (root.has("all")) { + if (root.get("all").asBoolean()) return; + throw new WrongAttributeAssignmentException("Property 'all' is only valid with true ({\"all\":true})."); + } + + // Initialize included categories to check + includeCategories = new HashSet<>(); + fillSet(root.get("include_categories"), includeCategories); + + // Initialize excluded rps to check (optional) + excludeRps = new HashSet<>(); + if (root.has("exclude_rps")) { + fillSet(root.get("exclude_rps"), excludeRps); + } + } catch (JsonProcessingException e) { + throw new WrongAttributeAssignmentException("Attribute " + attribute + "is incorrectly assigned."); + } + + Attribute mfaCategories = perunSession.getPerunBl().getAttributesManagerBl().getEntitylessAttributes(perunSession, "mfaCategories").get(0); + String mfaSettingsValue = mfaCategories.valueAsString(); + try { + final ObjectMapper mapper = new ObjectMapper(); + JsonNode mfaCategoriesNode = mapper.readTree(mfaSettingsValue).get("categories"); + + // Iterate through categories and check that all included categories exist + for (Iterator> catIt = mfaCategoriesNode.fields(); catIt.hasNext(); ) { + Map.Entry catEntry = catIt.next(); + + boolean checkRps = includeCategories.remove(catEntry.getKey()); + if (checkRps) { + // Iterate through rps and check that all excluded rps exist + JsonNode rps = catEntry.getValue().get("rps"); + for (Iterator> rpsIt = rps.fields(); rpsIt.hasNext(); ) { + Map.Entry rp = rpsIt.next(); + excludeRps.remove(rp.getKey()); + } + } + } + + // Both sets should be empty + if (!includeCategories.isEmpty()) { + throw new WrongReferenceAttributeValueException("Categories " + includeCategories + " do not exist inside mfaCategories attribute."); + } + if (!excludeRps.isEmpty()) { + throw new WrongReferenceAttributeValueException("Rps " + excludeRps + " do not exist inside included categories in mfaCategories attribute."); + } + } catch (JsonProcessingException e) { + throw new WrongAttributeAssignmentException("Attribute " + mfaCategories + "is incorrectly assigned."); + } + } + + /** + * Add all elements of node to a set + * + * @param node JsonNode + * @param set HashSet + */ + private void fillSet(JsonNode node, Set set) { + for (Iterator it = node.elements(); it.hasNext(); ) { + JsonNode next = it.next(); + set.add(next.textValue()); + } + } +} diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java index 089334e145..1fbfd0a857 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_virt_userEligibilities.java @@ -82,45 +82,6 @@ public Attribute getAttributeValue(PerunSessionImpl sess, User user, AttributeDe return attribute; } -// @Override -// public List getHandleIdentifiers() { -// List handleIdentifiers = super.getHandleIdentifiers(); -// handleIdentifiers.add(auditEvent -> { -// if (auditEvent instanceof AttributeChangedForUser && -// ((AttributeChangedForUser) auditEvent).getAttribute().getFriendlyName().equals(getDestinationAttributeFriendlyName())) { -// return ((AttributeChangedForUser) auditEvent).getUser().getId(); -// } else { -// return null; -// } -// }); -// return handleIdentifiers; -// } -// -// @Override -// public List resolveVirtualAttributeValueChange(PerunSessionImpl perunSession, AuditEvent message) throws AttributeNotExistsException, WrongAttributeAssignmentException, WrongReferenceAttributeValueException { -// List resolvingMessages = super.resolveVirtualAttributeValueChange(perunSession, message); -// if (message == null) return resolvingMessages; -// -// if (message instanceof AttributeSetForUser && -// ((AttributeSetForUser) message).getAttribute().getFriendlyName().equals(getSecondarySourceAttributeFriendlyName())) { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// resolvingMessages.add(new AttributeSetForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeSetForUser) message).getUser())); -// } else if (message instanceof AttributeChangedForUser) { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// resolvingMessages.add(new AttributeChangedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeChangedForUser) message).getUser())); -// } else if (message instanceof AttributeRemovedForUser) { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// resolvingMessages.add(new AttributeRemovedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), ((AttributeRemovedForUser) message).getUser())); -// } -// -// return resolvingMessages; -// } - -// private AuditEvent resolveEvent(PerunSessionImpl perunSession, User user) throws AttributeNotExistsException { -// AttributeDefinition attrVirtUserEligibilitiesDefinition = perunSession.getPerunBl().getAttributesManagerBl().getAttributeDefinition(perunSession, A_U_V_USER_ELIGIBILITIES); -// return new AttributeChangedForUser(new Attribute(attrVirtUserEligibilitiesDefinition), user); -// } - @Override public AttributeDefinition getAttributeDefinition() { AttributeDefinition attr = new AttributeDefinition(); diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java index 581c8f458f..6c862d0a6e 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AttributesManagerImplApi.java @@ -2775,6 +2775,16 @@ public interface AttributesManagerImplApi { */ boolean isAttributeActionCritical(PerunSession sess, AttributeDefinition attr, AttributeAction action); + /** + * Checks if the action is critical on given attribute for all objects. + * + * @param sess session + * @param attrId attribute definition id + * @param action critical action + * @return true if action is globally critical, false otherwise + */ + boolean isAttributeActionGloballyCritical(PerunSession sess, int attrId, AttributeAction action); + /** * Returns critical actions on given attribute. * @@ -2792,11 +2802,12 @@ public interface AttributesManagerImplApi { * @param attr attribute definition * @param action critical action * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects * * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action */ - void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical) throws RelationExistsException, RelationNotExistsException; + void setAttributeActionCriticality(PerunSession sess, AttributeDefinition attr, AttributeAction action, boolean critical, boolean global) throws RelationExistsException, RelationNotExistsException; /** * Returns list of all possible namespaces. diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java index f2aa3226a3..679f4cbf49 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/AuthzResolverImplApi.java @@ -3,6 +3,7 @@ import cz.metacentrum.perun.core.api.Facility; import cz.metacentrum.perun.core.api.Group; import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.PerunPolicy; import cz.metacentrum.perun.core.api.PerunSession; import cz.metacentrum.perun.core.api.Resource; import cz.metacentrum.perun.core.api.SecurityTeam; @@ -651,4 +652,20 @@ public interface AuthzResolverImplApi { */ Set getSecurityTeamsWhereUserIsInRoles(User user, List roles); + /** + * Get role id by its name, returns -1 if role does not exist. + * + * @param name - name of the role + * @return - role id with the given name + */ + int getRoleIdByName(String name); + + /** + * Returns true if the user in session is vo admin or vo observer of specific Vo. + * + * @param sess - session + * @param vo - vo + * @return bolean + */ + boolean isVoAdminOrObserver(PerunSession sess, Vo vo); } diff --git a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java index 96c3bf2af9..737d2ef42d 100644 --- a/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java +++ b/perun-core/src/main/java/cz/metacentrum/perun/core/implApi/MembersManagerImplApi.java @@ -19,6 +19,7 @@ import cz.metacentrum.perun.core.api.exceptions.MemberAlreadyRemovedException; import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.PolicyNotExistsException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.impl.LoginNamespacesRulesConfigLoader; @@ -408,9 +409,20 @@ public interface MembersManagerImplApi { * @param sess session * @param vo vo * @param query query with page information + * @param policy policy to replace the default one (`filter_getMembersPage-policy`) * @return page of requested rich members */ - Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query); + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query, String policy) throws PolicyNotExistsException; + + /** + * Get page of members from the given vo + * + * @param sess session + * @param vo vo + * @param query query with page information + * @return page of requested rich members + */ + Paginated getMembersPage(PerunSession sess, Vo vo, MembersPageQuery query) throws PolicyNotExistsException; /** * Update the sponsorship of given member for given sponsor. diff --git a/perun-core/src/main/resources/postgresChangelog.txt b/perun-core/src/main/resources/postgresChangelog.txt index 4e1c5feaa1..02666d4f51 100644 --- a/perun-core/src/main/resources/postgresChangelog.txt +++ b/perun-core/src/main/resources/postgresChangelog.txt @@ -6,6 +6,15 @@ -- Directly under version number should be version commands. They will be executed in the order they are written here. -- Comments are prefixed with -- and can be written only between version blocks, that means not in the lines with commands. They have to be at the start of the line. +3.2.17 +ALTER TABLE attribute_critical_actions ADD COLUMN global boolean default false not null; +UPDATE configurations SET value='3.2.17' WHERE property='DATABASE VERSION'; + +3.2.16 +ALTER TABLE authz ADD COLUMN created_at timestamp default statement_timestamp() not null; +ALTER TABLE authz ADD column created_by varchar default user not null; +UPDATE configurations set value='3.2.16' WHERE property='DATABASE VERSION'; + 3.2.15 INSERT INTO authz (user_id, role_id) VALUES ((select user_id from user_ext_sources where login_ext='perun' and ext_sources_id=(select id from ext_sources where name='INTERNAL' and type='cz.metacentrum.perun.core.impl.ExtSourceInternal')),(select id from roles where name='perunadmin')) ON CONFLICT DO NOTHING; UPDATE configurations set value='3.2.15' WHERE property='DATABASE VERSION'; diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java index 8ac4f80c4b..ae519a8beb 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/api/AuthzResolverIntegrationTest.java @@ -1200,6 +1200,7 @@ public void setRoleGroupAdminSucceedsForVoAdmin() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); @@ -1249,6 +1250,7 @@ public void getVoAdminGroupsWithProperRights() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); AuthzResolver.setRole(testSession, testGroup, testVo, Role.VOADMIN); @@ -1287,6 +1289,7 @@ public void getVoDirectRichAdminsWithProperRights() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); AuthzResolver.setRole(testSession, testUser, testVo, Role.VOADMIN); @@ -1331,6 +1334,7 @@ public void getVoRichAdminsWithProperRights() throws Exception { when(mockedPerunPrincipal.isAuthzInitialized()).thenReturn(true); when(mockedPerunPrincipal.getRoles()).thenReturn(new AuthzRoles(Role.VOADMIN, testVo)); when(mockedPerunPrincipal.getRolesUpdatedAt()).thenReturn(System.currentTimeMillis()); + when(mockedPerunPrincipal.getActor()).thenReturn("test"); PerunSession testSession = new PerunSessionImpl(sess.getPerun(), mockedPerunPrincipal, sess.getPerunClient()); AuthzResolver.setRole(testSession, testUser, testVo, Role.VOADMIN); diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java index 15abaf2715..6bee2a4d7d 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/AttributesManagerEntryIntegrationTest.java @@ -10013,9 +10013,9 @@ public void unmarkWriteActionAsCritical() throws Exception { Attribute attribute = setUpMemberGroupAttribute().get(0); assertTrue("Writing to attribute should be critical by default", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.WRITE)); - assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions()).containsExactly(AttributeAction.valueOf("WRITE")); + assertTrue(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions().containsKey(AttributeAction.WRITE)); - perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, false); + perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, false, false); assertFalse("Writing to attribute should not be critical", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.WRITE)); assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions()).isEmpty(); } @@ -10029,13 +10029,37 @@ public void setCriticalOperations() throws Exception { member = setUpMember(); Attribute attribute = setUpMemberGroupAttribute().get(0); - assertThrows(RelationExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true)); - assertThrows(RelationNotExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, false)); + assertThrows(RelationExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true, false)); + assertThrows(RelationNotExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, false, false)); assertFalse(perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); - perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, true); + perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, true, false); assertTrue("Reading attribute should be critical", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); - assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions()).containsExactlyInAnyOrder(AttributeAction.WRITE, AttributeAction.READ); + assertThat(perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions() + .keySet()).containsExactlyInAnyOrder(AttributeAction.WRITE, AttributeAction.READ); + } + + @Test + public void setGloballyCriticalOperations() throws Exception { + System.out.println(CLASS_NAME + "setGloballyCriticalOperations"); + + vo = setUpVo(); + group = setUpGroup(); + member = setUpMember(); + Attribute attribute = setUpMemberGroupAttribute().get(0); + + assertThrows(RelationExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.WRITE, true, false)); + assertThrows(RelationNotExistsException.class, () -> perun.getAttributesManagerBl().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, false, false)); + + assertFalse(perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); + perun.getAttributesManager().setAttributeActionCriticality(sess, attribute, AttributeAction.READ, true, true); + assertTrue("This attribute should be marked as globally critical", perun.getAttributesManagerBl().isAttributeActionGloballyCritical(sess, attribute, AttributeAction.READ)); + assertTrue("Reading attribute should be critical", perun.getAttributesManagerBl().isAttributeActionCritical(sess, attribute, AttributeAction.READ)); + + Map criticalActionMap = perun.getAttributesManager().getAttributeRules(sess, attribute.getId()).getCriticalActions(); + assertThat(criticalActionMap.keySet()).containsExactlyInAnyOrder(AttributeAction.WRITE, AttributeAction.READ); + assertTrue(criticalActionMap.containsKey(AttributeAction.WRITE) && !criticalActionMap.get(AttributeAction.WRITE)); + assertTrue(criticalActionMap.containsKey(AttributeAction.READ) && criticalActionMap.get(AttributeAction.READ)); } @Test diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java index 3997b29a35..70b1fd94ee 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/MembersManagerEntryIntegrationTest.java @@ -4,6 +4,7 @@ import cz.metacentrum.perun.core.api.Attribute; import cz.metacentrum.perun.core.api.AttributeDefinition; import cz.metacentrum.perun.core.api.AttributesManager; +import cz.metacentrum.perun.core.api.AuthzResolver; import cz.metacentrum.perun.core.api.BanOnResource; import cz.metacentrum.perun.core.api.BanOnVo; import cz.metacentrum.perun.core.api.BeansUtils; @@ -21,6 +22,8 @@ import cz.metacentrum.perun.core.api.MembersOrderColumn; import cz.metacentrum.perun.core.api.MembershipType; import cz.metacentrum.perun.core.api.NamespaceRules; +import cz.metacentrum.perun.core.api.PerunClient; +import cz.metacentrum.perun.core.api.PerunPrincipal; import cz.metacentrum.perun.core.api.RichUser; import cz.metacentrum.perun.core.api.SortingOrder; import cz.metacentrum.perun.core.api.Paginated; @@ -40,6 +43,7 @@ import cz.metacentrum.perun.core.api.Validation; import cz.metacentrum.perun.core.api.Vo; import cz.metacentrum.perun.core.api.VosManager; +import cz.metacentrum.perun.core.api.exceptions.AlreadyAdminException; import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; import cz.metacentrum.perun.core.api.exceptions.AlreadySponsorException; import cz.metacentrum.perun.core.api.exceptions.AlreadySponsoredMemberException; @@ -51,6 +55,7 @@ import cz.metacentrum.perun.core.api.exceptions.NamespaceRulesNotExistsException; import cz.metacentrum.perun.core.api.exceptions.ParseUserNameException; import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; +import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException; import cz.metacentrum.perun.core.api.exceptions.SponsorshipDoesNotExistException; import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; import cz.metacentrum.perun.core.api.exceptions.UserNotInRoleException; @@ -58,6 +63,7 @@ import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; import cz.metacentrum.perun.core.blImpl.AuthzResolverBlImpl; +import cz.metacentrum.perun.core.impl.AuthzRoles; import cz.metacentrum.perun.core.implApi.modules.attributes.AbstractMembershipExpirationRulesModule; import cz.metacentrum.perun.core.api.SponsoredUserData; import org.junit.Before; @@ -74,7 +80,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static cz.metacentrum.perun.core.blImpl.VosManagerBlImpl.A_MEMBER_DEF_MEMBER_ORGANIZATIONS; import static cz.metacentrum.perun.core.impl.modules.attributes.urn_perun_vo_attribute_def_def_membershipExpirationRules.VO_EXPIRATION_RULES_ATTR; @@ -3354,6 +3359,346 @@ public void getGroupMembersPageWithMemberGroupAttribute() throws Exception { } + @Test + public void getMemberPageBasedOnPolicy() throws Exception { + System.out.println(CLASS_NAME + "getMemberPageBasedOnPolicy"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member4 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group, Role.GROUPADMIN); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(2); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId()); + } + @Test + public void getMembersPageBasedOnPolicyNoDuplicities() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyNoDuplicities"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.GROUPADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.GROUPOBSERVER); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(2); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member3.getId(), member4.getId()); + } + + @Test + public void getMembersPageBasedOnPolicyPerunObserver() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyVoAdmin"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, null, Role.PERUNOBSERVER); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(5); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + + @Test + public void getMembersPageBasedOnPolicyVoAdmin() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyVoAdmin"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.VOADMIN); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy-vo"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(5); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + + @Test + public void getMembersPageBasedOnPolicyVoObserver() throws Exception { + System.out.println(CLASS_NAME + "getMembersPageBasedOnPolicyVoAdmin"); + + // Create a new session + PerunPrincipal pp = new PerunPrincipal("perunTestsPagination", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + PerunSession sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Hacky way to mock things + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + // Create a new user, replace perun principal with it + User user = new User(); + user.setFirstName("perunTestsPagination"); + user.setLastName("perunTestsPagination"); + user = perun.getUsersManagerBl().createUser(sess2, user); + + pp.setUser(user); + sess2 = perun.getPerunSession(pp, new PerunClient()); + + // Setup user as a PERUNADMIN + AuthzResolver.setRole(sess2, user, null, Role.PERUNADMIN); + + Vo vo = perun.getVosManager().createVo(sess2, new Vo(0, "testPagination", "tp")); + Group group = perun.getGroupsManager().createGroup(sess2, vo, new Group("test", "testPaginationInGroup")); + Group group2 = perun.getGroupsManager().createGroup(sess2, vo, new Group("test2", "testPaginationInGroup2")); + + // Create 2 members, for each group + Member member = setupMemberInSession(sess2, vo, "Doe", "John"); + Member member2 = setupMemberInSession(sess2, vo, "Doe", "Jane"); + Member member3 = setupMemberInSession(sess2, vo, "Perun", "Ján"); + Member member4 = setupMemberInSession(sess2, vo, "Buck", "Mister"); + + perun.getGroupsManager().addMember(sess2, group, member); + perun.getGroupsManager().addMember(sess2, group, member2); + perun.getGroupsManager().addMember(sess2, group2, member3); + perun.getGroupsManager().addMember(sess2, group2, member4); + + // Create a new session, set u2 as a PERUNADMIN and GROUPADMIN + Member member5 = setupMemberInSession(sess2, vo, "Doe", "John"); + User u2 = perun.getUsersManager().getUserByMember(sess2, member5); + + pp = new PerunPrincipal("perunTestsPagination2", ExtSourcesManager.EXTSOURCE_NAME_INTERNAL, ExtSourcesManager.EXTSOURCE_INTERNAL); + pp.setUser(u2); + + sess2 = perun.getPerunSession(pp, new PerunClient()); + sess2.getPerunPrincipal().setRoles(new AuthzRoles(Role.PERUNADMIN)); + sess2.getPerunPrincipal().setAuthzInitialized(true); + + AuthzResolver.setRole(sess2, u2, null, Role.PERUNADMIN); + AuthzResolver.setRole(sess2, u2, group2, Role.VOOBSERVER); + + // Unset PERUNADMIN for u2 + AuthzResolver.unsetRole(sess2, u2, null, Role.PERUNADMIN); + + // Call getMembersPage with to get all Members in a VO + MembersPageQuery query = new MembersPageQuery(10, 0, SortingOrder.ASCENDING, MembersOrderColumn.NAME, "", List.of(), null, List.of()); + Paginated result = perun.getMembersManager().getMembersPage(sess2, vo, query, List.of(), "test_filter-getMembersPage_policy-voobserver"); + List returnedMemberIds = result.getData().stream() + .map(PerunBean::getId) + .collect(toList()); + + // Check returned members + assertThat(returnedMemberIds.size()).isEqualTo(5); + assertThat(returnedMemberIds).containsExactlyInAnyOrder(member.getId(), member2.getId(), member3.getId(), member4.getId(), member5.getId()); + } + @Test public void getAllMembers_membersFromTwoVos() throws Exception { System.out.println(CLASS_NAME + "getAllMembers_membersFromTwoVos"); @@ -3561,6 +3906,14 @@ private Vo setUpVo(String name) throws Exception { return perun.getVosManagerBl().createVo(sess, new Vo(0, name, name)); } + private Member setupMemberInSession(PerunSession sess, Vo vo, String lastName, String firstName) throws Exception { + User user = new User(); + user.setFirstName(firstName); + user.setLastName(lastName); + user = perun.getUsersManagerBl().createUser(sess, user); + return perun.getMembersManagerBl().createMember(sess, vo, user); + } + private Member setUpMember(Vo vo, String lastName, String firstName) throws Exception { User user = new User(); user.setFirstName(firstName); diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java index a79401ba40..2ff13ff890 100644 --- a/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/entry/ServicesManagerEntryIntegrationTest.java @@ -42,9 +42,12 @@ import cz.metacentrum.perun.core.api.exceptions.ServicesPackageExistsException; import cz.metacentrum.perun.core.api.exceptions.ServicesPackageNotExistsException; import cz.metacentrum.perun.core.impl.AuthzRoles; +import cz.metacentrum.perun.taskslib.model.Task; +import cz.metacentrum.perun.taskslib.model.TaskResult; import org.assertj.core.api.Assertions; import org.junit.Test; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -56,7 +59,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; /** @@ -1271,6 +1273,43 @@ public void getAllRichDestinationsWithFacility() throws Exception { assertTrue("there is the right destination in the richDestination",richDestination.getDestination().equals(destination.getDestination())); } + @Test + public void getAllRichDestinationsAndTheirLastSuccessfulPropagation() throws Exception { + System.out.println(CLASS_NAME + "getAllRichDestinationsAndTheirLastSuccessfulPropagation"); + + Timestamp success_at = new Timestamp(System.currentTimeMillis()); + + service = setUpService(); + facility = setUpFacility(); + destination = setUpDestination(); + perun.getServicesManagerBl().addDestination(sess, service, facility, destination); + + // Task + Task task = new Task(); + task.setFacility(facility); + task.setService(service); + task.setSchedule(0L); + task.setStatus(Task.TaskStatus.WARNING); + task.setDestinations(List.of(destination)); + task.setId(perun.getTasksManagerBl().insertTask(sess, task)); + + // Task result + TaskResult result = new TaskResult(); + result.setDestination(destination); + result.setDestinationId(destination.getId()); + result.setService(service); + result.setTaskId(task.getId()); + result.setStatus(TaskResult.TaskResultStatus.DONE); + result.setTimestamp(success_at); + result.setId(perun.getTasksManagerBl().insertNewTaskResult(sess, result)); + + List richDestinations = perun.getServicesManager().getAllRichDestinations(sess, facility); + assertTrue("There should be one destination",richDestinations.size() == 1); + + RichDestination richDestination = richDestinations.get(0); + assertEquals(success_at.getTime() / 1000, richDestination.getLastSuccessfulPropagation().getTime() / 1000); + } + @Test public void getAllRichDestinationsWithService() throws Exception { System.out.println(CLASS_NAME + "getAllRichDestinationsWithService"); diff --git a/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettingsTest.java b/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettingsTest.java new file mode 100644 index 0000000000..ec1a061efb --- /dev/null +++ b/perun-core/src/test/java/cz/metacentrum/perun/core/impl/modules/attributes/urn_perun_user_attribute_def_def_mfaEnforceSettingsTest.java @@ -0,0 +1,181 @@ +package cz.metacentrum.perun.core.impl.modules.attributes; + +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.impl.PerunSessionImpl; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +public class urn_perun_user_attribute_def_def_mfaEnforceSettingsTest { + private static urn_perun_user_attribute_def_def_mfaEnforceSettings classInstance; + private static PerunSessionImpl session; + private static User user; + private static Attribute attributeToCheck; + + @Before + public void setUp() { + classInstance = new urn_perun_user_attribute_def_def_mfaEnforceSettings(); + session = mock(PerunSessionImpl.class, RETURNS_DEEP_STUBS); + user = new User(); + attributeToCheck = new Attribute(); + + + Attribute mockMfaCategories = new Attribute(); + mockMfaCategories.setValue("{\"categories\":" + + " {" + + " \"cat1\":" + + " {" + + " \"label\": {\"en\": \"cat1_en_label\"}," + + " \"rps\":" + + " {" + + " \"cat1_rps1\": {\"en\":\"cat1_rps1_en_label\"}," + + " \"cat1_rps2\": {\"en\":\"cat1_rps2_en_label\"}" + + " }" + + " }," + + " \"cat2\":" + + " {" + + " \"label\": {\"en\": \"cat2_en_label\"}," + + " \"rps\":" + + " {" + + " \"cat2_rps1\": {\"en\":\"cat2_rps1_en_label\"}" + + " }" + + " }" + + " }" + + "}"); + when(session.getPerunBl().getAttributesManagerBl().getEntitylessAttributes(any(), any())) + .thenReturn(Collections.singletonList(mockMfaCategories)); + } + + @Test + public void testAttributeSyntaxNotValidJSON() { + System.out.println("testAttributeSyntaxNotValidJSON()"); + attributeToCheck.setValue("plain string"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals("Attribute value " + attributeToCheck.getValue() + " is not a valid JSON." , errorWithoutId(e.getMessage())); + } + } + + @Test + public void testAttributeSyntaxNull() throws Exception { + System.out.println("testAttributeSyntaxNull()"); + attributeToCheck.setValue(null); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxEmpty() throws Exception { + System.out.println("testAttributeSyntaxEmpty()"); + attributeToCheck.setValue(""); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxAllTrue() throws Exception { + System.out.println("testAttributeSyntaxAllTrue()"); + attributeToCheck.setValue("{\"all\": true}"); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxCategories() throws Exception { + System.out.println("testAttributeSyntaxCategories()"); + attributeToCheck.setValue("{\"include_categories\":[\"str1\",\"str2\"]}"); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxCategoriesRps() throws Exception { + System.out.println("testAttributeSyntaxCategoriesRps()"); + attributeToCheck.setValue("{\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"rp1\",\"rp2\"]}"); + + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } + + @Test + public void testAttributeSyntaxCategoryNotArray() { + System.out.println("testAttributeSyntaxCategoryNotArray()"); + attributeToCheck.setValue("{\"include_categories\":\"str\"}"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals("Property 'include_categories' is not an array.", errorWithoutId(e.getMessage())); + } + } + + @Test + public void testAttributeSyntaxCategoryValueNotString() { + System.out.println("testAttributeSyntaxCategoryValueNotString()"); + attributeToCheck.setValue("{\"include_categories\":[\"str\", []]}"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals("Property 'include_categories' has non textual value []", errorWithoutId(e.getMessage())); + } + } + + @Test + public void testAttributeSyntaxWrongFormat() { + System.out.println("testAttributeSyntaxWrongFormat()"); + attributeToCheck.setValue("{\"all\":true,\"include_categories\":[\"str1\",\"str2\"]}"); + + try { + classInstance.checkAttributeSyntax(session, user, attributeToCheck); + } catch (WrongAttributeValueException e) { + assertEquals( + "Attribute value {\"all\":true,\"include_categories\":[\"str1\",\"str2\"]} has incorrect format." + + " Allowed values are:" + + " empty string or null," + + " {\"all\":true}, {\"include_categories\":[\"str1\",\"str2\"]}," + + " {\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"rp1\",\"rp2\"]}", + errorWithoutId(e.getMessage())); + } + } + + @Test + public void testCheckAttributeSemanticsCorrect() throws Exception { + System.out.println("testCheckAttributeSemanticsCorrect()"); + attributeToCheck.setValue("{\"include_categories\":[\"cat1\",\"cat2\"],\"exclude_rps\":[\"cat1_rps2\",\"cat2_rps1\"]}"); + + classInstance.checkAttributeSemantics(session, user, attributeToCheck); + } + + @Test(expected = WrongReferenceAttributeValueException.class) + public void testCheckAttributeSemanticsWrongCategoryReference() throws Exception { + System.out.println("testCheckAttributeSemanticsWrongCategoryReference()"); + attributeToCheck.setValue("{\"include_categories\":[\"cat1\",\"wrong_cat\"],\"exclude_rps\":[\"cat1_rps2\",\"cat2_rps1\"]}"); + + classInstance.checkAttributeSemantics(session, user, attributeToCheck); + } + + @Test(expected = WrongReferenceAttributeValueException.class) + public void testCheckAttributeSemanticsWrongRpsReference() throws Exception { + System.out.println("testCheckAttributeSemanticsWrongRpsReference()"); + attributeToCheck.setValue("{\"include_categories\":[\"str1\",\"str2\"],\"exclude_rps\":[\"wrong_rps\",\"rp2\"]}"); + + classInstance.checkAttributeSemantics(session, user, attributeToCheck); + } + + private String errorWithoutId(String msg) { + return msg.substring(msg.indexOf(":") + 1).trim(); + } +} diff --git a/perun-db/postgres.sql b/perun-db/postgres.sql index 00dd4f86c6..c2923381a6 100644 --- a/perun-db/postgres.sql +++ b/perun-db/postgres.sql @@ -1,4 +1,4 @@ --- database version 3.2.15 (don't forget to update insert statement at the end of file) +-- database version 3.2.17 (don't forget to update insert statement at the end of file) -- VOS - virtual organizations create table vos ( @@ -329,6 +329,7 @@ create table attribute_policies ( create table attribute_critical_actions ( attr_id integer not null, --identifier of attribute (attr_names.id) action attribute_action not null, --action on attribute (READ/WRITE) + global boolean default false not null, --action is critical globally for all objects constraint attrcritops_pk primary key (attr_id, action), constraint attrcritops_attr_fk foreign key (attr_id) references attr_names (id) on delete cascade ); @@ -1597,6 +1598,8 @@ create table authz ( modified_by_uid integer, authorized_group_id integer, --identifier of whole authorized group security_team_id integer, --identifier of security team + created_at timestamp default statement_timestamp() not null, + created_by varchar default user not null, constraint authz_role_fk foreign key (role_id) references roles(id), constraint authz_user_fk foreign key (user_id) references users(id), constraint authz_authz_group_fk foreign key (authorized_group_id) references groups(id), @@ -2014,7 +2017,7 @@ grant all on blocked_logins to perun; grant all on auto_registration_groups to perun; -- set initial Perun DB version -insert into configurations values ('DATABASE VERSION','3.2.15'); +insert into configurations values ('DATABASE VERSION','3.2.17'); -- insert membership types insert into membership_types (id, membership_type, description) values (1, 'DIRECT', 'Member is directly added into group'); diff --git a/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java b/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java index 6e0b63a97a..2d6feeff47 100644 --- a/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java +++ b/perun-ldapc/src/main/java/cz/metacentrum/perun/ldapc/model/PerunAttribute.java @@ -74,7 +74,7 @@ public interface PerunAttributeNames { public static final String ldapAttrAdminOfGroup = "adminOfGroup"; public static final String ldapAttrAdminOfFacility = "adminOfFacility"; public static final String ldapAttrUuid = "uuid"; - public static final String ldapAttrUserEligibilities = "userEligibilities"; + public static final String ldapAttrUserEligibilities = perunAttrUserEligibilities; //LDAP OBJECT CLASSES public static final String objectClassTop = "top"; diff --git a/perun-openapi/openapi.yml b/perun-openapi/openapi.yml index 71ecb90717..1d021652bd 100644 --- a/perun-openapi/openapi.yml +++ b/perun-openapi/openapi.yml @@ -389,9 +389,14 @@ components: items: $ref: '#/components/schemas/AttributePolicyCollection' criticalActions: - type: array - items: - $ref: '#/components/schemas/AttributeAction' + type: object + description: "Represents types of actions (READ, WRITE) that users can perform upon attributes and if they are critical globally or not." + additionalProperties: + type: boolean + example: { + READ: false, + WRITE: true, + } AuditEvent: type: object @@ -1003,6 +1008,7 @@ components: blocked: { type: boolean } service: { $ref: '#/components/schemas/Service' } facility: { $ref: '#/components/schemas/Facility' } + lastSuccessfulPropagation: { type: string } discriminator: propertyName: beanName @@ -7713,6 +7719,7 @@ paths: - $ref: '#/components/parameters/attributeDefinitionId' - {name: action, in: query, schema: { $ref: '#/components/schemas/AttributeAction' }, required: true} - {name: critical, description: "if action should be marked as critical", schema: { type: boolean }, in: query, required: true} + - {name: global, description: "if action should be globally critical (for all objects)", schema: { type: boolean }, in: query, required: false} responses: '200': $ref: '#/components/responses/VoidResponse' @@ -10995,6 +11002,23 @@ paths: default: $ref: '#/components/responses/ExceptionResponse' + /urlinjsonout/membersManager/sendUsernameReminderEmail: + post: + tags: + - MembersManager + operationId: sendUsernameReminderEmail + summary: Send mail to user's preferred email address with a reminder of the username for the given namespace. + parameters: + - $ref: '#/components/parameters/memberId' + - $ref: '#/components/parameters/namespace' + - { name: emailAttributeURN, schema: { type: string }, in: query, required: true, description: "urn of the attribute with stored mail" } + - { name: language, schema: { type: string }, in: query, required: true, description: "language of the message" } + responses: + '200': + $ref: '#/components/responses/VoidResponse' + default: + $ref: '#/components/responses/ExceptionResponse' + /urlinjsonout/membersManager/sendPasswordResetLinkEmail: post: tags: diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/AbstractLifeScienceHostelRI.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/AbstractLifeScienceHostelRI.java new file mode 100644 index 0000000000..0b92dbd0d6 --- /dev/null +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/AbstractLifeScienceHostelRI.java @@ -0,0 +1,164 @@ +package cz.metacentrum.perun.registrar.modules; + +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.AttributesManager; +import cz.metacentrum.perun.core.api.ExtSource; +import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.PerunSession; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.UserExtSource; +import cz.metacentrum.perun.core.api.Vo; +import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; +import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; +import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; +import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; +import cz.metacentrum.perun.core.api.exceptions.UserExtSourceNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.bl.PerunBl; +import cz.metacentrum.perun.registrar.exceptions.RegistrarException; +import cz.metacentrum.perun.registrar.model.Application; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + * Abstract module for VO lifescience_hostel used in the LifeScience (LS AAI) Perun instance. + * Creates UES with LS Hostel identity and adds user to the lifescience VO directly when the application is approved. + * The concrete implementation has to specify some properties, as this module can be re-used by various instances + * (e.g. production and acceptance instances) + * + * @see cz.metacentrum.perun.registrar.modules.LifeScienceHostelRI for production instance module + * @see cz.metacentrum.perun.registrar.modules.LifeScienceHostelRIAcc for acceptance instance module + * + * @author Pavel Vyskocil + * @author Dominik Frantisek Bucik + */ +public abstract class AbstractLifeScienceHostelRI extends DefaultRegistrarModule { + + private final static Logger log = LoggerFactory.getLogger(LifescienceHostel.class); + + private final static String VO_SHORTNAME = "lifescience"; + + private final static String LOGIN_NAMESPACE = "login-namespace:lifescienceid-username"; + + private final static String AUIDS_ATTRIBUTE = "urn:perun:ues:attribute-def:def:additionalIdentifiers"; + + + /** + * Create proper UserExtSource + */ + @Override + public Application approveApplication(PerunSession session, Application app) + throws PrivilegeException, GroupNotExistsException, MemberNotExistsException, ExternallyManagedException, + WrongReferenceAttributeValueException, WrongAttributeValueException, RegistrarException, + ExtSourceNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, + VoNotExistsException, ExtendMembershipException, AlreadyMemberException + { + PerunBl perun = (PerunBl)session.getPerun(); + + User user = app.getUser(); + if (user != null) { + // Create UES for user + Attribute userLogin = perun.getAttributesManagerBl().getAttribute( + session, user, AttributesManager.NS_USER_ATTR_DEF + ":" + LOGIN_NAMESPACE); + if (userLogin != null && userLogin.getValue() != null) { + UserExtSource ues = storeAndGetUserExtSource(session, user, userLogin, perun); + setAuidIntoUesAttributes(session, ues, perun); + } + + if (Application.AppType.INITIAL.equals(app.getType())) { + try { + Vo vo = perun.getVosManagerBl().getVoByShortName(session, VO_SHORTNAME); + Member member = perun.getMembersManagerBl().createMember(session, vo, user); + perun.getMembersManagerBl().validateMemberAsync(session, member); + log.debug("LS Hostel member added to the main VO Lifescience {}", member); + } catch (VoNotExistsException e) { + log.warn("VO: " + VO_SHORTNAME + " not exists, can't add member into it."); + } catch (AlreadyMemberException ignore) { + // user is already in lifescience + } catch (ExtendMembershipException e) { + // can't be member of lifescience, shouldn't happen + log.error("LS Hostel member can't be added to VO: " + VO_SHORTNAME, e); + } + } + // User doesn't have login - don't set UES + } + + return app; + } + + /** + * Get scope part of the user login (including @ character) + * @return scope + */ + protected abstract String getScope(); + + /** + * Get name of the ExtSource for which the login is generated + * @return ExtSource name + */ + protected abstract String getExtSourceName(); + + /** + * Creates the UserExtSource object with user login and returns it. + * If the UES already exists, method just returns it. + */ + private UserExtSource storeAndGetUserExtSource(PerunSession session, User user, Attribute userLogin, PerunBl perun) + throws ExtSourceNotExistsException + { + ExtSource extSource = perun.getExtSourcesManagerBl().getExtSourceByName(session, getExtSourceName()); + String login = userLogin.valueAsString(); + UserExtSource ues = new UserExtSource(extSource, login + getScope()); + ues.setLoa(0); + + try { + ues = perun.getUsersManagerBl().addUserExtSource(session, user, ues); + } catch (UserExtSourceExistsException ex) { + try { + ues = perun.getUsersManagerBl().getUserExtSourceByExtLogin(session, extSource, login); + } catch (UserExtSourceNotExistsException e) { + // should not happen due to parent catch block + } + } + return ues; + } + + /** + * Stores the user login from passed UserExtSource into the AUIDS attribute (constant AUIDS_ATTRIBUTE). + */ + private void setAuidIntoUesAttributes(PerunSession session, UserExtSource ues, PerunBl perun) + throws WrongAttributeAssignmentException, WrongReferenceAttributeValueException, PrivilegeException, + WrongAttributeValueException + { + try { + Attribute auidsAttr = perun.getAttributesManager().getAttribute(session, ues, AUIDS_ATTRIBUTE); + Set attrValue = new HashSet<>(); + if (auidsAttr.getValue() != null + && auidsAttr.valueAsList() != null + && !auidsAttr.valueAsList().isEmpty() + ) { + attrValue.addAll(auidsAttr.valueAsList()); + } + attrValue.add(ues.getLogin()); + auidsAttr.setValue(new ArrayList<>(attrValue)); + perun.getAttributesManager().setAttribute(session, ues, auidsAttr); + } catch (UserExtSourceNotExistsException e) { + // should not happen + } catch (AttributeNotExistsException e) { + // ok, attribute is probably not used + } + } + +} + diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java index cd196a682f..e785354100 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRICollections.java @@ -40,6 +40,7 @@ * @author Jiri Mauritz (original) * @author Dominik Frantisek Bucik (modifications) */ +@Deprecated public class BBMRICollections extends DefaultRegistrarModule { private final static Logger log = LoggerFactory.getLogger(BBMRICollections.class); diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java index 09936aa99c..f32a182444 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRINetworks.java @@ -44,6 +44,7 @@ * @author Jiri Mauritz (original) * @author Dominik Frantisek Bucik (modifications) */ +@Deprecated public class BBMRINetworks extends DefaultRegistrarModule { private final static Logger log = LoggerFactory.getLogger(BBMRINetworks.class); diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRIResources.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRIResources.java new file mode 100644 index 0000000000..72322d3e1c --- /dev/null +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/BBMRIResources.java @@ -0,0 +1,314 @@ +package cz.metacentrum.perun.registrar.modules; + +import com.google.common.base.Strings; +import cz.metacentrum.perun.core.api.Attribute; +import cz.metacentrum.perun.core.api.Group; +import cz.metacentrum.perun.core.api.Member; +import cz.metacentrum.perun.core.api.PerunSession; +import cz.metacentrum.perun.core.api.User; +import cz.metacentrum.perun.core.api.Vo; +import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; +import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; +import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; +import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.NotGroupMemberException; +import cz.metacentrum.perun.core.api.exceptions.PerunException; +import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; +import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; +import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; +import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; +import cz.metacentrum.perun.core.bl.PerunBl; +import cz.metacentrum.perun.registrar.exceptions.CantBeApprovedException; +import cz.metacentrum.perun.registrar.exceptions.RegistrarException; +import cz.metacentrum.perun.registrar.model.Application; +import cz.metacentrum.perun.registrar.model.ApplicationFormItemData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Registration module for BBMRI Collections, Networks, canSERV services, etc. + * Module: + * 1. reads input with IDs of the resources and checks, whether groups representing resources exist + * - uses the content of attribute RESOURCE_ID_ATTR_NAME as the name of the attribute containing IDs of the + * resources on groups + * - parses the root group, under which the groups representing specified resources are located + * - if RESOURCE_ORIGIN_ENABLED_ATTR_NAME is set to true, the root group will be parsed from input from the form + * (where input is select named "resourceOrigin") + * - else if RESOURCES_ROOT_GROUP_ATTR_NAME is set, the root group will be parsed from the value of this attribute + * - else the root group will be considered the target group of the application + * 2. adds users to the appropriate groups + * + * @author Dominik Frantisek Bucik + */ +public class BBMRIResources extends DefaultRegistrarModule { + + private final static Logger log = LoggerFactory.getLogger(BBMRIResources.class); + + // field names + private static final String RESOURCE_IDS = "resourceIds"; + private static final String RESOURCE_ORIGIN = "resourceOrigin"; + + // configuration attributes + private static final String RESOURCE_ID_ATTR_NAME = "urn:perun:group:attribute-def:def:resourceIDAttrName"; + private static final String RESOURCE_ORIGIN_ENABLED_ATTR_NAME = "urn:perun:group:attribute-def:def:resourceOriginEnabled"; + private static final String RESOURCES_ROOT_GROUP_ATTR_NAME = "urn:perun:group:attribute-def:def:resourcesRootGroup"; + + /** + * Finds groups representing resources by provided input, adds user into these groups and removes from the + * group where this application form is used. + * + * @param session who approves the application + * @param app application + * @return unchanged application + * @throws PerunException in case of internal error in Perun + */ + @Override + public Application approveApplication(PerunSession session, Application app) + throws VoNotExistsException, UserNotExistsException, PrivilegeException, + MemberNotExistsException, RegistrarException, GroupNotExistsException, + AttributeNotExistsException, WrongAttributeAssignmentException, ExternallyManagedException, + WrongAttributeValueException, WrongReferenceAttributeValueException, NotGroupMemberException + { + if (app.getGroup() == null) { + throw new RegistrarException( + "Invalid usage of registrar module - module '" + this.getClass().getName() + + "' should be used on group level only"); + } + + // get perun and beans from session + PerunBl perun = (PerunBl) session.getPerun(); + Vo vo = app.getVo(); + User user = app.getUser(); + Member member; + try { + member = perun.getMembersManagerBl().getMemberByUser(session, vo, user); + } catch (MemberNotExistsException ex) { + log.error("User {} is not member in the VO {}", user, vo); + throw new RegistrarException("Cannot approve application - user is not member of the VO"); + } + + // get IDs of resources specified by the user + Set resourceIDsInApplication = getResourceIDsFromApplication(session, app); + // get IDs of resources represented as groups in the system + Map resourceIDToGroupMapsInSystem = getPerunResourceIdToGroupMap(session, app, perun); + + // add user to all groups from the field on application + for (String resourceId : resourceIDsInApplication) { + Group resource = resourceIDToGroupMapsInSystem.getOrDefault(resourceId, null); + if (resource == null) { + log.debug("There is no group for resource with ID: '{}'", resourceId); + } else { + try { + perun.getGroupsManagerBl().addMember(session, resource, member); + } catch (AlreadyMemberException ex) { + // ignore + } + } + } + try { + perun.getGroupsManagerBl().removeMember(session, app.getGroup(), member); + } catch (NotGroupMemberException e) { + //we can ignore this exception + } + + return app; + } + + /** + * Checks whether all resource IDs found in user input really exists in Perun. + * If not, CantBeApproved exception is thrown. + * + * @param session who approves the application + * @param app unchanged application + * @throws CantBeApprovedException if at least one resource ID does not exist in Perun + */ + @Override + public void canBeApproved(PerunSession session, Application app) throws PerunException { + // get perun and beans from session + PerunBl perun = (PerunBl) session.getPerun(); + + // get IDs of resources specified by the user + Set resourceIDsInApplication = getResourceIDsFromApplication(session, app); + // get IDs of resources represented as groups in the system + Set resourceIDsInSystem = getPerunResourceIdToGroupMap(session, app, perun).keySet(); + // remove existing resources, so we get invalid inputs + resourceIDsInApplication.removeAll(resourceIDsInSystem); + + // difference must be empty + if (!resourceIDsInApplication.isEmpty()) { + throw new CantBeApprovedException("Resources " + resourceIDsInApplication + " do not exist." + + "If you approve the application, these resources will be skipped.", "", "", "", true, app.getId()); + } + } + + /** + * Gets Resources present in the system as a map of the Resource ID to the group representing particular resource. + * + * @return Map of String to Group, where key is the ID of the resource and Group is the representation + */ + private Map getPerunResourceIdToGroupMap(PerunSession session, Application app, PerunBl perun) + throws PrivilegeException, RegistrarException, VoNotExistsException, WrongAttributeAssignmentException, + AttributeNotExistsException, GroupNotExistsException + { + // get root group for resources hierarchy + Group resourceOriginGroup = getResourceOriginGroup(session, app, perun); + // get name of the attribute (stored in RESOURCE_ID_ATTR_NAME attribute) containing ID of the resource + String resourceIdAttributeName = getResourceIdAttributeName(session, app, perun); + + // get map of ResourceID -> group (representing resource) + return getResourceIDsToGroupsMap(session, perun, resourceOriginGroup, resourceIdAttributeName); + } + + /** + * Gets name of the attribute containing Resource IDs. This attribute should be used at the groups + * identifying resources. + * + * @return name of the attribute where ID of the resource is stored, null if value is not set + */ + private String getResourceIdAttributeName(PerunSession session, Application app, PerunBl perun) + throws WrongAttributeAssignmentException, AttributeNotExistsException + { + return perun.getAttributesManagerBl() + .getAttribute(session, app.getGroup(), RESOURCE_ID_ATTR_NAME) + .valueAsString(); + } + + /** + * Gets root group, under which subgroups representing resources are placed. + * + * @return resource IDs set + */ + private Group getResourceOriginGroup(PerunSession session, Application app, PerunBl perun) + throws PrivilegeException, RegistrarException, VoNotExistsException + { + try { + if (perun.getAttributesManagerBl() + .getAttribute(session, app.getGroup(), RESOURCE_ORIGIN_ENABLED_ATTR_NAME) + .valueAsBoolean() + ) { + try { + String resourceOriginGroupName = getResourceOriginGroupNameFromApplication(session, app); + return perun.getGroupsManagerBl().getGroupByName(session, app.getVo(), resourceOriginGroupName); + } catch (GroupNotExistsException e) { + throw new InternalErrorException("Target group does not exist"); + } + } + } catch (AttributeNotExistsException | WrongAttributeAssignmentException ex) { + //OK, we consider it as disabled, try manually configured resource root group + } + + try { + try { + String resourceOriginGroupName = perun.getAttributesManagerBl() + .getAttribute(session, app.getGroup(), RESOURCES_ROOT_GROUP_ATTR_NAME) + .valueAsString(); + return perun.getGroupsManagerBl().getGroupByName(session, app.getVo(), resourceOriginGroupName); + } catch (GroupNotExistsException e) { + throw new InternalErrorException("Target group does not exist"); + } + } catch (AttributeNotExistsException | WrongAttributeAssignmentException exc) { + // OK, root will be the app group + } + + return app.getGroup(); + } + + /** + * Gets name of target group, where subgroups representing resources are placed. + * + * @return resource IDs set + */ + private String getResourceOriginGroupNameFromApplication(PerunSession session, Application app) + throws RegistrarException, PrivilegeException + { + String resourceOriginGroupName = null; + List formData = registrar.getApplicationDataById(session, app.getId()); + for (ApplicationFormItemData field : formData) { + if (RESOURCE_ORIGIN.equals(field.getShortname())) { + resourceOriginGroupName = field.getValue(); + break; + } + } + + if (resourceOriginGroupName == null) { + throw new InternalErrorException("There is no field with target group name on the registration form."); + } + + return resourceOriginGroupName; + } + + /** + * Gets resource IDs from a field on the application form with short name. + * + * @return resource IDs set + */ + private Set getResourceIDsFromApplication(PerunSession session, Application app) + throws RegistrarException, PrivilegeException + { + String resourceIdsString = null; + List formData = registrar.getApplicationDataById(session, app.getId()); + for (ApplicationFormItemData field : formData) { + if (RESOURCE_IDS.equals(field.getShortname())) { + resourceIdsString = field.getValue(); + break; + } + } + + if (resourceIdsString == null) { + throw new InternalErrorException("There is no field with resource IDs on the registration form."); + } + + // get set of resource IDs from application + Set resourceIDsInApplication = new HashSet<>(); + for (String resource : resourceIdsString.split("[,\n ]+")) { + resourceIDsInApplication.add(resource.trim()); + } + + return resourceIDsInApplication; + } + + /** + * Gets resources as map of resourceID => Group. + * + * @return Map of resource IDs to group. + */ + private Map getResourceIDsToGroupsMap(PerunSession session, + PerunBl perun, + Group resourceOriginGroup, + String resourceIdContainingAttribute) + throws WrongAttributeAssignmentException, AttributeNotExistsException + { + Map resourceIDsToGroupMap = new HashMap<>(); + + List resourceGroups = perun.getGroupsManagerBl().getSubGroups(session, resourceOriginGroup); + if (resourceGroups == null || resourceGroups.isEmpty()) { + log.debug("No resource groups found, returning empty map."); + return resourceIDsToGroupMap; + } + + for (Group resourceGroup : resourceGroups) { + Attribute resourceIDAttr = perun.getAttributesManagerBl() + .getAttribute(session, resourceGroup, resourceIdContainingAttribute); + + if (resourceIDAttr == null || Strings.isNullOrEmpty(resourceIDAttr.valueAsString())) { + log.warn("Found resource group ({}) without value in attr {}: ({})", + resourceGroup, resourceIdContainingAttribute, resourceIDAttr); + } else { + resourceIDsToGroupMap.put(resourceIDAttr.valueAsString(), resourceGroup); + } + } + + return resourceIDsToGroupMap; + } + +} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Ceitec.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Ceitec.java deleted file mode 100644 index 64a3c41728..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Ceitec.java +++ /dev/null @@ -1,49 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.exceptions.PerunException; -import cz.metacentrum.perun.registrar.exceptions.CantBeApprovedException; -import cz.metacentrum.perun.registrar.model.Application; -import cz.metacentrum.perun.registrar.model.ApplicationFormItemData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Objects; - -/** - * Module for CEITEC VO at MU instance of Perun. - * - * The module check if name provided by User and IdP is different. If so, automatic approval is cancelled - * and VO manager must approve it manually. - * - * @author Pavel Zlámal - */ -public class Ceitec extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Ceitec.class); - - @Override - public void canBeApproved(PerunSession session, Application app) throws PerunException { - - List data = registrar.getApplicationDataById(session, app.getId()); - - String name = ""; - String fed_name = ""; - - for (ApplicationFormItemData item : data) { - if (Objects.equals(item.getShortname(),"jmeno")) { - name = item.getValue(); - } - if (Objects.equals(item.getShortname(),"jmeno_fed")) { - fed_name = item.getValue(); - } - } - - if (!Objects.equals(name,fed_name)) { - throw new CantBeApprovedException("Users name provided by IdP and User differ. Please check for correct name before approval.","","","",true, app.getId()); - } - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java index 348ba577c0..3edb51bb4b 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/CeitecNcbr.java @@ -8,11 +8,11 @@ import org.slf4j.LoggerFactory; /** - * Module for CEITEC@MUNI and NCBR@MUNI VOs at CESNET instance of Perun. + * Module for NCBR@MUNI VOs at CESNET instance. * * The module - * 1. Allows (auto)approval of applications with LoA = 2. - * 2. Enforce manual approval of applications with LoA = 0, 1. + * 1. Allows (auto)approval of applications by users from IdP MUNI + * 2. Enforce manual approval of applications by users from different IdPs * * @author Pavel Zlámal */ @@ -20,11 +20,13 @@ public class CeitecNcbr extends DefaultRegistrarModule { final static Logger log = LoggerFactory.getLogger(CeitecNcbr.class); + private final static String IDP_MU = "https://idp2.ics.muni.cz/idp/shibboleth"; + @Override public void canBeApproved(PerunSession session, Application app) throws PerunException { - if (app.getExtSourceLoa() == 2) return; - throw new CantBeApprovedException("Application can't be approved automatically. LoA is: "+app.getExtSourceLoa()+". Please double check users identity before manual/force approval.", "", "", "", true, app.getId()); + if (IDP_MU.equals(app.getExtSourceName())) return; + throw new CantBeApprovedException("Application can't be approved automatically. User has not used IdP MUNI to log in. Please double check users identity before manual/force approval.", "", "", "", true, app.getId()); } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EduGain.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EduGain.java deleted file mode 100644 index 9407f55920..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/EduGain.java +++ /dev/null @@ -1,48 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.*; -import cz.metacentrum.perun.core.api.exceptions.AlreadyAdminException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.RoleCannotBeManagedException; -import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Application module for EduGain purpose - */ -public class EduGain extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Metacentrum.class); - - /** - * All new members will be given role VOOBSERVER and TOPGROUPCREATOR - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws UserNotExistsException, PrivilegeException, AlreadyAdminException, GroupNotExistsException, VoNotExistsException { - - if (Application.AppType.INITIAL.equals(app.getType())) { - - Vo vo = app.getVo(); - User user = app.getUser(); - - try { - AuthzResolver.setRole(session, user, vo, Role.TOPGROUPCREATOR); - - Group membersGroup = session.getPerun().getGroupsManager().getGroupByName(session, vo, "members"); - AuthzResolver.setRole(session, user, membersGroup, Role.GROUPADMIN); - } catch (RoleCannotBeManagedException e) { - throw new InternalErrorException(e); - } - - } - - return app; - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Elixircz.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Elixircz.java deleted file mode 100644 index 6905bad2aa..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Elixircz.java +++ /dev/null @@ -1,70 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Objects; - -/** - * Module for ELIXIR-CZ VO at CESNET instance of Perun. - * - * By default all VO members get nearest 1.12. (loa 2) or +3m without possibility of extension. - * - * 1. For new VO registrations, if loa=2, manually set 1.1.9999 - * 2. For new Group registration, use VO rules - * 3. For VO extension, user VO rules - * - * @author Pavel Zlámal - */ -public class Elixircz extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Elixircz.class); - - @Override - public Application approveApplication(PerunSession session, Application app) throws MemberNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, WrongAttributeValueException, WrongReferenceAttributeValueException, ExtendMembershipException { - - PerunBl perun = (PerunBl) session.getPerun(); - Member member = perun.getMembersManagerBl().getMemberByUser(session, app.getVo(), app.getUser()); - - if (app.getGroup() == null && Objects.equals(app.getType(), Application.AppType.INITIAL)) { - - // IF VO INITIAL override VO rules to set unlimited (only to those with LoA = 2). - Attribute loaAttr = perun.getAttributesManagerBl().getAttribute(session, app.getUser(), AttributesManager.NS_USER_ATTR_VIRT + ":loa"); - int loa = Integer.valueOf((String) loaAttr.getValue()); - - if (loa == 2) { - Attribute attr = perun.getAttributesManagerBl().getAttribute(session, member, AttributesManager.NS_MEMBER_ATTR_DEF + ":membershipExpiration"); - attr.setValue("9999-01-01"); // set distant future as never expires - perun.getAttributesManagerBl().setAttribute(session, member, attr); - } - - } - - if ((app.getGroup() != null && Objects.equals(app.getType(), Application.AppType.INITIAL)) || - (app.getGroup() != null && Objects.equals(app.getType(), Application.AppType.EMBEDDED)) || - (app.getGroup() == null && Objects.equals(app.getType(), Application.AppType.EXTENSION))) { - - // GROUP INITIAL/EMBEDDED OR VO EXTENSION -> set back standard expiration date based on VO rules - Attribute attr = perun.getAttributesManagerBl().getAttribute(session, member, AttributesManager.NS_MEMBER_ATTR_DEF + ":membershipExpiration"); - perun.getAttributesManagerBl().removeAttribute(session, member, attr); - perun.getMembersManagerBl().extendMembership(session, member); - - } - - return app; - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java index 5aa274fbc4..404a3e7948 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRI.java @@ -1,100 +1,27 @@ package cz.metacentrum.perun.registrar.modules; -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.ExtSource; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.UserExtSource; -import cz.metacentrum.perun.core.api.Vo; -import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; -import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; -import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.exceptions.RegistrarException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Module for VO lifescience_hostel on LifeScience Perun machine - * * On approval create UES with LS Hostel identity and add user to the lifescience VO directly. * * @author Pavel Vyskocil */ -public class LifeScienceHostelRI extends DefaultRegistrarModule { - - private final static Logger log = LoggerFactory.getLogger(LifescienceHostel.class); - - private final static String HOSTEL_HOSTANAME = "hostel.aai.lifescience-ri.eu"; - private final static String LOGIN_NAMESPACE = "login-namespace:lifescienceid-username"; - private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTANAME; - private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTANAME + "/lshostel/"; - private final static String VO_SHORTNAME = "lifescience"; - - /** - * Create proper UserExtSource - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws PrivilegeException, GroupNotExistsException, MemberNotExistsException, ExternallyManagedException, WrongReferenceAttributeValueException, WrongAttributeValueException, RegistrarException, ExtSourceNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, VoNotExistsException, ExtendMembershipException, AlreadyMemberException { - - PerunBl perun = (PerunBl)session.getPerun(); - - User user = app.getUser(); +public class LifeScienceHostelRI extends AbstractLifeScienceHostelRI { - if (user != null) { + private final static String HOSTEL_HOSTNAME = "hostel.aai.lifescience-ri.eu"; - // Create UES for user - Attribute userLogin = perun.getAttributesManagerBl().getAttribute(session, user, AttributesManager.NS_USER_ATTR_DEF + ":" + LOGIN_NAMESPACE); - if (userLogin != null && userLogin.getValue() != null) { - ExtSource extSource = perun.getExtSourcesManagerBl().getExtSourceByName(session, LS_HOSTEL_EXT_SOURCE_NAME); - String login = userLogin.valueAsString(); - UserExtSource ues = new UserExtSource(extSource, login + LS_HOSTEL_SCOPE); - ues.setLoa(0); + private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTNAME; - try { - perun.getUsersManagerBl().addUserExtSource(session, user, ues); - } catch (UserExtSourceExistsException ex) { - // this is OK - } - - } - - if (Application.AppType.INITIAL.equals(app.getType())) { - try { - Vo vo = perun.getVosManagerBl().getVoByShortName(session, VO_SHORTNAME); - Member member = perun.getMembersManagerBl().createMember(session, vo, user); - perun.getMembersManagerBl().validateMemberAsync(session, member); - log.debug("LS Hostel member added to the main VO Lifescience {}", member); - } catch (VoNotExistsException e) { - log.warn("VO: " + VO_SHORTNAME + " not exists, can't add member into it."); - } catch (AlreadyMemberException ignore) { - // user is already in lifescience - } catch (ExtendMembershipException e) { - // can't be member of lifescience, shouldn't happen - log.error("LS Hostel member can't be added to VO: " + VO_SHORTNAME, e); - } - } - - // User doesn't have login - don't set UES - - } - - return app; + private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTNAME + "/lshostel/"; + @Override + protected String getScope() { + return LS_HOSTEL_SCOPE; } + @Override + protected String getExtSourceName() { + return LS_HOSTEL_EXT_SOURCE_NAME; + } } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java index 0e6ab033eb..5dc93d27e8 100644 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java +++ b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/LifeScienceHostelRIAcc.java @@ -1,32 +1,5 @@ package cz.metacentrum.perun.registrar.modules; -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.AttributesManager; -import cz.metacentrum.perun.core.api.ExtSource; -import cz.metacentrum.perun.core.api.Member; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.UserExtSource; -import cz.metacentrum.perun.core.api.Vo; -import cz.metacentrum.perun.core.api.exceptions.AlreadyMemberException; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtSourceNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.ExtendMembershipException; -import cz.metacentrum.perun.core.api.exceptions.ExternallyManagedException; -import cz.metacentrum.perun.core.api.exceptions.GroupNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.MemberNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.UserExtSourceExistsException; -import cz.metacentrum.perun.core.api.exceptions.VoNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.exceptions.RegistrarException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Module for VO lifescience_hostel on LifeScience acceptance Perun machine * @@ -34,66 +7,22 @@ * * @author Pavel Vyskocil */ -public class LifeScienceHostelRIAcc extends DefaultRegistrarModule { - - private final static Logger log = LoggerFactory.getLogger(LifescienceHostel.class); - - private final static String HOSTEL_HOSTANAME = "hostel.acc.aai.lifescience-ri.eu"; - private final static String LOGIN_NAMESPACE = "login-namespace:lifescienceid-username"; - private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTANAME; - private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTANAME + "/lshostel/"; - private final static String VO_SHORTNAME = "lifescience"; - - /** - * Create proper UserExtSource - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws PrivilegeException, GroupNotExistsException, MemberNotExistsException, ExternallyManagedException, WrongReferenceAttributeValueException, WrongAttributeValueException, RegistrarException, ExtSourceNotExistsException, AttributeNotExistsException, WrongAttributeAssignmentException, VoNotExistsException, ExtendMembershipException, AlreadyMemberException { - - PerunBl perun = (PerunBl)session.getPerun(); - - User user = app.getUser(); +public class LifeScienceHostelRIAcc extends AbstractLifeScienceHostelRI { - if (user != null) { + private final static String HOSTEL_HOSTNAME = "hostel.acc.aai.lifescience-ri.eu"; - // Create UES for user - Attribute userLogin = perun.getAttributesManagerBl().getAttribute(session, user, AttributesManager.NS_USER_ATTR_DEF + ":" + LOGIN_NAMESPACE); - if (userLogin != null && userLogin.getValue() != null) { - ExtSource extSource = perun.getExtSourcesManagerBl().getExtSourceByName(session, LS_HOSTEL_EXT_SOURCE_NAME); - String login = userLogin.valueAsString(); - UserExtSource ues = new UserExtSource(extSource, login + LS_HOSTEL_SCOPE); - ues.setLoa(0); + private final static String LS_HOSTEL_SCOPE = "@" + HOSTEL_HOSTNAME; - try { - perun.getUsersManagerBl().addUserExtSource(session, user, ues); - } catch (UserExtSourceExistsException ex) { - // this is OK - } + private final static String LS_HOSTEL_EXT_SOURCE_NAME = "https://" + HOSTEL_HOSTNAME + "/lshostel/"; - } - - if (Application.AppType.INITIAL.equals(app.getType())) { - try { - Vo vo = perun.getVosManagerBl().getVoByShortName(session, VO_SHORTNAME); - Member member = perun.getMembersManagerBl().createMember(session, vo, user); - perun.getMembersManagerBl().validateMemberAsync(session, member); - log.debug("LS Hostel member added to the main VO Lifescience {}", member); - } catch (VoNotExistsException e) { - log.warn("VO: " + VO_SHORTNAME + " not exists, can't add member into it."); - } catch (AlreadyMemberException ignore) { - // user is already in lifescience - } catch (ExtendMembershipException e) { - // can't be member of lifescience, shouldn't happen - log.error("LS Hostel member can't be added to VO: " + VO_SHORTNAME, e); - } - } - - // User doesn't have login - don't set UES - - } - - return app; + @Override + protected String getScope() { + return LS_HOSTEL_SCOPE; + } + @Override + protected String getExtSourceName() { + return LS_HOSTEL_EXT_SOURCE_NAME; } } diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Sitola.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Sitola.java deleted file mode 100644 index 13b64758c3..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/Sitola.java +++ /dev/null @@ -1,92 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.Attribute; -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.exceptions.AttributeNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.InternalErrorException; -import cz.metacentrum.perun.core.api.exceptions.PrivilegeException; -import cz.metacentrum.perun.core.api.exceptions.UserNotExistsException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeAssignmentException; -import cz.metacentrum.perun.core.api.exceptions.WrongAttributeValueException; -import cz.metacentrum.perun.core.api.exceptions.WrongReferenceAttributeValueException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Module for VO Sitola - * - * @author Pavel Zlamal <256627@mail.muni.cz> - */ -public class Sitola extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(Sitola.class); - - /** - * All new Sitola members will have MU eduroam identity added if they posses MU login. - */ - @Override - public Application approveApplication(PerunSession session, Application app) throws WrongAttributeAssignmentException, UserNotExistsException, AttributeNotExistsException, PrivilegeException, WrongAttributeValueException, WrongReferenceAttributeValueException { - - // get perun from session - PerunBl perun = (PerunBl) session.getPerun(); - User user = app.getUser(); - - if (user != null) { - - Attribute eduroamIdentities = perun.getAttributesManagerBl().getAttribute(session, user, "urn:perun:user:attribute-def:def:eduroamIdentities"); - Attribute loginMu = perun.getAttributesManagerBl().getAttribute(session, user, "urn:perun:user:attribute-def:def:login-namespace:mu"); - - if (eduroamIdentities.getValue() == null) { - - if (loginMu.getValue() != null) { - - // add MU identity - List identities = new ArrayList<>(); - identities.add(loginMu.getValue() +"@eduroam.muni.cz"); - - eduroamIdentities.setValue(identities); - - // use Bl since VO manager normally can't set this attribute - perun.getAttributesManagerBl().setAttribute(session, user, eduroamIdentities); - - } - - } else { - - if (loginMu.getValue() != null) { - - // check if not already present and set - boolean found = false; - for (String value : eduroamIdentities.valueAsList()) { - if (Objects.equals(value, loginMu.getValue() +"@eduroam.muni.cz")) { - found = true; - break; - } - } - - if (!found) { - // add MU eduroam identity - ((List) eduroamIdentities.valueAsList()).add(loginMu.getValue() +"@eduroam.muni.cz"); - // use Bl since VO manager normally can't set this attribute - perun.getAttributesManagerBl().setAttribute(session, user, eduroamIdentities); - } - - } - - } - - - } - - return app; - - } - -} diff --git a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/WeNMR.java b/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/WeNMR.java deleted file mode 100644 index b18a09f952..0000000000 --- a/perun-registrar-lib/src/main/java/cz/metacentrum/perun/registrar/modules/WeNMR.java +++ /dev/null @@ -1,66 +0,0 @@ -package cz.metacentrum.perun.registrar.modules; - -import cz.metacentrum.perun.core.api.PerunSession; -import cz.metacentrum.perun.core.api.User; -import cz.metacentrum.perun.core.api.UserExtSource; -import cz.metacentrum.perun.core.api.exceptions.PerunException; -import cz.metacentrum.perun.core.bl.PerunBl; -import cz.metacentrum.perun.registrar.exceptions.CantBeApprovedException; -import cz.metacentrum.perun.registrar.model.Application; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Objects; - -/** - * Module for WeNMR VO at CESNET instance of Perun. - * - * The module - * 1. Allows (auto)approval of applications submitted by users with IdP identity https://www.structuralbiology.eu/idp/shibboleth - * 2. Enforce manual approval of applications submitted by users without IdP identity https://www.structuralbiology.eu/idp/shibboleth - * - * @author Pavel Zlámal - */ -public class WeNMR extends DefaultRegistrarModule { - - final static Logger log = LoggerFactory.getLogger(WeNMR.class); - - @Override - public void canBeApproved(PerunSession session, Application app) throws PerunException { - - // check if submitted from trusted IdP - if (!Objects.equals("https://www.structuralbiology.eu/idp/shibboleth", app.getExtSourceName())) { - - // submitted by untrusted IdP - PerunBl perun = (PerunBl) session.getPerun(); - User user; - - // check if user is known - if (app.getUser() != null) { - user = app.getUser(); - } else { - try { - user = perun.getUsersManagerBl().getUserByExtSourceNameAndExtLogin(session, app.getExtSourceName(), app.getCreatedBy()); - } catch (Exception ex) { - // unable to find user -> untrusted IdP - throw new CantBeApprovedException("Application can't be approved automatically. User doesn't have identity from \"www.structuralbiology.eu\". Please check users identity before manual/force approval.", "", "", "", true, app.getId()); - } - } - - List ueses = perun.getUsersManagerBl().getUserExtSources(session, user); - for (UserExtSource ues : ueses) { - if (Objects.equals("https://www.structuralbiology.eu/idp/shibboleth", ues.getExtSource().getName())) { - // user has trusted identity - return; - } - } - throw new CantBeApprovedException("Application can't be approved automatically. User doesn't have identity from \"www.structuralbiology.eu\". Please check users identity before manual/force approval.", "", "", "", true, app.getId()); - - } - - // submitted from trusted IdP - - } - -} diff --git a/perun-rpc/pom.xml b/perun-rpc/pom.xml index 48148cc370..d1cdd8f9ba 100644 --- a/perun-rpc/pom.xml +++ b/perun-rpc/pom.xml @@ -34,7 +34,7 @@ org.codehaus.cargo cargo-maven3-plugin - 1.10.8 + 1.10.9 tomcat9x diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java index 747e27ef26..c9ae71d6b2 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java @@ -225,6 +225,8 @@ private static PerunPrincipal setupPerunPrincipal(HttpServletRequest req, Deseri int extSourceLoa; Map additionalInformations = new HashMap<>(); + String referer = req.getHeader("Referer"); + String shibIdentityProvider = getStringAttribute(req, SHIB_IDENTITY_PROVIDER); String sourceIdpEntityId = getStringAttribute(req, SOURCE_IDP_ENTITY_ID); String remoteUser = req.getRemoteUser(); @@ -415,8 +417,8 @@ else if (Objects.equals(req.getAttribute(SSL_CLIENT_VERIFY), SUCCESS)) { if (isEmpty(extLogin) || isEmpty(extSourceName)) { throw new UserNotExistsException("extLogin or extSourceName is empty"); } - log.trace("creating PerunPrincipal(actor={},extSourceName={},extSourceType={},extSourceLoa={},additionalInformations={})",extLogin,extSourceName, extSourceType, extSourceLoa, additionalInformations); - return new PerunPrincipal(extLogin, extSourceName, extSourceType, extSourceLoa, additionalInformations); + log.trace("creating PerunPrincipal(actor={},extSourceName={},extSourceType={},extSourceLoa={},additionalInformations={},referer={})",extLogin,extSourceName, extSourceType, extSourceLoa, additionalInformations, referer); + return new PerunPrincipal(extLogin, extSourceName, extSourceType, extSourceLoa, additionalInformations, referer); } private PerunClient setupPerunClient(HttpServletRequest req) { diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java index 3b1158d6b8..ac029f0cea 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/AttributesManagerMethod.java @@ -4086,6 +4086,19 @@ public GraphDTO call(ApiCaller ac, Deserializer parms) throws PrivilegeException * @throws RelationExistsException if trying to mark already critical action * @throws RelationNotExistsException if trying to unmark not critical action */ + /*# + * Marks the action on attribute as critical, which may require additional authentication of user + * performing that action on attribute. + * + * @param sess session + * @param attributeDefinition attribute definition id + * @param action critical action + * @param critical true if action should be set critical, false to non-critical + * @param global true if action should be globally critical, false if action should be critical only for critical objects + * + * @throws RelationExistsException if trying to mark already critical action + * @throws RelationNotExistsException if trying to unmark not critical action + */ setAttributeActionCriticality { @Override public Void call(ApiCaller ac, Deserializer parms) throws PerunException { @@ -4094,7 +4107,8 @@ public Void call(ApiCaller ac, Deserializer parms) throws PerunException { ac.getSession(), ac.getAttributeDefinitionById(parms.readInt("attributeDefinition")), AttributeAction.valueOf(parms.readString("action").toUpperCase()), - parms.readBoolean("critical")); + parms.readBoolean("critical"), + parms.contains("global") ? parms.readBoolean("global") : false); return null; } }, diff --git a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java index 01f8e1b078..c71707d4a6 100644 --- a/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java +++ b/perun-rpc/src/main/java/cz/metacentrum/perun/rpc/methods/MembersManagerMethod.java @@ -1962,6 +1962,28 @@ public String call(ApiCaller ac, Deserializer parms) throws PerunException { } }, + /*# + * Send reminder of username in the given namespace to user's preferred email address. + * + * @param member int Member to get user to send link mail to + * @param namespace String Namespace to change password in (member must have login in it) + * @param emailAttributeURN String URN of the attribute with stored mail + * @param language String 2-char language code of the message + */ + sendUsernameReminderEmail { + @Override + public Void call(ApiCaller ac, Deserializer parms) throws PerunException { + ac.getMembersManager().sendUsernameReminderEmail( + ac.getSession(), + ac.getMemberById(parms.readInt("member")), + parms.readString("namespace"), + parms.readString("emailAttributeURN"), + parms.readString("language") + ); + return null; + } + }, + /*# * Send mail to user's preferred email address with link for non-authz password reset. * Correct authz information is stored in link's URL. diff --git a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl index 8800149fe2..70fa596e2e 100755 --- a/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl +++ b/perun-utils/rpc-methods-javadoc-generator/parseRpcMethods.pl @@ -93,13 +93,15 @@ $objectExamples{"List<RichGroup>"} = $listPrepend . $objectExamples{"RichGroup"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<RichGroup>"}; +$objectExamples{"User"} = "{ \"firstName\" : \"Some\" , \"lastName\" : \"Body\" , \"middleName\" : null , \"titleBefore\" : \"Mgr.\" , \"titleAfter\" : null , \"serviceUser\" : false , \"sponsoredUser\" : false , \"specificUser\" : false , \"majorSpecificType\" : \"NORMAL\" , \"id\" : 34 , \"uuid\" : \"5e5a02dd-f991-4706-a428-69c3ea6c5ce8\" , \"beanName\" : \"User\" }"; +$objectExamples{"List<User>"} = $listPrepend . $objectExamples{"User"} . $listAppend; +$objectExamples{"List"} = $objectExamples{"List<User>"}; + $objectExamples{"Application"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"Application\" }"; $objectExamples{"List<Application>"} = $listPrepend . $objectExamples{"Application"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<Application>"}; -$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; -$objectExamples{"List<RichApplication>"} = $listPrepend . $objectExamples{"RichApplication"} . $listAppend; -$objectExamples{"List"} = $objectExamples{"List<RichApplication>"}; +$objectExamples{"ApplicationForm"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"automaticApproval\" : true , \"automaticApprovalExtension\" : true , \"moduleClassName\" : \"cz.metacentrum.perun.core.impl.modules.attributes.VirtualAttributesModule\" , \"beanName\" : \"ApplicationForm\" }"; $objectExamples{"ApplicationFormItem"} = "{ \"id\" : 12 , \"shortname\" : \"Form item name\' , \"required\" : false , \"updatable\" : true , \"type\" : \"CHECKBOX\" , \"federationAttribute\" : \"\" , \"perunSourceAttribute\" : \"PERUNPEOPLE\" , \"perunDestinationAttribute\" : \"\", \"regex\" : \"\", \"applicationTypes\" : [\"INITIAL\" , \"EXTENSION\"], \"ordnum\" : 5, \"hiddenDependencyItemId\" : 5, \"disabledDependencyItemId\" : 5, \"disabled\" : \"NEVER\", \"hidden\" : \"NEVER\", \"beanName\" : \"ApplicationFormItem\" }"; $objectExamples{"List<ApplicationFormItem>"} = $listPrepend . $objectExamples{"ApplicationFormItem"} . $listAppend; @@ -109,6 +111,10 @@ $objectExamples{"List<ApplicationFormItemData>"} = $listPrepend . $objectExamples{"ApplicationFormItemData"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<ApplicationFormItemData>"}; +$objectExamples{"RichApplication"} = "{ \"id\" : 12 , \"vo\" : ". $objectExamples{"Vo"} . " , \"type\" : \"INITIAL\" , \"fedInfo\" : \"\" , \"state\" : \"NEW\" , \"autoApproveError\" : null , \"extSourceName\" : \"PERUNPEOPLE\" , \"extSourceType\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"user\" : " . $objectExamples{"User"} . ", \"beanName\" : \"RichApplication\", \"formData\" : " . $objectExamples{"List"} . " }"; +$objectExamples{"List<RichApplication>"} = $listPrepend . $objectExamples{"RichApplication"} . $listAppend; +$objectExamples{"List"} = $objectExamples{"List<RichApplication>"}; + $objectExamples{"ApplicationsPageQuery"} = "{ \"pageSize\" : 3 , \"offset\" : 0 , \"order\" : \"ASCENDING\" , \"sortColumn\" : \"ID\" , \"includeGroupApplications\" : true , \"searchString\" : \"Doe\" , \"states\" : [\"VERIFIED\" , \"NEW\"] , \"dateFrom\" : \"2011-05-17\", \"dateTo\" : \"2011-05-17\", \"memberId\" : 10 , \"groupId\" : 10 }"; $objectExamples{"List<ApplicationsPageQuery>"} = $listPrepend . $objectExamples{"ApplicationsPageQuery"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<ApplicationsPageQuery>"}; @@ -120,10 +126,6 @@ $objectExamples{"List<Member>"} = $listPrepend . $objectExamples{"Member"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<Member>"}; -$objectExamples{"User"} = "{ \"firstName\" : \"Some\" , \"lastName\" : \"Body\" , \"middleName\" : null , \"titleBefore\" : \"Mgr.\" , \"titleAfter\" : null , \"serviceUser\" : false , \"sponsoredUser\" : false , \"specificUser\" : false , \"majorSpecificType\" : \"NORMAL\" , \"id\" : 34 , \"uuid\" : \"5e5a02dd-f991-4706-a428-69c3ea6c5ce8\" , \"beanName\" : \"User\" }"; -$objectExamples{"List<User>"} = $listPrepend . $objectExamples{"User"} . $listAppend; -$objectExamples{"List"} = $objectExamples{"List<User>"}; - $objectExamples{"ExtSource"} = "{ \"name\" : \"PERUNPEOPLE\" , \"type\" : \"cz.metacentrum.perun.core.impl.ExtSourceSql\" , \"attributes\" : {} , \"id\" : 2 , \"beanName\" : \"ExtSource\" }"; $objectExamples{"List<ExtSource>"} = $listPrepend . $objectExamples{"ExtSource"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<ExtSource>"}; @@ -272,7 +274,8 @@ $objectExamples{"List<AttributePolicy>"} = $listPrepend . $objectExamples{"AttributePolicy"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<AttributePolicy>"}; -$objectExamples{"AttributePolicyCollection"} = "{ \"id\" : 10 , \"attributeId\" : 2220 , \"action\" : \"READ\" , \"policies\" : " . $objectExamples{"List<AttributePolicyCollection>"} . " }"; +$objectExamples{"AttributePolicyCollection"} = "{ \"id\" : 10 , \"attributeId\" : 2220 , \"action\" : \"READ\" , \"policies\" : " . $objectExamples{"List<AttributePolicy>"} . " }"; + $objectExamples{"List<AttributePolicyCollection>"} = $listPrepend . $objectExamples{"AttributePolicyCollection"} . $listAppend; $objectExamples{"List"} = $objectExamples{"List<AttributePolicyCollection>"}; diff --git a/pom.xml b/pom.xml index 60f5bb0c60..c4fd19ed3f 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.13 + 2.7.14 @@ -69,7 +69,7 @@ 1.6 0.5.10 9.1.22 - directory_v1-rev20230516-2.0.0 + directory_v1-rev20230802-2.0.0 2.2.21.Final 3.2.10.Final 1.1.0.GA