mirror of
https://github.com/Athou/commafeed.git
synced 2026-11-02 18:34:48 +00:00
Compare commits
1 Commits
master
...
renovate/r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e382ed73e |
274
commafeed-client/package-lock.json
generated
274
commafeed-client/package-lock.json
generated
@@ -163,13 +163,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.29.12",
|
"version": "7.29.8",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.12.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
|
||||||
"integrity": "sha512-2W76mhjrpQFO1lK5e8IvGYB2D0YZ7S2R1Z2BjO2ZUYuW+amcpK/z60U6ZZmzskwoa7sJ6XohP/Wg8H5KxXI/IA==",
|
"integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.12",
|
"@babel/parser": "^7.29.8",
|
||||||
"@babel/types": "^7.29.11",
|
"@babel/types": "^7.29.8",
|
||||||
"@jridgewell/gen-mapping": "^0.3.12",
|
"@jridgewell/gen-mapping": "^0.3.12",
|
||||||
"@jridgewell/trace-mapping": "^0.3.28",
|
"@jridgewell/trace-mapping": "^0.3.28",
|
||||||
"jsesc": "^3.0.2"
|
"jsesc": "^3.0.2"
|
||||||
@@ -283,9 +283,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.29.12",
|
"version": "7.29.11",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.12.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz",
|
||||||
"integrity": "sha512-WJ7JBp/Q4+z/ImydtI7jS6NVco5++632ExBemSX+Ysb+G1lCwQ5BVo3SY+jGGOcZXI4ZccbZSUSJLNBW6eEpJg==",
|
"integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.29.11"
|
"@babel/types": "^7.29.11"
|
||||||
@@ -321,17 +321,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse": {
|
"node_modules/@babel/traverse": {
|
||||||
"version": "7.29.12",
|
"version": "7.29.10",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.12.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz",
|
||||||
"integrity": "sha512-MscXe/NAZcOkYEsRajHZFt3ynsb22PUi6CTrp5U7+RYycR9By84mn9mFN54YgfITiqK4hFx3Ju3tZSyzfpS5ew==",
|
"integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.7",
|
"@babel/code-frame": "^7.29.7",
|
||||||
"@babel/generator": "^7.29.12",
|
"@babel/generator": "^7.29.8",
|
||||||
"@babel/helper-globals": "^7.29.7",
|
"@babel/helper-globals": "^7.29.7",
|
||||||
"@babel/parser": "^7.29.12",
|
"@babel/parser": "^7.29.9",
|
||||||
"@babel/template": "^7.29.7",
|
"@babel/template": "^7.29.7",
|
||||||
"@babel/types": "^7.29.11",
|
"@babel/types": "^7.29.8",
|
||||||
"debug": "^4.3.1"
|
"debug": "^4.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -631,9 +631,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@csstools/css-color-parser": {
|
"node_modules/@csstools/css-color-parser": {
|
||||||
"version": "4.2.7",
|
"version": "4.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.6.tgz",
|
||||||
"integrity": "sha512-zgm9yjQz5iJB4VL8RyVwd71TWB8Q89FSxt/NzXoWqPSkEBOVtn9L6vAPtSc9hpKfB0Awo/AL2VKFHjYetBLFHQ==",
|
"integrity": "sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -682,9 +682,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||||
"version": "1.1.16",
|
"version": "1.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.16.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz",
|
||||||
"integrity": "sha512-LsDbJPP/B8cPasUJyTA5S99F7C4G93ngW8hUluXz8CFeOaaRnBPtBiIlvgcPYU0HmHNz8CXg+1vwFrIB4F6Dgw==",
|
"integrity": "sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1581,9 +1581,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@oxc-project/types": {
|
"node_modules/@oxc-project/types": {
|
||||||
"version": "0.156.0",
|
"version": "0.155.0",
|
||||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.156.0.tgz",
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz",
|
||||||
"integrity": "sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw==",
|
"integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/oxc-project"
|
"url": "https://github.com/sponsors/oxc-project"
|
||||||
@@ -1679,9 +1679,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm-eabi": {
|
"node_modules/@rolldown/binding-android-arm-eabi": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz",
|
||||||
"integrity": "sha512-ia3DwAs0OsMGS7+IConptSiyk9pEXkOx/AbJ2n/lAGxH3QkWQus1t8d7gnAT38FCU2sQu8lUbeR6AiZFv4JxaA==",
|
"integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -1695,9 +1695,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm64": {
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz",
|
||||||
"integrity": "sha512-kx5wBiMB6E7TEvpl2B9gjLU004BkvEm2Dr941BFjx/SontPwSzNuQUIoVKxHHoSjpFIfHMmcguyLOtyn0hObRg==",
|
"integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1711,9 +1711,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz",
|
||||||
"integrity": "sha512-qslZrXnKJU4cUpjoAmtAo/KQM9e2IRMlmpig6Zpwj+8KWxW8v+nkAf9216Bsek9lOC1jvg2yCjygCFo2f83msQ==",
|
"integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1727,9 +1727,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-x64": {
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz",
|
||||||
"integrity": "sha512-FdF+y48ND13eAyM2mQn+85kvAqNlvRWROy2rhgA463X/0Gv4vGtazZtfaF1mRBLscYvtOlh4QnLjOWiFzX69OQ==",
|
"integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1743,9 +1743,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz",
|
||||||
"integrity": "sha512-ddjqFM5e95AgLbUPtKIS7V1KTVlA0RUgCVr5mn+/IEhsdy3pr+Vuj/HZ7nlMN2FyxIwUktOH1LEPIQXO1v9y1Q==",
|
"integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1759,9 +1759,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz",
|
||||||
"integrity": "sha512-tHdKWbFkFBI687iim3LIyIvyL7j66VvQYkDPss5Stn/qyE2tWkR1YD0dXmXPFKKqT/o5ZEJVsaRsWS2meQwGOA==",
|
"integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -1775,9 +1775,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz",
|
||||||
"integrity": "sha512-FJiLGAblPBMlBEdbOqw6kW6QcXDWAf/oJu7gYs9ZJZwmg+K+/zjFLxPTIY9izJUzxzc24FHvmseyA0IvJSl17w==",
|
"integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1794,9 +1794,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz",
|
||||||
"integrity": "sha512-hrJpblMsrQ58U9veWpbXzkhV5zStunds6HWLprqq1qauvgBr1E6P+24kLj6lSLI9vjRn/RYpjprhkHQPKGWpNg==",
|
"integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1813,9 +1813,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz",
|
||||||
"integrity": "sha512-qJd/dC6j6CwLG9/s/RP2nb1RZ0GIgj7Ioj7x/SYa3RzjVrlzrmH0fhIOpfvqQDeIsdP0loi+kHaMhJzY1PBIaA==",
|
"integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -1832,9 +1832,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz",
|
||||||
"integrity": "sha512-Vx7Xg5uUJuNcujJjI70OICaH0EYj80BtOLZTSN7uOYg8QOLlyKnaG0IW7Fn/J0IpJsaKzmDKMhOQv1JBM9m9vg==",
|
"integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -1851,9 +1851,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz",
|
||||||
"integrity": "sha512-TNKhgE3uJTsTzatUSSYt0Kgo+Oa2uSlSjz4r1aEIea7HX6ULBym4fAlYUhG1i6pjNf2njfCUAfKwr6C3E9Xg1w==",
|
"integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1870,9 +1870,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz",
|
||||||
"integrity": "sha512-7chXdpjXZ8qG0+mIxECN3cT9kvhZ3/oZhMX6Pp876P7CDrIy8nJswva6Mq357JR7eYXfuskwZxMeV1SzoZdHrA==",
|
"integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1889,9 +1889,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz",
|
||||||
"integrity": "sha512-Kwjt+7uNTJK5/MDkGWGVO2KQPnj8yAV75dMpfwyHZrJSXWHrniOB0aUEWlVYf3H1hOqFLIStLdM5QXq6bnKmOQ==",
|
"integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1905,9 +1905,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz",
|
||||||
"integrity": "sha512-/LH6WfjDRXXJz/p5xl4TmU+jpcnwcxbKEQBW/GZE0JHfN0lGIyTtsUEGaQ/q/GikkGJ2yLKe6pwm7AnEWO8pdw==",
|
"integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1921,9 +1921,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz",
|
||||||
"integrity": "sha512-1lv1KvX0Z871zfk7vw+dnXMMnHNyDwMzWG8Kv3HxDXMOhzCX4SHc7HK319fInP0i+0p9D8brkfzL7pJOaGl+tw==",
|
"integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2158,9 +2158,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
||||||
"integrity": "sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==",
|
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -2202,9 +2202,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "26.6.6",
|
"version": "26.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz",
|
||||||
"integrity": "sha512-MzQj+8Kx3XI40OmUAE6Qoh58ZMNstNNMwn2nfd3qWsfe7nDNSqJNzes4njhz6AHwrSHOffSLwXU6yb474Q4aog==",
|
"integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~8.9.0"
|
"undici-types": "~8.9.0"
|
||||||
@@ -2796,9 +2796,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.11.34",
|
"version": "2.11.33",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.34.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.33.tgz",
|
||||||
"integrity": "sha512-Aqr8mA1kIvyJ3UY5YAY7ib8LSo1DmDMbMnVqGPtsKW1LxaFQFwn9Eb3yvq2fUKJsRvaXjnAV7R5osqETapzNiA==",
|
"integrity": "sha512-yiVKDX+qCMRKBE084lXvq9EcwFUhKZ2nU8TK1EQQuOgPNR/lmbGxXOeDdlGUqC2Zz11Cs9rk1d/8NfgBmlkt0w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"baseline-browser-mapping": "dist/cli.cjs"
|
"baseline-browser-mapping": "dist/cli.cjs"
|
||||||
@@ -2831,9 +2831,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.29.5",
|
"version": "4.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.5.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz",
|
||||||
"integrity": "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==",
|
"integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -2850,10 +2850,10 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.11.33",
|
"baseline-browser-mapping": "^2.11.29",
|
||||||
"caniuse-lite": "^1.0.30001819",
|
"caniuse-lite": "^1.0.30001816",
|
||||||
"electron-to-chromium": "^1.5.464",
|
"electron-to-chromium": "^1.5.459",
|
||||||
"node-releases": "^2.0.60",
|
"node-releases": "^2.0.59",
|
||||||
"update-browserslist-db": "^1.3.4"
|
"update-browserslist-db": "^1.3.4"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -2898,9 +2898,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001820",
|
"version": "1.0.30001819",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001820.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz",
|
||||||
"integrity": "sha512-gfWD6MxqVCqAOwmODfZf7DchWvlrA+SRdNX7ceLfg3xRqe5ssjjpF7EgslC/0tiLiNTajdaNN/q8c/5u/7Gz8w==",
|
"integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -3245,9 +3245,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.472",
|
"version": "1.5.464",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.472.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz",
|
||||||
"integrity": "sha512-s4+nfT29b0FKpVKoaIxl2pWsoNcn7A/nMPSrxayw4T3BsmPeV2lF9Y06BsAQeMGhCTQ4nYJ5XST7jkqe4ZEqFw==",
|
"integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
@@ -3258,9 +3258,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "9.0.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
|
||||||
"integrity": "sha512-oJFNDJg3kTPwAD1cNn2+WmUq4wFoo09npc3Ydf1D7ED4smRiWrsKnaLxykrTWgyGEcusc87P5dxlAleol9kpkg==",
|
"integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4274,9 +4274,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "1.4.5",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz",
|
||||||
"integrity": "sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==",
|
"integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4430,9 +4430,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.63",
|
"version": "2.0.60",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.63.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz",
|
||||||
"integrity": "sha512-6li2QanLsAXpIH2R6j77MzRyvOyrvdTqlKtO4CgBdHGNvN99ZZfyxszHc5o5+IZBWD5u6Hzwg83JoGXmqZNG9Q==",
|
"integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -4536,9 +4536,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ora/node_modules/ansi-regex": {
|
"node_modules/ora/node_modules/ansi-regex": {
|
||||||
"version": "6.5.0",
|
"version": "6.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz",
|
||||||
"integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==",
|
"integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4625,13 +4625,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse5": {
|
"node_modules/parse5": {
|
||||||
"version": "8.1.0",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
|
||||||
"integrity": "sha512-sq3qBFpee5jeocbFbO48VHlGr8sT7mbXS0vxlKN6sottBtJzrOG3998sr0llSZcQAGgfpdmEYAIZE0P9z0dE8w==",
|
"integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"entities": "^9.0.0"
|
"entities": "^8.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
@@ -4694,9 +4694,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.32",
|
"version": "8.5.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz",
|
||||||
"integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==",
|
"integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5228,12 +5228,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.2.16",
|
"version": "1.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz",
|
||||||
"integrity": "sha512-bWvlMpFKiTfDKfnK7FC2MPgPeWCf/Kpwsq49ApdW+FQaBZb6O6dZ4MAPbPUjp7hYQrw/RobqCzn64BRrQecGHw==",
|
"integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.156.0",
|
"@oxc-project/types": "=0.155.0",
|
||||||
"@rolldown/pluginutils": "^1.0.0"
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -5243,21 +5243,21 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rolldown/binding-android-arm-eabi": "1.2.16",
|
"@rolldown/binding-android-arm-eabi": "1.2.15",
|
||||||
"@rolldown/binding-android-arm64": "1.2.16",
|
"@rolldown/binding-android-arm64": "1.2.15",
|
||||||
"@rolldown/binding-darwin-arm64": "1.2.16",
|
"@rolldown/binding-darwin-arm64": "1.2.15",
|
||||||
"@rolldown/binding-darwin-x64": "1.2.16",
|
"@rolldown/binding-darwin-x64": "1.2.15",
|
||||||
"@rolldown/binding-freebsd-x64": "1.2.16",
|
"@rolldown/binding-freebsd-x64": "1.2.15",
|
||||||
"@rolldown/binding-linux-arm-gnueabihf": "1.2.16",
|
"@rolldown/binding-linux-arm-gnueabihf": "1.2.15",
|
||||||
"@rolldown/binding-linux-arm64-gnu": "1.2.16",
|
"@rolldown/binding-linux-arm64-gnu": "1.2.15",
|
||||||
"@rolldown/binding-linux-arm64-musl": "1.2.16",
|
"@rolldown/binding-linux-arm64-musl": "1.2.15",
|
||||||
"@rolldown/binding-linux-ppc64-gnu": "1.2.16",
|
"@rolldown/binding-linux-ppc64-gnu": "1.2.15",
|
||||||
"@rolldown/binding-linux-s390x-gnu": "1.2.16",
|
"@rolldown/binding-linux-s390x-gnu": "1.2.15",
|
||||||
"@rolldown/binding-linux-x64-gnu": "1.2.16",
|
"@rolldown/binding-linux-x64-gnu": "1.2.15",
|
||||||
"@rolldown/binding-linux-x64-musl": "1.2.16",
|
"@rolldown/binding-linux-x64-musl": "1.2.15",
|
||||||
"@rolldown/binding-openharmony-arm64": "1.2.16",
|
"@rolldown/binding-openharmony-arm64": "1.2.15",
|
||||||
"@rolldown/binding-win32-arm64-msvc": "1.2.16",
|
"@rolldown/binding-win32-arm64-msvc": "1.2.15",
|
||||||
"@rolldown/binding-win32-x64-msvc": "1.2.16"
|
"@rolldown/binding-win32-x64-msvc": "1.2.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/saxes": {
|
"node_modules/saxes": {
|
||||||
@@ -5467,9 +5467,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tinybench": {
|
"node_modules/tinybench": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz",
|
||||||
"integrity": "sha512-Al01C2JIBnO2CXCAPbpAl6sUiOZHeThGtHYNTa51JRw9PyWOCdr+9Ong05R4qg/Sk3+H645E8ov1h2c58ityNw==",
|
"integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -5533,9 +5533,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tldts-core": {
|
"node_modules/tldts-core": {
|
||||||
"version": "7.4.20",
|
"version": "7.4.19",
|
||||||
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.20.tgz",
|
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.19.tgz",
|
||||||
"integrity": "sha512-C630TWXZCZmLq0F2bW4QqwnaaxCQ/svpAqvJwnC4JvgEM5ohhBnGsEzTLSOXcN+xjz+kEaHS5uoWIw2klnqd0A==",
|
"integrity": "sha512-goNXV6Ho9TIOfpJZanTd6CKG5DyYgYAL+k+22T0SLh8l6+KHIfBUPFuePuSZv05UttIaaLmI9tb7TmGIdK5AkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -5579,9 +5579,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.9.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/tss-react": {
|
"node_modules/tss-react": {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"@monaco-editor/react": "4.7.0",
|
"@monaco-editor/react": "4.7.0",
|
||||||
"@react-querybuilder/mantine": "8.26.0",
|
"@react-querybuilder/mantine": "8.26.0",
|
||||||
"@reduxjs/toolkit": "2.13.0",
|
"@reduxjs/toolkit": "2.13.0",
|
||||||
"@rolldown/plugin-babel": "0.2.4",
|
"@rolldown/plugin-babel": "0.3.0",
|
||||||
"axios": "1.21.0",
|
"axios": "1.21.0",
|
||||||
"dayjs": "1.11.23",
|
"dayjs": "1.11.23",
|
||||||
"escape-string-regexp": "5.0.0",
|
"escape-string-regexp": "5.0.0",
|
||||||
|
|||||||
@@ -10,10 +10,7 @@ import io.quarkus.security.UnauthorizedException;
|
|||||||
|
|
||||||
import jakarta.annotation.Priority;
|
import jakarta.annotation.Priority;
|
||||||
import jakarta.validation.ValidationException;
|
import jakarta.validation.ValidationException;
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
import jakarta.ws.rs.core.NewCookie;
|
import jakarta.ws.rs.core.NewCookie;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
|
||||||
import jakarta.ws.rs.ext.Provider;
|
import jakarta.ws.rs.ext.Provider;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -52,19 +49,8 @@ public class ExceptionMappers {
|
|||||||
|
|
||||||
@ServerExceptionMapper(AuthenticationFailedException.class)
|
@ServerExceptionMapper(AuthenticationFailedException.class)
|
||||||
public RestResponse<AuthenticationFailed> authenticationFailed(
|
public RestResponse<AuthenticationFailed> authenticationFailed(
|
||||||
AuthenticationFailedException e, HttpHeaders headers, UriInfo uriInfo) {
|
AuthenticationFailedException e) {
|
||||||
NewCookie logoutCookie = cookieService.buildLogoutCookie();
|
NewCookie logoutCookie = cookieService.buildLogoutCookie();
|
||||||
|
|
||||||
// a browser navigating with an invalidated session would otherwise display the error
|
|
||||||
// instead of the page, retry the request now that the cookie is removed
|
|
||||||
if (headers.getAcceptableMediaTypes().stream()
|
|
||||||
.anyMatch(m -> MediaType.TEXT_HTML_TYPE.isCompatible(m) && !m.isWildcardType())) {
|
|
||||||
return ResponseBuilder.<AuthenticationFailed>create(Status.TEMPORARY_REDIRECT)
|
|
||||||
.location(uriInfo.getRequestUri())
|
|
||||||
.cookie(logoutCookie)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResponseBuilder.create(Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage()))
|
return ResponseBuilder.create(Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage()))
|
||||||
.cookie(logoutCookie)
|
.cookie(logoutCookie)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ public class HttpClientFactory {
|
|||||||
|
|
||||||
private static final DnsResolver DNS_RESOLVER = SystemDefaultDnsResolver.INSTANCE;
|
private static final DnsResolver DNS_RESOLVER = SystemDefaultDnsResolver.INSTANCE;
|
||||||
private static final IPAddress CGNAT_RANGE = new IPAddressString("100.64.0.0/10").getAddress();
|
private static final IPAddress CGNAT_RANGE = new IPAddressString("100.64.0.0/10").getAddress();
|
||||||
private static final IPAddress NAT64_LOCAL_USE_RANGE =
|
|
||||||
new IPAddressString("64:ff9b:1::/48").getAddress();
|
|
||||||
|
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
private final CommaFeedVersion version;
|
private final CommaFeedVersion version;
|
||||||
@@ -151,11 +149,7 @@ public class HttpClientFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isLocalAddress(IPAddress ip) {
|
private static boolean isLocalAddress(IPAddress ip) {
|
||||||
if (ip.isLocal()
|
if (ip.isLocal() || ip.isLoopback() || ip.isMulticast() || CGNAT_RANGE.contains(ip)) {
|
||||||
|| ip.isLoopback()
|
|
||||||
|| ip.isMulticast()
|
|
||||||
|| CGNAT_RANGE.contains(ip)
|
|
||||||
|| NAT64_LOCAL_USE_RANGE.contains(ip)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ public class User extends AbstractModel {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private boolean disabled;
|
private boolean disabled;
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private int sessionVersion;
|
|
||||||
|
|
||||||
@Column private Instant lastLogin;
|
@Column private Instant lastLogin;
|
||||||
|
|
||||||
@Column private Instant created;
|
@Column private Instant created;
|
||||||
|
|||||||
@@ -155,17 +155,6 @@ public class UserService {
|
|||||||
userDAO.delete(user);
|
userDAO.delete(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changePassword(User user, String password) {
|
|
||||||
user.setPassword(encryptionService.getEncryptedPassword(password, user.getSalt()));
|
|
||||||
userDAO.merge(user);
|
|
||||||
|
|
||||||
invalidateSessions(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidateSessions(User user) {
|
|
||||||
user.setSessionVersion(user.getSessionVersion() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String generateApiKey(User user) {
|
public String generateApiKey(User user) {
|
||||||
byte[] key =
|
byte[] key =
|
||||||
encryptionService.getEncryptedPassword(
|
encryptionService.getEncryptedPassword(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.commafeed.backend.dao.UserRoleDAO;
|
|||||||
import com.commafeed.backend.model.User;
|
import com.commafeed.backend.model.User;
|
||||||
import com.commafeed.backend.model.UserRole;
|
import com.commafeed.backend.model.UserRole;
|
||||||
import com.commafeed.backend.model.UserRole.Role;
|
import com.commafeed.backend.model.UserRole.Role;
|
||||||
|
import com.commafeed.backend.service.PasswordEncryptionService;
|
||||||
import com.commafeed.backend.service.UserService;
|
import com.commafeed.backend.service.UserService;
|
||||||
import com.commafeed.frontend.model.UserModel;
|
import com.commafeed.frontend.model.UserModel;
|
||||||
import com.commafeed.frontend.model.request.AdminSaveUserRequest;
|
import com.commafeed.frontend.model.request.AdminSaveUserRequest;
|
||||||
@@ -55,6 +56,7 @@ public class AdminREST {
|
|||||||
private final UserDAO userDAO;
|
private final UserDAO userDAO;
|
||||||
private final UserRoleDAO userRoleDAO;
|
private final UserRoleDAO userRoleDAO;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final PasswordEncryptionService encryptionService;
|
||||||
private final MetricRegistry metrics;
|
private final MetricRegistry metrics;
|
||||||
|
|
||||||
@Path("/user/save")
|
@Path("/user/save")
|
||||||
@@ -100,15 +102,10 @@ public class AdminREST {
|
|||||||
User u = userDAO.findById(id);
|
User u = userDAO.findById(id);
|
||||||
u.setName(req.getName());
|
u.setName(req.getName());
|
||||||
if (StringUtils.isNotBlank(req.getPassword())) {
|
if (StringUtils.isNotBlank(req.getPassword())) {
|
||||||
userService.changePassword(u, req.getPassword());
|
u.setPassword(
|
||||||
if (StringUtils.isNotBlank(u.getApiKey())) {
|
encryptionService.getEncryptedPassword(req.getPassword(), u.getSalt()));
|
||||||
u.setApiKey(userService.generateApiKey(u));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
u.setEmail(req.getEmail());
|
u.setEmail(req.getEmail());
|
||||||
if (!u.isDisabled() && !req.isEnabled()) {
|
|
||||||
userService.invalidateSessions(u);
|
|
||||||
}
|
|
||||||
u.setDisabled(!req.isEnabled());
|
u.setDisabled(!req.isEnabled());
|
||||||
|
|
||||||
Set<Role> roles = userRoleDAO.findRoles(u);
|
Set<Role> roles = userRoleDAO.findRoles(u);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.commafeed.backend.model.UserSettings.ReadingMode;
|
|||||||
import com.commafeed.backend.model.UserSettings.ReadingOrder;
|
import com.commafeed.backend.model.UserSettings.ReadingOrder;
|
||||||
import com.commafeed.backend.model.UserSettings.ScrollMode;
|
import com.commafeed.backend.model.UserSettings.ScrollMode;
|
||||||
import com.commafeed.backend.service.MailService;
|
import com.commafeed.backend.service.MailService;
|
||||||
|
import com.commafeed.backend.service.PasswordEncryptionService;
|
||||||
import com.commafeed.backend.service.PushNotificationService;
|
import com.commafeed.backend.service.PushNotificationService;
|
||||||
import com.commafeed.backend.service.UserService;
|
import com.commafeed.backend.service.UserService;
|
||||||
import com.commafeed.backend.service.db.DatabaseStartupService;
|
import com.commafeed.backend.service.db.DatabaseStartupService;
|
||||||
@@ -35,7 +36,6 @@ import com.commafeed.frontend.model.request.RegistrationRequest;
|
|||||||
import com.commafeed.security.AuthenticationContext;
|
import com.commafeed.security.AuthenticationContext;
|
||||||
import com.commafeed.security.Roles;
|
import com.commafeed.security.Roles;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.html.HtmlEscapers;
|
|
||||||
|
|
||||||
import jakarta.annotation.security.PermitAll;
|
import jakarta.annotation.security.PermitAll;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
@@ -86,6 +86,7 @@ public class UserREST {
|
|||||||
private final UserRoleDAO userRoleDAO;
|
private final UserRoleDAO userRoleDAO;
|
||||||
private final UserSettingsDAO userSettingsDAO;
|
private final UserSettingsDAO userSettingsDAO;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final PasswordEncryptionService encryptionService;
|
||||||
private final DatabaseStartupService databaseStartupService;
|
private final DatabaseStartupService databaseStartupService;
|
||||||
private final MailService mailService;
|
private final MailService mailService;
|
||||||
private final CommaFeedConfiguration config;
|
private final CommaFeedConfiguration config;
|
||||||
@@ -315,7 +316,10 @@ public class UserREST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(request.getNewPassword())) {
|
if (StringUtils.isNotBlank(request.getNewPassword())) {
|
||||||
userService.changePassword(user, request.getNewPassword());
|
byte[] password =
|
||||||
|
encryptionService.getEncryptedPassword(
|
||||||
|
request.getNewPassword(), user.getSalt());
|
||||||
|
user.setPassword(password);
|
||||||
user.setApiKey(userService.generateApiKey(user));
|
user.setApiKey(userService.generateApiKey(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +417,7 @@ public class UserREST {
|
|||||||
Urls.removeTrailingSlash(config.passwordRecoveryPublicBaseUrl().orElseThrow());
|
Urls.removeTrailingSlash(config.passwordRecoveryPublicBaseUrl().orElseThrow());
|
||||||
return String.format(
|
return String.format(
|
||||||
"You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.",
|
"You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.",
|
||||||
HtmlEscapers.htmlEscaper().escape(user.getName()), callbackUrl(user, publicUrl));
|
user.getName(), callbackUrl(user, publicUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String callbackUrl(User user, String publicUrl) throws URISyntaxException {
|
private String callbackUrl(User user, String publicUrl) throws URISyntaxException {
|
||||||
@@ -451,7 +455,8 @@ public class UserREST {
|
|||||||
return Response.status(Status.UNAUTHORIZED).entity("Token expired.").build();
|
return Response.status(Status.UNAUTHORIZED).entity("Token expired.").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
userService.changePassword(user, password);
|
byte[] encryptedPassword = encryptionService.getEncryptedPassword(password, user.getSalt());
|
||||||
|
user.setPassword(encryptedPassword);
|
||||||
if (StringUtils.isNotBlank(user.getApiKey())) {
|
if (StringUtils.isNotBlank(user.getApiKey())) {
|
||||||
user.setApiKey(userService.generateApiKey(user));
|
user.setApiKey(userService.generateApiKey(user));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ public class AuthenticationContext {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = securityIdentity.getPrincipal().getName();
|
String userId = securityIdentity.getPrincipal().getName();
|
||||||
if (name == null) {
|
if (userId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return userDAO.findById(SessionPrincipal.parse(name).userId());
|
return userDAO.findById(Long.valueOf(userId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.commafeed.security;
|
|
||||||
|
|
||||||
import com.commafeed.backend.model.User;
|
|
||||||
|
|
||||||
/** Value of the security principal, which is also what ends up in the encrypted session cookie. */
|
|
||||||
public record SessionPrincipal(long userId, int sessionVersion) {
|
|
||||||
|
|
||||||
private static final String SEPARATOR = ":";
|
|
||||||
|
|
||||||
public static SessionPrincipal of(User user) {
|
|
||||||
return new SessionPrincipal(user.getId(), user.getSessionVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SessionPrincipal parse(String name) {
|
|
||||||
// cookies created before session versions were introduced only contain the user id
|
|
||||||
if (!name.contains(SEPARATOR)) {
|
|
||||||
return new SessionPrincipal(Long.parseLong(name), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] parts = name.split(SEPARATOR, 2);
|
|
||||||
return new SessionPrincipal(Long.parseLong(parts[0]), Integer.parseInt(parts[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toName() {
|
|
||||||
return userId + SEPARATOR + sessionVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import com.commafeed.backend.dao.UnitOfWork;
|
|||||||
import com.commafeed.backend.model.User;
|
import com.commafeed.backend.model.User;
|
||||||
import com.commafeed.backend.model.UserRole.Role;
|
import com.commafeed.backend.model.UserRole.Role;
|
||||||
import com.commafeed.backend.service.UserService;
|
import com.commafeed.backend.service.UserService;
|
||||||
import com.commafeed.security.SessionPrincipal;
|
|
||||||
|
|
||||||
import io.quarkus.security.AuthenticationFailedException;
|
import io.quarkus.security.AuthenticationFailedException;
|
||||||
import io.quarkus.security.identity.AuthenticationRequestContext;
|
import io.quarkus.security.identity.AuthenticationRequestContext;
|
||||||
@@ -48,7 +47,7 @@ public class DatabaseApiKeyIdentityProvider
|
|||||||
|
|
||||||
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get()));
|
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get()));
|
||||||
return QuarkusSecurityIdentity.builder()
|
return QuarkusSecurityIdentity.builder()
|
||||||
.setPrincipal(new QuarkusPrincipal(SessionPrincipal.of(user.get()).toName()))
|
.setPrincipal(new QuarkusPrincipal(String.valueOf(user.get().getId())))
|
||||||
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
|
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
|
||||||
.build();
|
.build();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.commafeed.backend.model.UserRole.Role;
|
|||||||
import com.commafeed.backend.service.UserService;
|
import com.commafeed.backend.service.UserService;
|
||||||
import com.commafeed.frontend.exception.CommaFeedApplicationException;
|
import com.commafeed.frontend.exception.CommaFeedApplicationException;
|
||||||
import com.commafeed.frontend.exception.CommaFeedExceptionType;
|
import com.commafeed.frontend.exception.CommaFeedExceptionType;
|
||||||
import com.commafeed.security.SessionPrincipal;
|
|
||||||
|
|
||||||
import io.quarkus.security.identity.AuthenticationRequestContext;
|
import io.quarkus.security.identity.AuthenticationRequestContext;
|
||||||
import io.quarkus.security.identity.IdentityProvider;
|
import io.quarkus.security.identity.IdentityProvider;
|
||||||
@@ -53,7 +52,7 @@ public class DatabaseUsernamePasswordIdentityProvider
|
|||||||
|
|
||||||
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get()));
|
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user.get()));
|
||||||
return QuarkusSecurityIdentity.builder()
|
return QuarkusSecurityIdentity.builder()
|
||||||
.setPrincipal(new QuarkusPrincipal(SessionPrincipal.of(user.get()).toName()))
|
.setPrincipal(new QuarkusPrincipal(String.valueOf(user.get().getId())))
|
||||||
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
|
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
|
||||||
.build();
|
.build();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.commafeed.backend.model.User;
|
|||||||
import com.commafeed.backend.model.UserRole.Role;
|
import com.commafeed.backend.model.UserRole.Role;
|
||||||
import com.commafeed.backend.service.UserService;
|
import com.commafeed.backend.service.UserService;
|
||||||
import com.commafeed.backend.service.internal.PostLoginActivities;
|
import com.commafeed.backend.service.internal.PostLoginActivities;
|
||||||
import com.commafeed.security.SessionPrincipal;
|
|
||||||
|
|
||||||
import io.quarkus.security.AuthenticationFailedException;
|
import io.quarkus.security.AuthenticationFailedException;
|
||||||
import io.quarkus.security.identity.AuthenticationRequestContext;
|
import io.quarkus.security.identity.AuthenticationRequestContext;
|
||||||
@@ -42,17 +41,11 @@ public class TrustedIdentityProvider implements IdentityProvider<TrustedAuthenti
|
|||||||
public Uni<SecurityIdentity> authenticate(
|
public Uni<SecurityIdentity> authenticate(
|
||||||
TrustedAuthenticationRequest request, AuthenticationRequestContext context) {
|
TrustedAuthenticationRequest request, AuthenticationRequestContext context) {
|
||||||
return context.runBlocking(() -> {
|
return context.runBlocking(() -> {
|
||||||
SessionPrincipal principal = SessionPrincipal.parse(request.getPrincipal());
|
Long userId = Long.valueOf(request.getPrincipal());
|
||||||
User user = unitOfWork.call(() -> userDAO.findById(principal.userId()));
|
User user = unitOfWork.call(() -> userDAO.findById(userId));
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new AuthenticationFailedException("user not found");
|
throw new AuthenticationFailedException("user not found");
|
||||||
}
|
}
|
||||||
if (user.isDisabled()) {
|
|
||||||
throw new AuthenticationFailedException("user disabled");
|
|
||||||
}
|
|
||||||
if (user.getSessionVersion() != principal.sessionVersion()) {
|
|
||||||
throw new AuthenticationFailedException("session invalidated");
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute post login activities manually because we didn't call login() since
|
// execute post login activities manually because we didn't call login() since
|
||||||
// we received
|
// we received
|
||||||
@@ -61,7 +54,7 @@ public class TrustedIdentityProvider implements IdentityProvider<TrustedAuthenti
|
|||||||
|
|
||||||
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user));
|
Set<Role> roles = unitOfWork.call(() -> userService.getRoles(user));
|
||||||
return QuarkusSecurityIdentity.builder()
|
return QuarkusSecurityIdentity.builder()
|
||||||
.setPrincipal(new QuarkusPrincipal(principal.toName()))
|
.setPrincipal(new QuarkusPrincipal(String.valueOf(userId)))
|
||||||
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
|
.addRoles(roles.stream().map(Enum::name).collect(Collectors.toSet()))
|
||||||
.build();
|
.build();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
|
||||||
|
|
||||||
<changeSet id="user-session-version" author="athou">
|
|
||||||
<addColumn tableName="USERS">
|
|
||||||
<column name="sessionVersion" type="INT" valueNumeric="0" defaultValueNumeric="0">
|
|
||||||
<constraints nullable="false" />
|
|
||||||
</column>
|
|
||||||
</addColumn>
|
|
||||||
</changeSet>
|
|
||||||
|
|
||||||
</databaseChangeLog>
|
|
||||||
@@ -39,6 +39,5 @@
|
|||||||
<include file="changelogs/db.changelog-5.12.xml" />
|
<include file="changelogs/db.changelog-5.12.xml" />
|
||||||
<include file="changelogs/db.changelog-7.0.xml" />
|
<include file="changelogs/db.changelog-7.0.xml" />
|
||||||
<include file="changelogs/db.changelog-7.2.xml" />
|
<include file="changelogs/db.changelog-7.2.xml" />
|
||||||
<include file="changelogs/db.changelog-7.5.xml" />
|
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
@@ -561,17 +561,6 @@ class HttpGetterTest {
|
|||||||
() -> getter.get("http://[64:ff9b::a00:1]")); // 10.0.0.1
|
() -> getter.get("http://[64:ff9b::a00:1]")); // 10.0.0.1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void nat64LocalUse() {
|
|
||||||
// 64:ff9b:1::/48 is reserved for local use (RFC 8215), the whole range is blocked
|
|
||||||
Assertions.assertThrows(
|
|
||||||
UnknownHostException.class,
|
|
||||||
() -> getter.get("http://[64:ff9b:1::a9fe:a9fe]")); // 169.254.169.254
|
|
||||||
Assertions.assertThrows(
|
|
||||||
UnknownHostException.class,
|
|
||||||
() -> getter.get("http://[64:ff9b:1:ffff::a00:1]")); // 10.0.0.1
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sixToFour() {
|
void sixToFour() {
|
||||||
// 2002::/16 embeds the IPv4 address in bits 16-47 (RFC 3056)
|
// 2002::/16 embeds the IPv4 address in bits 16-47 (RFC 3056)
|
||||||
|
|||||||
@@ -109,15 +109,15 @@ public abstract class BaseIT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<HttpCookie> login() {
|
protected List<HttpCookie> login() {
|
||||||
return login(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<HttpCookie> login(String userName, String password) {
|
|
||||||
List<Header> setCookieHeaders =
|
List<Header> setCookieHeaders =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.auth()
|
.auth()
|
||||||
.none()
|
.none()
|
||||||
.formParams("j_username", userName, "j_password", password)
|
.formParams(
|
||||||
|
"j_username",
|
||||||
|
TestConstants.ADMIN_USERNAME,
|
||||||
|
"j_password",
|
||||||
|
TestConstants.ADMIN_PASSWORD)
|
||||||
.post("j_security_check")
|
.post("j_security_check")
|
||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_OK)
|
.statusCode(HttpStatus.SC_OK)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.commafeed.TestConstants;
|
|||||||
import com.commafeed.frontend.exception.CommaFeedExceptionType;
|
import com.commafeed.frontend.exception.CommaFeedExceptionType;
|
||||||
import com.commafeed.frontend.model.Entries;
|
import com.commafeed.frontend.model.Entries;
|
||||||
import com.commafeed.frontend.model.UserModel;
|
import com.commafeed.frontend.model.UserModel;
|
||||||
import com.commafeed.frontend.model.request.AdminSaveUserRequest;
|
|
||||||
import com.commafeed.frontend.model.request.MarkRequest;
|
import com.commafeed.frontend.model.request.MarkRequest;
|
||||||
import com.commafeed.frontend.model.request.ProfileModificationRequest;
|
import com.commafeed.frontend.model.request.ProfileModificationRequest;
|
||||||
import com.commafeed.frontend.model.request.SubscribeRequest;
|
import com.commafeed.frontend.model.request.SubscribeRequest;
|
||||||
@@ -18,7 +17,6 @@ import io.restassured.http.ContentType;
|
|||||||
import jakarta.ws.rs.core.HttpHeaders;
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.hamcrest.Matchers;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -30,10 +28,6 @@ import java.util.stream.Collectors;
|
|||||||
@QuarkusTest
|
@QuarkusTest
|
||||||
class SecurityIT extends BaseIT {
|
class SecurityIT extends BaseIT {
|
||||||
|
|
||||||
private static final String USER_NAME = "session-user";
|
|
||||||
private static final String USER_PASSWORD = "MyPassword123!";
|
|
||||||
private static final String NEW_USER_PASSWORD = "MyNewPassword123!";
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||||
@@ -190,232 +184,4 @@ class SecurityIT extends BaseIT {
|
|||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_UNAUTHORIZED);
|
.statusCode(HttpStatus.SC_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void selfPasswordChangeInvalidatesSessions() {
|
|
||||||
createUser(false);
|
|
||||||
String sessionA = session(USER_NAME, USER_PASSWORD);
|
|
||||||
String sessionB = session(USER_NAME, USER_PASSWORD);
|
|
||||||
String adminSession = session(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
|
||||||
|
|
||||||
ProfileModificationRequest req = new ProfileModificationRequest();
|
|
||||||
req.setCurrentPassword(USER_PASSWORD);
|
|
||||||
req.setNewPassword(NEW_USER_PASSWORD);
|
|
||||||
RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, sessionA)
|
|
||||||
.body(req)
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.post("rest/user/profile")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_OK);
|
|
||||||
|
|
||||||
assertSessionStatus(sessionA, HttpStatus.SC_UNAUTHORIZED);
|
|
||||||
assertSessionStatus(sessionB, HttpStatus.SC_UNAUTHORIZED);
|
|
||||||
assertSessionStatus(adminSession, HttpStatus.SC_OK);
|
|
||||||
assertSessionStatus(session(USER_NAME, NEW_USER_PASSWORD), HttpStatus.SC_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void profileChangeWithoutNewPasswordKeepsSessions() {
|
|
||||||
createUser(false);
|
|
||||||
String session = session(USER_NAME, USER_PASSWORD);
|
|
||||||
|
|
||||||
ProfileModificationRequest req = new ProfileModificationRequest();
|
|
||||||
req.setCurrentPassword(USER_PASSWORD);
|
|
||||||
req.setEmail("session-user@commafeed.com");
|
|
||||||
RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.body(req)
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.post("rest/user/profile")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_OK);
|
|
||||||
|
|
||||||
assertSessionStatus(session, HttpStatus.SC_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void adminPasswordChangeInvalidatesSessionsAndRotatesApiKey() {
|
|
||||||
Long id = createUser(false);
|
|
||||||
String session = session(USER_NAME, USER_PASSWORD);
|
|
||||||
|
|
||||||
ProfileModificationRequest apiKeyReq = new ProfileModificationRequest();
|
|
||||||
apiKeyReq.setCurrentPassword(USER_PASSWORD);
|
|
||||||
apiKeyReq.setNewApiKey(true);
|
|
||||||
RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.body(apiKeyReq)
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.post("rest/user/profile")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_OK);
|
|
||||||
String apiKey = profile(session).getApiKey();
|
|
||||||
Assertions.assertNotNull(apiKey);
|
|
||||||
|
|
||||||
AdminSaveUserRequest req = saveUserRequest(false);
|
|
||||||
req.setId(id);
|
|
||||||
req.setPassword(NEW_USER_PASSWORD);
|
|
||||||
saveUser(req);
|
|
||||||
|
|
||||||
assertSessionStatus(session, HttpStatus.SC_UNAUTHORIZED);
|
|
||||||
String newSession = session(USER_NAME, NEW_USER_PASSWORD);
|
|
||||||
assertSessionStatus(newSession, HttpStatus.SC_OK);
|
|
||||||
Assertions.assertNotEquals(apiKey, profile(newSession).getApiKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void adminPasswordChangeDoesNotCreateApiKey() {
|
|
||||||
Long id = createUser(false);
|
|
||||||
|
|
||||||
AdminSaveUserRequest req = saveUserRequest(false);
|
|
||||||
req.setId(id);
|
|
||||||
req.setPassword(NEW_USER_PASSWORD);
|
|
||||||
saveUser(req);
|
|
||||||
|
|
||||||
Assertions.assertNull(profile(session(USER_NAME, NEW_USER_PASSWORD)).getApiKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void disablingUserInvalidatesSessions() {
|
|
||||||
Long id = createUser(false);
|
|
||||||
String session = session(USER_NAME, USER_PASSWORD);
|
|
||||||
assertSessionStatus(session, HttpStatus.SC_OK);
|
|
||||||
|
|
||||||
AdminSaveUserRequest req = saveUserRequest(false);
|
|
||||||
req.setId(id);
|
|
||||||
req.setEnabled(false);
|
|
||||||
saveUser(req);
|
|
||||||
|
|
||||||
assertSessionStatus(session, HttpStatus.SC_UNAUTHORIZED);
|
|
||||||
|
|
||||||
// sessions opened before the user was disabled stay invalid after enabling it again
|
|
||||||
req.setEnabled(true);
|
|
||||||
saveUser(req);
|
|
||||||
|
|
||||||
assertSessionStatus(session, HttpStatus.SC_UNAUTHORIZED);
|
|
||||||
assertSessionStatus(session(USER_NAME, USER_PASSWORD), HttpStatus.SC_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void savingUserWithoutChangesKeepsSessions() {
|
|
||||||
Long id = createUser(false);
|
|
||||||
String session = session(USER_NAME, USER_PASSWORD);
|
|
||||||
|
|
||||||
AdminSaveUserRequest req = saveUserRequest(false);
|
|
||||||
req.setId(id);
|
|
||||||
saveUser(req);
|
|
||||||
|
|
||||||
assertSessionStatus(session, HttpStatus.SC_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void disablingAdminRevokesAdminAccess() {
|
|
||||||
Long id = createUser(true);
|
|
||||||
String session = session(USER_NAME, USER_PASSWORD);
|
|
||||||
RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.get("rest/admin/metrics")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_OK);
|
|
||||||
|
|
||||||
AdminSaveUserRequest req = saveUserRequest(true);
|
|
||||||
req.setId(id);
|
|
||||||
req.setEnabled(false);
|
|
||||||
saveUser(req);
|
|
||||||
|
|
||||||
RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.get("rest/admin/metrics")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void invalidatedSessionRemovesCookie() {
|
|
||||||
Long id = createUser(false);
|
|
||||||
String session = session(USER_NAME, USER_PASSWORD);
|
|
||||||
|
|
||||||
AdminSaveUserRequest req = saveUserRequest(false);
|
|
||||||
req.setId(id);
|
|
||||||
req.setEnabled(false);
|
|
||||||
saveUser(req);
|
|
||||||
|
|
||||||
// api calls receive an error
|
|
||||||
String apiSetCookie =
|
|
||||||
RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.get("rest/user/profile")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_UNAUTHORIZED)
|
|
||||||
.extract()
|
|
||||||
.header(HttpHeaders.SET_COOKIE);
|
|
||||||
Assertions.assertTrue(apiSetCookie.contains("Max-Age=0"));
|
|
||||||
|
|
||||||
// browsers are redirected to the page they asked for, this time without the cookie
|
|
||||||
String browserSetCookie =
|
|
||||||
RestAssured.given()
|
|
||||||
.redirects()
|
|
||||||
.follow(false)
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.header(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,*/*;q=0.8")
|
|
||||||
.get("next?category=all")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT)
|
|
||||||
.header(HttpHeaders.LOCATION, Matchers.endsWith("/next?category=all"))
|
|
||||||
.extract()
|
|
||||||
.header(HttpHeaders.SET_COOKIE);
|
|
||||||
Assertions.assertTrue(browserSetCookie.contains("Max-Age=0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long createUser(boolean admin) {
|
|
||||||
AdminSaveUserRequest req = saveUserRequest(admin);
|
|
||||||
req.setPassword(USER_PASSWORD);
|
|
||||||
return saveUser(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AdminSaveUserRequest saveUserRequest(boolean admin) {
|
|
||||||
AdminSaveUserRequest req = new AdminSaveUserRequest();
|
|
||||||
req.setName(USER_NAME);
|
|
||||||
req.setEnabled(true);
|
|
||||||
req.setAdmin(admin);
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long saveUser(AdminSaveUserRequest req) {
|
|
||||||
return RestAssured.given()
|
|
||||||
.auth()
|
|
||||||
.preemptive()
|
|
||||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
|
||||||
.body(req)
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.post("rest/admin/user/save")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_OK)
|
|
||||||
.extract()
|
|
||||||
.as(Long.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String session(String userName, String password) {
|
|
||||||
return login(userName, password).stream()
|
|
||||||
.map(HttpCookie::toString)
|
|
||||||
.collect(Collectors.joining(";"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserModel profile(String session) {
|
|
||||||
return RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.get("rest/user/profile")
|
|
||||||
.then()
|
|
||||||
.statusCode(HttpStatus.SC_OK)
|
|
||||||
.extract()
|
|
||||||
.as(UserModel.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertSessionStatus(String session, int status) {
|
|
||||||
RestAssured.given()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.get("rest/user/profile")
|
|
||||||
.then()
|
|
||||||
.statusCode(status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.commafeed.TestConstants;
|
|||||||
import com.commafeed.frontend.model.Settings;
|
import com.commafeed.frontend.model.Settings;
|
||||||
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
|
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
|
||||||
import com.commafeed.frontend.model.request.PasswordResetRequest;
|
import com.commafeed.frontend.model.request.PasswordResetRequest;
|
||||||
import com.commafeed.frontend.model.request.RegistrationRequest;
|
|
||||||
import com.commafeed.integration.BaseIT;
|
import com.commafeed.integration.BaseIT;
|
||||||
|
|
||||||
import io.quarkus.mailer.MockMailbox;
|
import io.quarkus.mailer.MockMailbox;
|
||||||
@@ -14,21 +13,17 @@ import io.restassured.http.ContentType;
|
|||||||
import io.vertx.ext.mail.MailMessage;
|
import io.vertx.ext.mail.MailMessage;
|
||||||
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
|
||||||
|
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
import org.jsoup.select.Elements;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.net.HttpCookie;
|
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@QuarkusTest
|
@QuarkusTest
|
||||||
class UserIT extends BaseIT {
|
class UserIT extends BaseIT {
|
||||||
@@ -52,9 +47,6 @@ class UserIT extends BaseIT {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resetPassword() {
|
void resetPassword() {
|
||||||
String session =
|
|
||||||
login().stream().map(HttpCookie::toString).collect(Collectors.joining(";"));
|
|
||||||
|
|
||||||
PasswordResetRequest req = new PasswordResetRequest();
|
PasswordResetRequest req = new PasswordResetRequest();
|
||||||
req.setEmail("admin@commafeed.com");
|
req.setEmail("admin@commafeed.com");
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@@ -107,59 +99,11 @@ class UserIT extends BaseIT {
|
|||||||
.then()
|
.then()
|
||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
|
|
||||||
// sessions opened before the reset are no longer valid
|
|
||||||
RestAssured.given()
|
|
||||||
.auth()
|
|
||||||
.none()
|
|
||||||
.header(HttpHeaders.COOKIE, session)
|
|
||||||
.get("rest/user/settings")
|
|
||||||
.then()
|
|
||||||
.statusCode(401);
|
|
||||||
|
|
||||||
RestAssured.authentication =
|
RestAssured.authentication =
|
||||||
RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, newPassword);
|
RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, newPassword);
|
||||||
RestAssured.given().get("rest/user/settings").then().statusCode(200);
|
RestAssured.given().get("rest/user/settings").then().statusCode(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void resetPasswordEscapesUsernameInEmail() {
|
|
||||||
String name = "<a href=//evil.example>CLICK</a>";
|
|
||||||
String email = "victim@commafeed.com";
|
|
||||||
|
|
||||||
RegistrationRequest registrationReq = new RegistrationRequest();
|
|
||||||
registrationReq.setName(name);
|
|
||||||
registrationReq.setPassword("MyPassword123!");
|
|
||||||
registrationReq.setEmail(email);
|
|
||||||
RestAssured.given()
|
|
||||||
.body(registrationReq)
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.post("rest/user/register")
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
|
|
||||||
PasswordResetRequest req = new PasswordResetRequest();
|
|
||||||
req.setEmail(email);
|
|
||||||
RestAssured.given()
|
|
||||||
.body(req)
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.post("rest/user/passwordReset")
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
|
|
||||||
List<MailMessage> mails = mailbox.getMailMessagesSentTo(email);
|
|
||||||
Assertions.assertEquals(1, mails.size());
|
|
||||||
|
|
||||||
String html = mails.getFirst().getHtml();
|
|
||||||
Assertions.assertTrue(
|
|
||||||
html.startsWith(
|
|
||||||
"You asked for password recovery for account '<a href=//evil.example>CLICK</a>'"));
|
|
||||||
|
|
||||||
Elements links = Jsoup.parse(html).select("a");
|
|
||||||
Assertions.assertEquals(1, links.size());
|
|
||||||
Assertions.assertTrue(
|
|
||||||
links.getFirst().attr("href").startsWith("https://commafeed.example.com"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void saveSettings() {
|
void saveSettings() {
|
||||||
Settings settings =
|
Settings settings =
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.commafeed.security;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
class SessionPrincipalTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void roundTrip() {
|
|
||||||
SessionPrincipal principal = new SessionPrincipal(12, 3);
|
|
||||||
Assertions.assertEquals("12:3", principal.toName());
|
|
||||||
Assertions.assertEquals(principal, SessionPrincipal.parse(principal.toName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void cookieWithoutSessionVersion() {
|
|
||||||
Assertions.assertEquals(new SessionPrincipal(12, 0), SessionPrincipal.parse("12"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user