mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 06:03:34 +01:00
Feat/oauth2 (#6281): Added oauth to API paths
* Added oauth provider. * Fixed provider. * Added auth flow. * Fixed auth flow and added scaffolding vite config. * Added working oauth2. * Fixed dockerfile. * Adapted run.sh script * Moved api tests to oauth2. * Updated security schemes. * Removed api key from existance. * Fixed installation * Added missing issuer in config. * Fixed dev dependencies. * Updated lock file.
This commit is contained in:
parent
562177022f
commit
fb56809e55
44 changed files with 1782 additions and 237 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,3 +27,4 @@ plugin_packages
|
||||||
/src/test-results
|
/src/test-results
|
||||||
playwright-report
|
playwright-report
|
||||||
state.json
|
state.json
|
||||||
|
/src/static/oidc
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
FROM node:alpine as adminBuild
|
FROM node:alpine as adminBuild
|
||||||
|
|
||||||
WORKDIR /opt/etherpad-lite
|
WORKDIR /opt/etherpad-lite
|
||||||
COPY ./admin ./admin
|
COPY ./ ./
|
||||||
COPY ./src/locales ./src/locales
|
|
||||||
RUN cd ./admin && npm install -g pnpm && pnpm install && pnpm run build --outDir ./dist
|
RUN cd ./admin && npm install -g pnpm && pnpm install && pnpm run build --outDir ./dist
|
||||||
|
RUN cd ./ui && pnpm install && pnpm run build --outDir ./dist
|
||||||
|
|
||||||
|
|
||||||
FROM node:alpine as build
|
FROM node:alpine as build
|
||||||
|
@ -116,6 +116,7 @@ FROM build as development
|
||||||
|
|
||||||
COPY --chown=etherpad:etherpad ./src/package.json .npmrc ./src/
|
COPY --chown=etherpad:etherpad ./src/package.json .npmrc ./src/
|
||||||
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
||||||
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/ui/dist ./src/static/oidc
|
||||||
|
|
||||||
RUN bin/installDeps.sh && \
|
RUN bin/installDeps.sh && \
|
||||||
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
||||||
|
@ -130,6 +131,7 @@ ENV ETHERPAD_PRODUCTION=true
|
||||||
|
|
||||||
COPY --chown=etherpad:etherpad ./src ./src
|
COPY --chown=etherpad:etherpad ./src ./src
|
||||||
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
||||||
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/ui/dist ./src/static/oidc
|
||||||
|
|
||||||
RUN bin/installDeps.sh && rm -rf ~/.npm && rm -rf ~/.local && rm -rf ~/.cache && \
|
RUN bin/installDeps.sh && rm -rf ~/.npm && rm -rf ~/.local && rm -rf ~/.cache && \
|
||||||
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
||||||
|
|
|
@ -15,7 +15,8 @@ export default defineConfig({
|
||||||
})],
|
})],
|
||||||
base: '/admin',
|
base: '/admin',
|
||||||
build:{
|
build:{
|
||||||
outDir: '../src/templates/admin'
|
outDir: '../src/templates/admin',
|
||||||
|
emptyOutDir: true,
|
||||||
},
|
},
|
||||||
server:{
|
server:{
|
||||||
proxy: {
|
proxy: {
|
||||||
|
|
|
@ -19,6 +19,15 @@ IF EXIST admin (
|
||||||
cd /D ..
|
cd /D ..
|
||||||
)
|
)
|
||||||
|
|
||||||
|
:: Install ui only if available
|
||||||
|
IF EXIST ui (
|
||||||
|
cd /D .\ui
|
||||||
|
dir
|
||||||
|
cmd /C pnpm i || exit /B 1
|
||||||
|
cmd /C pnpm run build || exit /B 1
|
||||||
|
cd /D ..
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
cmd /C pnpm i || exit /B 1
|
cmd /C pnpm i || exit /B 1
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ Please type 'Etherpad rocks my socks' (or restart with the '--root'
|
||||||
argument) if you still want to start it as root:
|
argument) if you still want to start it as root:
|
||||||
EOF
|
EOF
|
||||||
printf "> " >&2
|
printf "> " >&2
|
||||||
read rocks
|
read -r rocks
|
||||||
[ "$rocks" = "Etherpad rocks my socks" ] || fatal "Your input was incorrect"
|
[ "$rocks" = "Etherpad rocks my socks" ] || fatal "Your input was incorrect"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -32,9 +32,11 @@ bin/installDeps.sh "$@" || exit 1
|
||||||
|
|
||||||
## Create the admin ui
|
## Create the admin ui
|
||||||
if [ -z "$NODE_ENV" ] || [ "$NODE_ENV" = "development" ]; then
|
if [ -z "$NODE_ENV" ] || [ "$NODE_ENV" = "development" ]; then
|
||||||
ADMIN_UI_PATH="$(dirname $0)/../admin"
|
ADMIN_UI_PATH="$(dirname "$0")/../admin"
|
||||||
|
UI_PATH="$(dirname "$0")/../ui"
|
||||||
log "Creating the admin UI..."
|
log "Creating the admin UI..."
|
||||||
(cd $ADMIN_UI_PATH && pnpm run build)
|
(cd "$ADMIN_UI_PATH" && pnpm run build)
|
||||||
|
(cd "$UI_PATH" && pnpm run build)
|
||||||
else
|
else
|
||||||
log "Cannot create the admin UI in production mode"
|
log "Cannot create the admin UI in production mode"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"admin": "workspace:./admin",
|
"admin": "workspace:./admin",
|
||||||
"docs": "workspace:./doc"
|
"docs": "workspace:./doc",
|
||||||
|
"ui": "workspace:./ui"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18.2",
|
"node": ">=18.18.2",
|
||||||
|
|
507
pnpm-lock.yaml
507
pnpm-lock.yaml
|
@ -18,6 +18,9 @@ importers:
|
||||||
docs:
|
docs:
|
||||||
specifier: workspace:./doc
|
specifier: workspace:./doc
|
||||||
version: link:doc
|
version: link:doc
|
||||||
|
ui:
|
||||||
|
specifier: workspace:./ui
|
||||||
|
version: link:ui
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
@ -178,6 +181,9 @@ importers:
|
||||||
http-errors:
|
http-errors:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
|
jose:
|
||||||
|
specifier: ^5.2.3
|
||||||
|
version: 5.2.3
|
||||||
js-cookie:
|
js-cookie:
|
||||||
specifier: ^3.0.5
|
specifier: ^3.0.5
|
||||||
version: 3.0.5
|
version: 3.0.5
|
||||||
|
@ -187,6 +193,9 @@ importers:
|
||||||
jsonminify:
|
jsonminify:
|
||||||
specifier: 0.4.2
|
specifier: 0.4.2
|
||||||
version: 0.4.2
|
version: 0.4.2
|
||||||
|
jsonwebtoken:
|
||||||
|
specifier: ^9.0.2
|
||||||
|
version: 9.0.2
|
||||||
languages4translatewiki:
|
languages4translatewiki:
|
||||||
specifier: 0.1.3
|
specifier: 0.1.3
|
||||||
version: 0.1.3
|
version: 0.1.3
|
||||||
|
@ -199,12 +208,18 @@ importers:
|
||||||
log4js:
|
log4js:
|
||||||
specifier: ^6.9.1
|
specifier: ^6.9.1
|
||||||
version: 6.9.1
|
version: 6.9.1
|
||||||
|
lru-cache:
|
||||||
|
specifier: ^10.2.0
|
||||||
|
version: 10.2.0
|
||||||
measured-core:
|
measured-core:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
mime-types:
|
mime-types:
|
||||||
specifier: ^2.1.35
|
specifier: ^2.1.35
|
||||||
version: 2.1.35
|
version: 2.1.35
|
||||||
|
oidc-provider:
|
||||||
|
specifier: ^8.4.5
|
||||||
|
version: 8.4.5
|
||||||
openapi-backend:
|
openapi-backend:
|
||||||
specifier: ^5.10.6
|
specifier: ^5.10.6
|
||||||
version: 5.10.6
|
version: 5.10.6
|
||||||
|
@ -272,18 +287,27 @@ importers:
|
||||||
'@types/express':
|
'@types/express':
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
|
'@types/formidable':
|
||||||
|
specifier: ^3.4.5
|
||||||
|
version: 3.4.5
|
||||||
'@types/http-errors':
|
'@types/http-errors':
|
||||||
specifier: ^2.0.4
|
specifier: ^2.0.4
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
'@types/jsdom':
|
'@types/jsdom':
|
||||||
specifier: ^21.1.6
|
specifier: ^21.1.6
|
||||||
version: 21.1.6
|
version: 21.1.6
|
||||||
|
'@types/jsonwebtoken':
|
||||||
|
specifier: ^9.0.6
|
||||||
|
version: 9.0.6
|
||||||
'@types/mocha':
|
'@types/mocha':
|
||||||
specifier: ^10.0.6
|
specifier: ^10.0.6
|
||||||
version: 10.0.6
|
version: 10.0.6
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20.11.30
|
specifier: ^20.11.30
|
||||||
version: 20.11.30
|
version: 20.11.30
|
||||||
|
'@types/oidc-provider':
|
||||||
|
specifier: ^8.4.4
|
||||||
|
version: 8.4.4
|
||||||
'@types/semver':
|
'@types/semver':
|
||||||
specifier: ^7.5.8
|
specifier: ^7.5.8
|
||||||
version: 7.5.8
|
version: 7.5.8
|
||||||
|
@ -333,6 +357,15 @@ importers:
|
||||||
specifier: ^5.4.3
|
specifier: ^5.4.3
|
||||||
version: 5.4.3
|
version: 5.4.3
|
||||||
|
|
||||||
|
ui:
|
||||||
|
devDependencies:
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.2.2
|
||||||
|
version: 5.4.3
|
||||||
|
vite:
|
||||||
|
specifier: ^5.2.0
|
||||||
|
version: 5.2.6
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
/@aashutoshrathi/word-wrap@1.2.6:
|
/@aashutoshrathi/word-wrap@1.2.6:
|
||||||
|
@ -1274,6 +1307,26 @@ packages:
|
||||||
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
|
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@koa/cors@5.0.0:
|
||||||
|
resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==}
|
||||||
|
engines: {node: '>= 14.0.0'}
|
||||||
|
dependencies:
|
||||||
|
vary: 1.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@koa/router@12.0.1:
|
||||||
|
resolution: {integrity: sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
dependencies:
|
||||||
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
|
http-errors: 2.0.0
|
||||||
|
koa-compose: 4.1.0
|
||||||
|
methods: 1.1.2
|
||||||
|
path-to-regexp: 6.2.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@nodelib/fs.scandir@2.1.5:
|
/@nodelib/fs.scandir@2.1.5:
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
@ -1799,6 +1852,11 @@ packages:
|
||||||
shiki: 1.2.0
|
shiki: 1.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@sindresorhus/is@5.6.0:
|
||||||
|
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@sinonjs/commons@2.0.0:
|
/@sinonjs/commons@2.0.0:
|
||||||
resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==}
|
resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2081,6 +2139,19 @@ packages:
|
||||||
resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
|
resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@szmarczak/http-timer@5.0.1:
|
||||||
|
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
dependencies:
|
||||||
|
defer-to-connect: 2.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@types/accepts@1.3.7:
|
||||||
|
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.11.30
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/async@3.2.24:
|
/@types/async@3.2.24:
|
||||||
resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==}
|
resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2098,6 +2169,10 @@ packages:
|
||||||
'@types/node': 20.11.30
|
'@types/node': 20.11.30
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/content-disposition@0.5.8:
|
||||||
|
resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/cookie@0.4.1:
|
/@types/cookie@0.4.1:
|
||||||
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
|
resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -2106,6 +2181,15 @@ packages:
|
||||||
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
|
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/cookies@0.9.0:
|
||||||
|
resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==}
|
||||||
|
dependencies:
|
||||||
|
'@types/connect': 3.4.38
|
||||||
|
'@types/express': 4.17.21
|
||||||
|
'@types/keygrip': 1.0.6
|
||||||
|
'@types/node': 20.11.30
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/cors@2.8.17:
|
/@types/cors@2.8.17:
|
||||||
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
|
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2140,6 +2224,12 @@ packages:
|
||||||
'@types/serve-static': 1.15.5
|
'@types/serve-static': 1.15.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/formidable@3.4.5:
|
||||||
|
resolution: {integrity: sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.11.30
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/fs-extra@9.0.13:
|
/@types/fs-extra@9.0.13:
|
||||||
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
|
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2152,6 +2242,14 @@ packages:
|
||||||
'@types/unist': 3.0.2
|
'@types/unist': 3.0.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/http-assert@1.5.5:
|
||||||
|
resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/http-cache-semantics@4.0.4:
|
||||||
|
resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/http-errors@2.0.4:
|
/@types/http-errors@2.0.4:
|
||||||
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
|
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2171,6 +2269,35 @@ packages:
|
||||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/jsonwebtoken@9.0.6:
|
||||||
|
resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.11.30
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/keygrip@1.0.6:
|
||||||
|
resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/koa-compose@3.2.8:
|
||||||
|
resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/koa': 2.15.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/koa@2.15.0:
|
||||||
|
resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==}
|
||||||
|
dependencies:
|
||||||
|
'@types/accepts': 1.3.7
|
||||||
|
'@types/content-disposition': 0.5.8
|
||||||
|
'@types/cookies': 0.9.0
|
||||||
|
'@types/http-assert': 1.5.5
|
||||||
|
'@types/http-errors': 2.0.4
|
||||||
|
'@types/keygrip': 1.0.6
|
||||||
|
'@types/koa-compose': 3.2.8
|
||||||
|
'@types/node': 20.11.30
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/linkify-it@3.0.5:
|
/@types/linkify-it@3.0.5:
|
||||||
resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==}
|
resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2238,6 +2365,13 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 5.26.5
|
undici-types: 5.26.5
|
||||||
|
|
||||||
|
/@types/oidc-provider@8.4.4:
|
||||||
|
resolution: {integrity: sha512-+SlmKc4qlCJLjpw6Du/8cXw18JsPEYyQwoy+xheLkiuNsCz1mPEYI/lRXLQHvfJD9TH6+2/WDTLZQ2UUJ5G4bw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/koa': 2.15.0
|
||||||
|
'@types/node': 20.11.30
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/prop-types@15.7.11:
|
/@types/prop-types@15.7.11:
|
||||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2977,6 +3111,10 @@ packages:
|
||||||
update-browserslist-db: 1.0.13(browserslist@4.23.0)
|
update-browserslist-db: 1.0.13(browserslist@4.23.0)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/buffer-equal-constant-time@1.0.1:
|
||||||
|
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/buffer-from@1.1.2:
|
/buffer-from@1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -2997,6 +3135,32 @@ packages:
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/cache-content-type@1.0.1:
|
||||||
|
resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==}
|
||||||
|
engines: {node: '>= 6.0.0'}
|
||||||
|
dependencies:
|
||||||
|
mime-types: 2.1.35
|
||||||
|
ylru: 1.3.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/cacheable-lookup@7.0.0:
|
||||||
|
resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/cacheable-request@10.2.14:
|
||||||
|
resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
dependencies:
|
||||||
|
'@types/http-cache-semantics': 4.0.4
|
||||||
|
get-stream: 6.0.1
|
||||||
|
http-cache-semantics: 4.1.1
|
||||||
|
keyv: 4.5.4
|
||||||
|
mimic-response: 4.0.0
|
||||||
|
normalize-url: 8.0.1
|
||||||
|
responselike: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/call-bind@1.0.7:
|
/call-bind@1.0.7:
|
||||||
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
@ -3083,6 +3247,11 @@ packages:
|
||||||
wrap-ansi: 7.0.0
|
wrap-ansi: 7.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/co@4.6.0:
|
||||||
|
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||||
|
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/color-convert@1.9.3:
|
/color-convert@1.9.3:
|
||||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3173,6 +3342,14 @@ packages:
|
||||||
/cookiejar@2.1.4:
|
/cookiejar@2.1.4:
|
||||||
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
|
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
|
||||||
|
|
||||||
|
/cookies@0.9.1:
|
||||||
|
resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
dependencies:
|
||||||
|
depd: 2.0.0
|
||||||
|
keygrip: 1.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/cors@2.8.5:
|
/cors@2.8.5:
|
||||||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
@ -3272,10 +3449,26 @@ packages:
|
||||||
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
|
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/decompress-response@6.0.0:
|
||||||
|
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
mimic-response: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/deep-equal@1.0.1:
|
||||||
|
resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/deep-is@0.1.4:
|
/deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/defer-to-connect@2.0.1:
|
||||||
|
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/define-data-property@1.1.4:
|
/define-data-property@1.1.4:
|
||||||
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
|
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
@ -3297,6 +3490,15 @@ packages:
|
||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
|
/delegates@1.0.0:
|
||||||
|
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/depd@1.1.2:
|
||||||
|
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/depd@2.0.0:
|
/depd@2.0.0:
|
||||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
@ -3370,6 +3572,12 @@ packages:
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ecdsa-sig-formatter@1.0.11:
|
||||||
|
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ee-first@1.1.1:
|
/ee-first@1.1.1:
|
||||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -3970,6 +4178,11 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/eta@3.4.0:
|
||||||
|
resolution: {integrity: sha512-tCsc7WXTjrTx4ZjYLplcqrI3o4mYJ+Z6YspeuGL8tbt/hHoMchwBwtKfwM09svEY86iRapY93vUqQttcNuIO5Q==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/etag@1.8.1:
|
/etag@1.8.1:
|
||||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
@ -4170,6 +4383,11 @@ packages:
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/form-data-encoder@2.1.4:
|
||||||
|
resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
|
||||||
|
engines: {node: '>= 14.17'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/form-data@4.0.0:
|
/form-data@4.0.0:
|
||||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
@ -4299,6 +4517,11 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/get-stream@6.0.1:
|
||||||
|
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/get-symbol-description@1.0.2:
|
/get-symbol-description@1.0.2:
|
||||||
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
|
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
@ -4385,6 +4608,23 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.4
|
get-intrinsic: 1.2.4
|
||||||
|
|
||||||
|
/got@13.0.0:
|
||||||
|
resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
dependencies:
|
||||||
|
'@sindresorhus/is': 5.6.0
|
||||||
|
'@szmarczak/http-timer': 5.0.1
|
||||||
|
cacheable-lookup: 7.0.0
|
||||||
|
cacheable-request: 10.2.14
|
||||||
|
decompress-response: 6.0.0
|
||||||
|
form-data-encoder: 2.1.4
|
||||||
|
get-stream: 6.0.1
|
||||||
|
http2-wrapper: 2.2.1
|
||||||
|
lowercase-keys: 3.0.0
|
||||||
|
p-cancelable: 3.0.0
|
||||||
|
responselike: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/graceful-fs@4.2.11:
|
/graceful-fs@4.2.11:
|
||||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||||
|
|
||||||
|
@ -4423,7 +4663,6 @@ packages:
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
dev: true
|
|
||||||
|
|
||||||
/hasown@2.0.1:
|
/hasown@2.0.1:
|
||||||
resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
|
resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
|
||||||
|
@ -4567,6 +4806,29 @@ packages:
|
||||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/http-assert@1.5.0:
|
||||||
|
resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
dependencies:
|
||||||
|
deep-equal: 1.0.1
|
||||||
|
http-errors: 1.8.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/http-cache-semantics@4.1.1:
|
||||||
|
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/http-errors@1.8.1:
|
||||||
|
resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
dependencies:
|
||||||
|
depd: 1.1.2
|
||||||
|
inherits: 2.0.4
|
||||||
|
setprototypeof: 1.2.0
|
||||||
|
statuses: 1.5.0
|
||||||
|
toidentifier: 1.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/http-errors@2.0.0:
|
/http-errors@2.0.0:
|
||||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
@ -4588,6 +4850,14 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/http2-wrapper@2.2.1:
|
||||||
|
resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
|
||||||
|
engines: {node: '>=10.19.0'}
|
||||||
|
dependencies:
|
||||||
|
quick-lru: 5.1.1
|
||||||
|
resolve-alpn: 1.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/https-proxy-agent@7.0.4:
|
/https-proxy-agent@7.0.4:
|
||||||
resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
|
resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
@ -4739,6 +5009,13 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/is-generator-function@1.0.10:
|
||||||
|
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
has-tostringtag: 1.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-glob@4.0.3:
|
/is-glob@4.0.3:
|
||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
@ -4855,6 +5132,10 @@ packages:
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/jose@5.2.3:
|
||||||
|
resolution: {integrity: sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/js-cookie@3.0.5:
|
/js-cookie@3.0.5:
|
||||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
@ -4912,9 +5193,14 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jsesc@3.0.2:
|
||||||
|
resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/json-buffer@3.0.1:
|
/json-buffer@3.0.1:
|
||||||
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/json-parse-even-better-errors@2.3.1:
|
/json-parse-even-better-errors@2.3.1:
|
||||||
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
||||||
|
@ -4971,19 +5257,99 @@ packages:
|
||||||
resolution: {integrity: sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==}
|
resolution: {integrity: sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jsonwebtoken@9.0.2:
|
||||||
|
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
||||||
|
engines: {node: '>=12', npm: '>=6'}
|
||||||
|
dependencies:
|
||||||
|
jws: 3.2.2
|
||||||
|
lodash.includes: 4.3.0
|
||||||
|
lodash.isboolean: 3.0.3
|
||||||
|
lodash.isinteger: 4.0.4
|
||||||
|
lodash.isnumber: 3.0.3
|
||||||
|
lodash.isplainobject: 4.0.6
|
||||||
|
lodash.isstring: 4.0.1
|
||||||
|
lodash.once: 4.1.1
|
||||||
|
ms: 2.1.3
|
||||||
|
semver: 7.6.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/just-extend@6.2.0:
|
/just-extend@6.2.0:
|
||||||
resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==}
|
resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jwa@1.4.1:
|
||||||
|
resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
|
||||||
|
dependencies:
|
||||||
|
buffer-equal-constant-time: 1.0.1
|
||||||
|
ecdsa-sig-formatter: 1.0.11
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/jws@3.2.2:
|
||||||
|
resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
|
||||||
|
dependencies:
|
||||||
|
jwa: 1.4.1
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/kebab-case@1.0.2:
|
/kebab-case@1.0.2:
|
||||||
resolution: {integrity: sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==}
|
resolution: {integrity: sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/keygrip@1.1.0:
|
||||||
|
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
dependencies:
|
||||||
|
tsscmp: 1.0.6
|
||||||
|
dev: false
|
||||||
|
|
||||||
/keyv@4.5.4:
|
/keyv@4.5.4:
|
||||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
json-buffer: 3.0.1
|
json-buffer: 3.0.1
|
||||||
dev: true
|
|
||||||
|
/koa-compose@4.1.0:
|
||||||
|
resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/koa-convert@2.0.0:
|
||||||
|
resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
dependencies:
|
||||||
|
co: 4.6.0
|
||||||
|
koa-compose: 4.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/koa@2.15.2:
|
||||||
|
resolution: {integrity: sha512-MXTeZH3M6AJ8ukW2QZ8wqO3Dcdfh2WRRmjCBkEP+NhKNCiqlO5RDqHmSnsyNrbRJrdjyvIGSJho4vQiWgQJSVA==}
|
||||||
|
engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4}
|
||||||
|
dependencies:
|
||||||
|
accepts: 1.3.8
|
||||||
|
cache-content-type: 1.0.1
|
||||||
|
content-disposition: 0.5.4
|
||||||
|
content-type: 1.0.5
|
||||||
|
cookies: 0.9.1
|
||||||
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
|
delegates: 1.0.0
|
||||||
|
depd: 2.0.0
|
||||||
|
destroy: 1.2.0
|
||||||
|
encodeurl: 1.0.2
|
||||||
|
escape-html: 1.0.3
|
||||||
|
fresh: 0.5.2
|
||||||
|
http-assert: 1.5.0
|
||||||
|
http-errors: 1.8.1
|
||||||
|
is-generator-function: 1.0.10
|
||||||
|
koa-compose: 4.1.0
|
||||||
|
koa-convert: 2.0.0
|
||||||
|
on-finished: 2.4.1
|
||||||
|
only: 0.0.2
|
||||||
|
parseurl: 1.3.3
|
||||||
|
statuses: 1.5.0
|
||||||
|
type-is: 1.6.18
|
||||||
|
vary: 1.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
/languages4translatewiki@0.1.3:
|
/languages4translatewiki@0.1.3:
|
||||||
resolution: {integrity: sha512-Z7+IM3FF+VyRbWl2CPWQoRf498zGy/qnolP5wJhMny4W3v0SL9rUCIPDHPao9rrw2yg2KIKnHIzopuWvGdnooQ==}
|
resolution: {integrity: sha512-Z7+IM3FF+VyRbWl2CPWQoRf498zGy/qnolP5wJhMny4W3v0SL9rUCIPDHPao9rrw2yg2KIKnHIzopuWvGdnooQ==}
|
||||||
|
@ -5044,9 +5410,37 @@ packages:
|
||||||
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lodash.includes@4.3.0:
|
||||||
|
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/lodash.isboolean@3.0.3:
|
||||||
|
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/lodash.isinteger@4.0.4:
|
||||||
|
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/lodash.isnumber@3.0.3:
|
||||||
|
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/lodash.isplainobject@4.0.6:
|
||||||
|
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/lodash.isstring@4.0.1:
|
||||||
|
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lodash.merge@4.6.2:
|
/lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
|
/lodash.once@4.1.1:
|
||||||
|
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lodash@4.17.21:
|
/lodash@4.17.21:
|
||||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -5085,6 +5479,16 @@ packages:
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lowercase-keys@3.0.0:
|
||||||
|
resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/lru-cache@10.2.0:
|
||||||
|
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
|
||||||
|
engines: {node: 14 || >=16.14}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lru-cache@5.1.1:
|
/lru-cache@5.1.1:
|
||||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5212,6 +5616,16 @@ packages:
|
||||||
engines: {node: '>=4.0.0'}
|
engines: {node: '>=4.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
/mimic-response@3.1.0:
|
||||||
|
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/mimic-response@4.0.0:
|
||||||
|
resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/minimatch@3.1.2:
|
/minimatch@3.1.2:
|
||||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5333,6 +5747,12 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/nanoid@5.0.6:
|
||||||
|
resolution: {integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==}
|
||||||
|
engines: {node: ^18 || >=20}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/natural-compare@1.4.0:
|
/natural-compare@1.4.0:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -5387,6 +5807,11 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/normalize-url@8.0.1:
|
||||||
|
resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/nwsapi@2.2.7:
|
/nwsapi@2.2.7:
|
||||||
resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
|
resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -5396,6 +5821,11 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/object-hash@3.0.0:
|
||||||
|
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/object-inspect@1.13.1:
|
/object-inspect@1.13.1:
|
||||||
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
|
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
|
||||||
|
|
||||||
|
@ -5446,6 +5876,31 @@ packages:
|
||||||
resolution: {integrity: sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==}
|
resolution: {integrity: sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/oidc-provider@8.4.5:
|
||||||
|
resolution: {integrity: sha512-2NsPrvIAX1W4ZR41cGbz2Lt2Ci8iXvECh+x+LcKcM115s/h8iB1pwnNlCdIrvAA2iBGM4/TkO75Xg7xb2FCzWA==}
|
||||||
|
dependencies:
|
||||||
|
'@koa/cors': 5.0.0
|
||||||
|
'@koa/router': 12.0.1
|
||||||
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
|
eta: 3.4.0
|
||||||
|
got: 13.0.0
|
||||||
|
jose: 5.2.3
|
||||||
|
jsesc: 3.0.2
|
||||||
|
koa: 2.15.2
|
||||||
|
nanoid: 5.0.6
|
||||||
|
object-hash: 3.0.0
|
||||||
|
oidc-token-hash: 5.0.3
|
||||||
|
quick-lru: 7.0.0
|
||||||
|
raw-body: 2.5.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/oidc-token-hash@5.0.3:
|
||||||
|
resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==}
|
||||||
|
engines: {node: ^10.13.0 || >=12.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/on-finished@2.4.1:
|
/on-finished@2.4.1:
|
||||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
@ -5463,6 +5918,10 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
wrappy: 1.0.2
|
wrappy: 1.0.2
|
||||||
|
|
||||||
|
/only@0.0.2:
|
||||||
|
resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/openapi-backend@5.10.6:
|
/openapi-backend@5.10.6:
|
||||||
resolution: {integrity: sha512-vTjBRys/O4JIHdlRHUKZ7pxS+gwIJreAAU9dvYRFrImtPzQ5qxm5a6B8BTVT9m6I8RGGsShJv35MAc3Tu2/y/A==}
|
resolution: {integrity: sha512-vTjBRys/O4JIHdlRHUKZ7pxS+gwIJreAAU9dvYRFrImtPzQ5qxm5a6B8BTVT9m6I8RGGsShJv35MAc3Tu2/y/A==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
@ -5516,6 +5975,11 @@ packages:
|
||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/p-cancelable@3.0.0:
|
||||||
|
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
|
||||||
|
engines: {node: '>=12.20'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-limit@3.1.0:
|
/p-limit@3.1.0:
|
||||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -5580,7 +6044,6 @@ packages:
|
||||||
|
|
||||||
/path-to-regexp@6.2.1:
|
/path-to-regexp@6.2.1:
|
||||||
resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
|
resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/path-type@4.0.0:
|
/path-type@4.0.0:
|
||||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||||
|
@ -5690,6 +6153,16 @@ packages:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/quick-lru@5.1.1:
|
||||||
|
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/quick-lru@7.0.0:
|
||||||
|
resolution: {integrity: sha512-MX8gB7cVYTrYcFfAnfLlhRd0+Toyl8yX8uBx1MrX7K0jegiz9TumwOK27ldXrgDlHRdVi+MqU9Ssw6dr4BNreg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/rambda@7.5.0:
|
/rambda@7.5.0:
|
||||||
resolution: {integrity: sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==}
|
resolution: {integrity: sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -5915,6 +6388,10 @@ packages:
|
||||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/resolve-alpn@1.2.1:
|
||||||
|
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/resolve-from@4.0.0:
|
/resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -5931,6 +6408,13 @@ packages:
|
||||||
path-parse: 1.0.7
|
path-parse: 1.0.7
|
||||||
supports-preserve-symlinks-flag: 1.0.0
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
|
||||||
|
/responselike@3.0.0:
|
||||||
|
resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
dependencies:
|
||||||
|
lowercase-keys: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/reusify@1.0.4:
|
/reusify@1.0.4:
|
||||||
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||||
|
@ -6234,6 +6718,11 @@ packages:
|
||||||
resolution: {integrity: sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==}
|
resolution: {integrity: sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/statuses@1.5.0:
|
||||||
|
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/statuses@2.0.1:
|
/statuses@2.0.1:
|
||||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
@ -6499,6 +6988,11 @@ packages:
|
||||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tsscmp@1.0.6:
|
||||||
|
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
||||||
|
engines: {node: '>=0.6.x'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tsx@4.7.1:
|
/tsx@4.7.1:
|
||||||
resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==}
|
resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
@ -7102,6 +7596,11 @@ packages:
|
||||||
yargs-parser: 20.2.4
|
yargs-parser: 20.2.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ylru@1.3.2:
|
||||||
|
resolution: {integrity: sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==}
|
||||||
|
engines: {node: '>= 4.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/yocto-queue@0.1.0:
|
/yocto-queue@0.1.0:
|
||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
|
@ -3,3 +3,4 @@ packages:
|
||||||
- admin
|
- admin
|
||||||
- bin
|
- bin
|
||||||
- doc
|
- doc
|
||||||
|
- ui
|
||||||
|
|
|
@ -650,5 +650,24 @@
|
||||||
/*
|
/*
|
||||||
* Enable/Disable case-insensitive pad names.
|
* Enable/Disable case-insensitive pad names.
|
||||||
*/
|
*/
|
||||||
"lowerCasePadIds": "${LOWER_CASE_PAD_IDS:false}"
|
"lowerCasePadIds": "${LOWER_CASE_PAD_IDS:false}",
|
||||||
|
"sso": {
|
||||||
|
"issuer": "${SSO_ISSUER:http://localhost:9001}",
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"client_id": "${ADMIN_CLIENT:admin_client}",
|
||||||
|
"client_secret": "${ADMIN_SECRET:admin}",
|
||||||
|
"grant_types": ["authorization_code"],
|
||||||
|
"response_types": ["code"],
|
||||||
|
"redirect_uris": ["${ADMIN_REDIRECT:http://localhost:9001/admin/}"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "${USER_CLIENT:user_client}",
|
||||||
|
"client_secret": "${USER_SECRET:user}",
|
||||||
|
"grant_types": ["authorization_code"],
|
||||||
|
"response_types": ["code"],
|
||||||
|
"redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -650,5 +650,25 @@
|
||||||
/*
|
/*
|
||||||
* Enable/Disable case-insensitive pad names.
|
* Enable/Disable case-insensitive pad names.
|
||||||
*/
|
*/
|
||||||
"lowerCasePadIds": false
|
"lowerCasePadIds": false,
|
||||||
|
|
||||||
|
"sso": {
|
||||||
|
"issuer": "${SSO_ISSUER:http://localhost:9001}",
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"client_id": "${ADMIN_CLIENT:admin_client}",
|
||||||
|
"client_secret": "${ADMIN_SECRET:admin}",
|
||||||
|
"grant_types": ["authorization_code"],
|
||||||
|
"response_types": ["code"],
|
||||||
|
"redirect_uris": ["${ADMIN_REDIRECT:http://localhost:9001/admin/}"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "${USER_CLIENT:user_client}",
|
||||||
|
"client_secret": "${USER_SECRET:user}",
|
||||||
|
"grant_types": ["authorization_code"],
|
||||||
|
"response_types": ["code"],
|
||||||
|
"redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,12 @@
|
||||||
"expressPreSession": "ep_etherpad-lite/node/hooks/express/specialpages"
|
"expressPreSession": "ep_etherpad-lite/node/hooks/express/specialpages"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "oauth2",
|
||||||
|
"hooks": {
|
||||||
|
"expressCreateServer": "ep_etherpad-lite/node/security/OAuth2Provider"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "padurlsanitize",
|
"name": "padurlsanitize",
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
|
|
@ -21,30 +21,12 @@
|
||||||
|
|
||||||
import {MapArrayType} from "../types/MapType";
|
import {MapArrayType} from "../types/MapType";
|
||||||
|
|
||||||
const absolutePaths = require('../utils/AbsolutePaths');
|
|
||||||
import fs from 'fs';
|
|
||||||
const api = require('../db/API');
|
const api = require('../db/API');
|
||||||
import log4js from 'log4js';
|
|
||||||
const padManager = require('../db/PadManager');
|
const padManager = require('../db/PadManager');
|
||||||
const randomString = require('../utils/randomstring');
|
|
||||||
const argv = require('../utils/Cli').argv;
|
|
||||||
import createHTTPError from 'http-errors';
|
import createHTTPError from 'http-errors';
|
||||||
|
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
||||||
const apiHandlerLogger = log4js.getLogger('APIHandler');
|
import {publicKeyExported} from "../security/OAuth2Provider";
|
||||||
|
import {jwtVerify} from "jose";
|
||||||
// ensure we have an apikey
|
|
||||||
let apikey:string|null = null;
|
|
||||||
const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt');
|
|
||||||
|
|
||||||
try {
|
|
||||||
apikey = fs.readFileSync(apikeyFilename, 'utf8');
|
|
||||||
apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`);
|
|
||||||
} catch (e) {
|
|
||||||
apiHandlerLogger.info(
|
|
||||||
`Api key file "${apikeyFilename}" not found. Creating with random contents.`);
|
|
||||||
apikey = randomString(32);
|
|
||||||
fs.writeFileSync(apikeyFilename, apikey!, 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
// a list of all functions
|
// a list of all functions
|
||||||
const version:MapArrayType<any> = {};
|
const version:MapArrayType<any> = {};
|
||||||
|
@ -167,21 +149,20 @@ exports.version = version;
|
||||||
|
|
||||||
|
|
||||||
type APIFields = {
|
type APIFields = {
|
||||||
apikey: string;
|
|
||||||
api_key: string;
|
api_key: string;
|
||||||
padID: string;
|
padID: string;
|
||||||
padName: string;
|
padName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a HTTP API call
|
* Handles an HTTP API call
|
||||||
* @param {String} apiVersion the version of the api
|
* @param {String} apiVersion the version of the api
|
||||||
* @param {String} functionName the name of the called function
|
* @param {String} functionName the name of the called function
|
||||||
* @param fields the params of the called function
|
* @param fields the params of the called function
|
||||||
* @req express request object
|
* @param req express request object
|
||||||
* @res express response object
|
* @param res express response object
|
||||||
*/
|
*/
|
||||||
exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields) {
|
exports.handle = async function (apiVersion: string, functionName: string, fields: APIFields, req: Http2ServerRequest, res: Http2ServerResponse) {
|
||||||
// say goodbye if this is an unknown API version
|
// say goodbye if this is an unknown API version
|
||||||
if (!(apiVersion in version)) {
|
if (!(apiVersion in version)) {
|
||||||
throw new createHTTPError.NotFound('no such api version');
|
throw new createHTTPError.NotFound('no such api version');
|
||||||
|
@ -192,13 +173,20 @@ exports.handle = async function (apiVersion: string, functionName: string, field
|
||||||
throw new createHTTPError.NotFound('no such function');
|
throw new createHTTPError.NotFound('no such function');
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the api key!
|
if(!req.headers.authorization) {
|
||||||
fields.apikey = fields.apikey || fields.api_key;
|
|
||||||
|
|
||||||
if (fields.apikey !== apikey!.trim()) {
|
|
||||||
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'],
|
||||||
|
requiredClaims: ["admin"]})
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// sanitize any padIDs before continuing
|
// sanitize any padIDs before continuing
|
||||||
if (fields.padID) {
|
if (fields.padID) {
|
||||||
fields.padID = await padManager.sanitizePadId(fields.padID);
|
fields.padID = await padManager.sanitizePadId(fields.padID);
|
||||||
|
@ -217,7 +205,3 @@ exports.handle = async function (apiVersion: string, functionName: string, field
|
||||||
// call the api function
|
// call the api function
|
||||||
return api[functionName].apply(this, functionParams);
|
return api[functionName].apply(this, functionParams);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.exportedForTestingOnly = {
|
|
||||||
apiKey: apikey,
|
|
||||||
};
|
|
||||||
|
|
|
@ -483,14 +483,24 @@ const generateDefinitionForVersion = (version:string, style = APIPathStyle.FLAT)
|
||||||
...defaultResponses,
|
...defaultResponses,
|
||||||
},
|
},
|
||||||
securitySchemes: {
|
securitySchemes: {
|
||||||
ApiKey: {
|
openid: {
|
||||||
type: 'apiKey',
|
type: "oauth2",
|
||||||
in: 'query',
|
flows: {
|
||||||
name: 'apikey',
|
authorizationCode: {
|
||||||
|
authorizationUrl: settings.sso.issuer+"/oidc/auth",
|
||||||
|
tokenUrl: settings.sso.issuer+"/oidc/token",
|
||||||
|
scopes: {
|
||||||
|
openid: "openid",
|
||||||
|
profile: "profile",
|
||||||
|
email: "email",
|
||||||
|
admin: "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
security: [{ApiKey: []}],
|
security: [{openid: []}],
|
||||||
};
|
};
|
||||||
|
|
||||||
// build operations
|
// build operations
|
||||||
|
@ -657,7 +667,7 @@ exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// start and bind to express
|
// start and bind to express
|
||||||
api.init();
|
await api.init();
|
||||||
app.use(apiRoot, async (req:any, res:any) => {
|
app.use(apiRoot, async (req:any, res:any) => {
|
||||||
let response = null;
|
let response = null;
|
||||||
try {
|
try {
|
||||||
|
|
274
src/node/security/OAuth2Provider.ts
Normal file
274
src/node/security/OAuth2Provider.ts
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
import {ArgsExpressType} from "../types/ArgsExpressType";
|
||||||
|
import Provider, {Account, Configuration} from 'oidc-provider';
|
||||||
|
import {generateKeyPair, exportJWK, KeyLike} from 'jose'
|
||||||
|
import MemoryAdapter from "./OIDCAdapter";
|
||||||
|
import path from "path";
|
||||||
|
const settings = require('../utils/Settings');
|
||||||
|
import {IncomingForm} from 'formidable'
|
||||||
|
import express, {Request, Response} from 'express';
|
||||||
|
import {format} from 'url'
|
||||||
|
import {ParsedUrlQuery} from "node:querystring";
|
||||||
|
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
||||||
|
|
||||||
|
const configuration: Configuration = {
|
||||||
|
scopes: ['openid', 'profile', 'email'],
|
||||||
|
findAccount: async (ctx, id) => {
|
||||||
|
const users = settings.users as {
|
||||||
|
[username: string]: {
|
||||||
|
password: string;
|
||||||
|
is_admin: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const usersArray1 = Object.keys(users).map((username) => ({
|
||||||
|
username,
|
||||||
|
...users[username]
|
||||||
|
}));
|
||||||
|
|
||||||
|
const account = usersArray1.find((user) => user.username === id);
|
||||||
|
|
||||||
|
if(account === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
if (account.is_admin) {
|
||||||
|
return {
|
||||||
|
accountId: id,
|
||||||
|
claims: () => ({
|
||||||
|
sub: id,
|
||||||
|
admin: true
|
||||||
|
})
|
||||||
|
} as Account
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
accountId: id,
|
||||||
|
claims: () => ({
|
||||||
|
sub: id,
|
||||||
|
})
|
||||||
|
} as Account
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ttl:{
|
||||||
|
AccessToken: 1 * 60 * 60, // 1 hour in seconds
|
||||||
|
AuthorizationCode: 10 * 60, // 10 minutes in seconds
|
||||||
|
ClientCredentials: 1 * 60 * 60, // 1 hour in seconds
|
||||||
|
IdToken: 1 * 60 * 60, // 1 hour in seconds
|
||||||
|
RefreshToken: 1 * 24 * 60 * 60, // 1 day in seconds
|
||||||
|
},
|
||||||
|
claims: {
|
||||||
|
openid: ['sub'],
|
||||||
|
email: ['email'],
|
||||||
|
profile: ['name'],
|
||||||
|
admin: ['admin']
|
||||||
|
},
|
||||||
|
cookies: {
|
||||||
|
keys: ['oidc'],
|
||||||
|
},
|
||||||
|
features:{
|
||||||
|
devInteractions: {enabled: false},
|
||||||
|
},
|
||||||
|
adapter: MemoryAdapter
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export let publicKeyExported: KeyLike|null
|
||||||
|
export let privateKeyExported: KeyLike|null
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function is used to initialize the OAuth2 provider
|
||||||
|
*/
|
||||||
|
export const expressCreateServer = async (hookName: string, args: ArgsExpressType, cb: Function) => {
|
||||||
|
const {privateKey, publicKey} = await generateKeyPair('RS256');
|
||||||
|
const privateKeyJWK = await exportJWK(privateKey);
|
||||||
|
publicKeyExported = publicKey
|
||||||
|
privateKeyExported = privateKey
|
||||||
|
|
||||||
|
const oidc = new Provider(settings.sso.issuer, {
|
||||||
|
...configuration, jwks: {
|
||||||
|
keys: [
|
||||||
|
privateKeyJWK
|
||||||
|
],
|
||||||
|
},
|
||||||
|
conformIdTokenClaims: false,
|
||||||
|
claims: {
|
||||||
|
address: ['address'],
|
||||||
|
email: ['email', 'email_verified'],
|
||||||
|
phone: ['phone_number', 'phone_number_verified'],
|
||||||
|
profile: ['birthdate', 'family_name', 'gender', 'given_name', 'locale', 'middle_name', 'name',
|
||||||
|
'nickname', 'picture', 'preferred_username', 'profile', 'updated_at', 'website', 'zoneinfo'],
|
||||||
|
},
|
||||||
|
features:{
|
||||||
|
userinfo: {enabled: true},
|
||||||
|
claimsParameter: {enabled: true},
|
||||||
|
devInteractions: {enabled: false},
|
||||||
|
resourceIndicators: {enabled: true, defaultResource(ctx) {
|
||||||
|
return ctx.origin;
|
||||||
|
},
|
||||||
|
getResourceServerInfo(ctx, resourceIndicator, client) {
|
||||||
|
return {
|
||||||
|
scope: client.scope as string,
|
||||||
|
audience: 'account',
|
||||||
|
accessTokenFormat: 'jwt',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
useGrantedResource(ctx, model) {
|
||||||
|
return true;
|
||||||
|
},},
|
||||||
|
jwtResponseModes: {enabled: true},
|
||||||
|
},
|
||||||
|
clientBasedCORS: (ctx, origin, client) => {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
extraTokenClaims: async (ctx, token) => {
|
||||||
|
|
||||||
|
|
||||||
|
if(token.kind === 'AccessToken') {
|
||||||
|
// Add your custom claims here. For example:
|
||||||
|
const users = settings.users as {
|
||||||
|
[username: string]: {
|
||||||
|
password: string;
|
||||||
|
is_admin: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const usersArray1 = Object.keys(users).map((username) => ({
|
||||||
|
username,
|
||||||
|
...users[username]
|
||||||
|
}));
|
||||||
|
|
||||||
|
const account = usersArray1.find((user) => user.username === token.accountId);
|
||||||
|
return {
|
||||||
|
admin: account?.is_admin
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clients: settings.sso.clients
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
args.app.post('/interaction/:uid', async (req: Http2ServerRequest, res: Http2ServerResponse, next:Function) => {
|
||||||
|
const formid = new IncomingForm();
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
const {login, password} = (await formid.parse(req))[0]
|
||||||
|
const {prompt, jti, session,cid, params, grantId} = await oidc.interactionDetails(req, res);
|
||||||
|
|
||||||
|
const client = await oidc.Client.find(params.client_id as string);
|
||||||
|
|
||||||
|
switch (prompt.name) {
|
||||||
|
case 'login': {
|
||||||
|
const users = settings.users as {
|
||||||
|
[username: string]: {
|
||||||
|
password: string;
|
||||||
|
admin: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const usersArray1 = Object.keys(users).map((username) => ({
|
||||||
|
username,
|
||||||
|
...users[username]
|
||||||
|
}));
|
||||||
|
const account = usersArray1.find((user) => user.username === login as unknown as string && user.password === password as unknown as string);
|
||||||
|
if (!account) {
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
res.end(JSON.stringify({error: "Invalid login"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account) {
|
||||||
|
await oidc.interactionFinished(req, res, {
|
||||||
|
login: {accountId: account.username}
|
||||||
|
}, {mergeWithLastSubmission: false});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'consent': {
|
||||||
|
let grant;
|
||||||
|
if (grantId) {
|
||||||
|
// we'll be modifying existing grant in existing session
|
||||||
|
grant = await oidc.Grant.find(grantId);
|
||||||
|
} else {
|
||||||
|
// we're establishing a new grant
|
||||||
|
grant = new oidc.Grant({
|
||||||
|
accountId: session!.accountId,
|
||||||
|
clientId: params.client_id as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prompt.details.missingOIDCScope) {
|
||||||
|
// @ts-ignore
|
||||||
|
grant!.addOIDCScope(prompt.details.missingOIDCScope.join(' '));
|
||||||
|
}
|
||||||
|
if (prompt.details.missingOIDCClaims) {
|
||||||
|
grant!.addOIDCClaims(prompt.details.missingOIDCClaims as string[]);
|
||||||
|
}
|
||||||
|
if (prompt.details.missingResourceScopes) {
|
||||||
|
for (const [indicator, scope] of Object.entries(prompt.details.missingResourceScopes)) {
|
||||||
|
grant!.addResourceScope(indicator, scope.join(' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = {consent: {grantId: await grant!.save()}};
|
||||||
|
await oidc.interactionFinished(req, res, result, {
|
||||||
|
mergeWithLastSubmission: true,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await next();
|
||||||
|
} catch (err:any) {
|
||||||
|
return res.writeHead(500).end(err.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
args.app.get('/interaction/:uid', async (req: Request, res: Response, next: Function) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
uid, prompt, params, session,
|
||||||
|
} = await oidc.interactionDetails(req, res);
|
||||||
|
|
||||||
|
params["state"] = uid
|
||||||
|
|
||||||
|
switch (prompt.name) {
|
||||||
|
case 'login': {
|
||||||
|
res.redirect(format({
|
||||||
|
pathname: '/views/login.html',
|
||||||
|
query: params as ParsedUrlQuery
|
||||||
|
}))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'consent': {
|
||||||
|
res.redirect(format({
|
||||||
|
pathname: '/views/consent.html',
|
||||||
|
query: params as ParsedUrlQuery
|
||||||
|
}))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return res.sendFile(path.join(settings.root,'src','static', 'oidc','login.html'));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
args.app.use('/views/', express.static(path.join(settings.root,'src','static', 'oidc'), {maxAge: 1000 * 60 * 60 * 24}));
|
||||||
|
|
||||||
|
/*
|
||||||
|
oidc.on('authorization.error', (ctx, error) => {
|
||||||
|
console.log('authorization.error', error);
|
||||||
|
})
|
||||||
|
|
||||||
|
oidc.on('server_error', (ctx, error) => {
|
||||||
|
console.log('server_error', error);
|
||||||
|
})
|
||||||
|
oidc.on('grant.error', (ctx, error) => {
|
||||||
|
console.log('grant.error', error);
|
||||||
|
})
|
||||||
|
oidc.on('introspection.error', (ctx, error) => {
|
||||||
|
console.log('introspection.error', error);
|
||||||
|
})
|
||||||
|
oidc.on('revocation.error', (ctx, error) => {
|
||||||
|
console.log('revocation.error', error);
|
||||||
|
})*/
|
||||||
|
args.app.use("/oidc", oidc.callback());
|
||||||
|
//cb();
|
||||||
|
}
|
5
src/node/security/OAuth2User.ts
Normal file
5
src/node/security/OAuth2User.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export type OAuth2User = {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
admin: boolean;
|
||||||
|
}
|
115
src/node/security/OIDCAdapter.ts
Normal file
115
src/node/security/OIDCAdapter.ts
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import {LRUCache} from 'lru-cache';
|
||||||
|
import type {Adapter, AdapterPayload} from "oidc-provider";
|
||||||
|
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
max: 500,
|
||||||
|
sizeCalculation: (item:any, key:any) => {
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
// for use with tracking overall storage size
|
||||||
|
maxSize: 5000,
|
||||||
|
|
||||||
|
// how long to live in ms
|
||||||
|
ttl: 1000 * 60 * 5,
|
||||||
|
|
||||||
|
// return stale items before removing from cache?
|
||||||
|
allowStale: false,
|
||||||
|
|
||||||
|
updateAgeOnGet: false,
|
||||||
|
updateAgeOnHas: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const epochTime = (date = Date.now()) => Math.floor(date / 1000);
|
||||||
|
|
||||||
|
const storage = new LRUCache<string,AdapterPayload|string[]|string>(options);
|
||||||
|
|
||||||
|
function grantKeyFor(id: string) {
|
||||||
|
return `grant:${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function userCodeKeyFor(userCode:string) {
|
||||||
|
return `userCode:${userCode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MemoryAdapter implements Adapter{
|
||||||
|
private readonly name: string;
|
||||||
|
constructor(name:string) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
key(id:string) {
|
||||||
|
return `${this.name}:${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(id:string) {
|
||||||
|
const key = this.key(id);
|
||||||
|
|
||||||
|
const found = storage.get(key) as AdapterPayload;
|
||||||
|
const grantId = found && found.grantId;
|
||||||
|
|
||||||
|
storage.delete(key);
|
||||||
|
|
||||||
|
if (grantId) {
|
||||||
|
const grantKey = grantKeyFor(grantId);
|
||||||
|
(storage.get(grantKey) as string[])!.forEach(token => storage.delete(token));
|
||||||
|
storage.delete(grantKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(id: string) {
|
||||||
|
(storage.get(this.key(id)) as AdapterPayload)!.consumed = epochTime();
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
find(id: string): Promise<AdapterPayload | void | undefined> {
|
||||||
|
if (storage.has(this.key(id))){
|
||||||
|
return Promise.resolve<AdapterPayload>(storage.get(this.key(id)) as AdapterPayload);
|
||||||
|
}
|
||||||
|
return Promise.resolve<undefined>(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
findByUserCode(userCode: string) {
|
||||||
|
const id = storage.get(userCodeKeyFor(userCode)) as string;
|
||||||
|
return this.find(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
upsert(id: string, payload: {
|
||||||
|
iat: number;
|
||||||
|
exp: number;
|
||||||
|
uid: string;
|
||||||
|
kind: string;
|
||||||
|
jti: string;
|
||||||
|
accountId: string;
|
||||||
|
loginTs: number;
|
||||||
|
}, expiresIn: number) {
|
||||||
|
const key = this.key(id);
|
||||||
|
|
||||||
|
storage.set(key, payload, {ttl: expiresIn * 1000});
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
findByUid(uid: string): Promise<AdapterPayload | void | undefined> {
|
||||||
|
for(const [_, value] of storage.entries()){
|
||||||
|
if(typeof value ==="object" && "uid" in value && value.uid === uid){
|
||||||
|
return Promise.resolve(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
revokeByGrantId(grantId: string): Promise<void | undefined> {
|
||||||
|
const grantKey = grantKeyFor(grantId);
|
||||||
|
const grant = storage.get(grantKey) as string[];
|
||||||
|
if (grant) {
|
||||||
|
grant.forEach((token) => storage.delete(token));
|
||||||
|
storage.delete(grantKey);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MemoryAdapter
|
|
@ -45,10 +45,5 @@ for (let i = 0; i < argv.length; i++) {
|
||||||
exports.argv.sessionkey = arg;
|
exports.argv.sessionkey = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override location of APIKEY.txt file
|
|
||||||
if (prevArg === '--apikey') {
|
|
||||||
exports.argv.apikey = arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
prevArg = arg;
|
prevArg = arg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,6 +342,11 @@ exports.requireAuthentication = false;
|
||||||
exports.requireAuthorization = false;
|
exports.requireAuthorization = false;
|
||||||
exports.users = {};
|
exports.users = {};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This setting is used for configuring sso
|
||||||
|
*/
|
||||||
|
exports.sso = {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Show settings in admin page, by default it is true
|
* Show settings in admin page, by default it is true
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -45,15 +45,18 @@
|
||||||
"find-root": "1.1.0",
|
"find-root": "1.1.0",
|
||||||
"formidable": "^3.5.1",
|
"formidable": "^3.5.1",
|
||||||
"http-errors": "^2.0.0",
|
"http-errors": "^2.0.0",
|
||||||
|
"jose": "^5.2.3",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsdom": "^24.0.0",
|
"jsdom": "^24.0.0",
|
||||||
"jsonminify": "0.4.2",
|
"jsonminify": "0.4.2",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
"languages4translatewiki": "0.1.3",
|
"languages4translatewiki": "0.1.3",
|
||||||
"live-plugin-manager-pnpm": "^0.18.1",
|
"live-plugin-manager-pnpm": "^0.18.1",
|
||||||
"lodash.clonedeep": "4.5.0",
|
"lodash.clonedeep": "4.5.0",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"measured-core": "^2.0.0",
|
"measured-core": "^2.0.0",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
|
"oidc-provider": "^8.4.5",
|
||||||
"openapi-backend": "^5.10.6",
|
"openapi-backend": "^5.10.6",
|
||||||
"proxy-addr": "^2.0.7",
|
"proxy-addr": "^2.0.7",
|
||||||
"rate-limiter-flexible": "^5.0.0",
|
"rate-limiter-flexible": "^5.0.0",
|
||||||
|
@ -72,22 +75,27 @@
|
||||||
"ueberdb2": "^4.2.63",
|
"ueberdb2": "^4.2.63",
|
||||||
"underscore": "1.13.6",
|
"underscore": "1.13.6",
|
||||||
"unorm": "1.6.0",
|
"unorm": "1.6.0",
|
||||||
"wtfnode": "^0.9.1"
|
"wtfnode": "^0.9.1",
|
||||||
|
"lru-cache": "^10.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"etherpad-healthcheck": "../bin/etherpad-healthcheck",
|
"etherpad-healthcheck": "../bin/etherpad-healthcheck",
|
||||||
"etherpad-lite": "node/server.ts"
|
"etherpad-lite": "node/server.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.42.1",
|
||||||
"@types/async": "^3.2.24",
|
"@types/async": "^3.2.24",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/formidable": "^3.4.5",
|
||||||
"@types/http-errors": "^2.0.4",
|
"@types/http-errors": "^2.0.4",
|
||||||
"@types/jsdom": "^21.1.6",
|
"@types/jsdom": "^21.1.6",
|
||||||
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node": "^20.11.30",
|
"@types/node": "^20.11.30",
|
||||||
|
"@types/oidc-provider": "^8.4.4",
|
||||||
|
"@types/semver": "^7.5.8",
|
||||||
"@types/sinon": "^17.0.3",
|
"@types/sinon": "^17.0.3",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@types/semver": "^7.5.8",
|
|
||||||
"@types/underscore": "^1.11.15",
|
"@types/underscore": "^1.11.15",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-etherpad": "^4.0.4",
|
"eslint-config-etherpad": "^4.0.4",
|
||||||
|
@ -96,7 +104,6 @@
|
||||||
"mocha-froth": "^0.2.10",
|
"mocha-froth": "^0.2.10",
|
||||||
"nodeify": "^1.0.1",
|
"nodeify": "^1.0.1",
|
||||||
"openapi-schema-validation": "^0.4.2",
|
"openapi-schema-validation": "^0.4.2",
|
||||||
"@playwright/test": "^1.42.1",
|
|
||||||
"set-cookie-parser": "^2.6.0",
|
"set-cookie-parser": "^2.6.0",
|
||||||
"sinon": "^17.0.1",
|
"sinon": "^17.0.1",
|
||||||
"split-grid": "^1.0.11",
|
"split-grid": "^1.0.11",
|
||||||
|
|
|
@ -13,18 +13,20 @@ const server = require('../../node/server');
|
||||||
const setCookieParser = require('set-cookie-parser');
|
const setCookieParser = require('set-cookie-parser');
|
||||||
const settings = require('../../node/utils/Settings');
|
const settings = require('../../node/utils/Settings');
|
||||||
import supertest from 'supertest';
|
import supertest from 'supertest';
|
||||||
|
import TestAgent from "supertest/lib/agent";
|
||||||
|
import {Http2Server} from "node:http2";
|
||||||
|
import {SignJWT} from "jose";
|
||||||
|
import {privateKeyExported} from "../../node/security/OAuth2Provider";
|
||||||
const webaccess = require('../../node/hooks/express/webaccess');
|
const webaccess = require('../../node/hooks/express/webaccess');
|
||||||
|
|
||||||
const backups:MapArrayType<any> = {};
|
const backups:MapArrayType<any> = {};
|
||||||
let agentPromise:Promise<any>|null = null;
|
let agentPromise:Promise<any>|null = null;
|
||||||
|
|
||||||
exports.apiKey = apiHandler.exportedForTestingOnly.apiKey;
|
export let agent: TestAgent|null = null;
|
||||||
exports.agent = null;
|
export let baseUrl:string|null = null;
|
||||||
exports.baseUrl = null;
|
export let httpServer: Http2Server|null = null;
|
||||||
exports.httpServer = null;
|
export const logger = log4js.getLogger('test');
|
||||||
exports.logger = log4js.getLogger('test');
|
|
||||||
|
|
||||||
const logger = exports.logger;
|
|
||||||
const logLevel = logger.level;
|
const logLevel = logger.level;
|
||||||
|
|
||||||
// Mocha doesn't monitor unhandled Promise rejections, so convert them to uncaught exceptions.
|
// Mocha doesn't monitor unhandled Promise rejections, so convert them to uncaught exceptions.
|
||||||
|
@ -33,10 +35,37 @@ process.on('unhandledRejection', (reason: string) => { throw reason; });
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
this.timeout(60000);
|
this.timeout(60000);
|
||||||
await exports.init();
|
await init();
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.init = async function () {
|
|
||||||
|
export const generateJWTToken = () => {
|
||||||
|
const jwt = new SignJWT({
|
||||||
|
sub: 'admin',
|
||||||
|
jti: '123',
|
||||||
|
exp: Math.floor(Date.now() / 1000) + 60 * 60,
|
||||||
|
aud: 'account',
|
||||||
|
iss: 'http://localhost:9001',
|
||||||
|
admin: true
|
||||||
|
})
|
||||||
|
jwt.setProtectedHeader({alg: 'RS256'})
|
||||||
|
return jwt.sign(privateKeyExported!)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const generateJWTTokenUser = () => {
|
||||||
|
const jwt = new SignJWT({
|
||||||
|
sub: 'admin',
|
||||||
|
jti: '123',
|
||||||
|
exp: Math.floor(Date.now() / 1000) + 60 * 60,
|
||||||
|
aud: 'account',
|
||||||
|
iss: 'http://localhost:9001',
|
||||||
|
})
|
||||||
|
jwt.setProtectedHeader({alg: 'RS256'})
|
||||||
|
return jwt.sign(privateKeyExported!)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const init = async function () {
|
||||||
if (agentPromise != null) return await agentPromise;
|
if (agentPromise != null) return await agentPromise;
|
||||||
let agentResolve;
|
let agentResolve;
|
||||||
agentPromise = new Promise((resolve) => { agentResolve = resolve; });
|
agentPromise = new Promise((resolve) => { agentResolve = resolve; });
|
||||||
|
@ -53,11 +82,13 @@ exports.init = async function () {
|
||||||
settings.ip = 'localhost';
|
settings.ip = 'localhost';
|
||||||
settings.importExportRateLimiting = {max: 999999};
|
settings.importExportRateLimiting = {max: 999999};
|
||||||
settings.commitRateLimiting = {duration: 0.001, points: 1e6};
|
settings.commitRateLimiting = {duration: 0.001, points: 1e6};
|
||||||
exports.httpServer = await server.start();
|
httpServer = await server.start();
|
||||||
exports.baseUrl = `http://localhost:${exports.httpServer.address().port}`;
|
// @ts-ignore
|
||||||
logger.debug(`HTTP server at ${exports.baseUrl}`);
|
baseUrl = `http://localhost:${httpServer!.address()!.port}`;
|
||||||
|
logger.debug(`HTTP server at ${baseUrl}`);
|
||||||
// Create a supertest user agent for the HTTP server.
|
// Create a supertest user agent for the HTTP server.
|
||||||
exports.agent = supertest(exports.baseUrl);
|
agent = supertest(baseUrl)
|
||||||
|
//.set('Authorization', `Bearer ${await generateJWTToken()}`);
|
||||||
// Speed up authn tests.
|
// Speed up authn tests.
|
||||||
backups.authnFailureDelayMs = webaccess.authnFailureDelayMs;
|
backups.authnFailureDelayMs = webaccess.authnFailureDelayMs;
|
||||||
webaccess.authnFailureDelayMs = 0;
|
webaccess.authnFailureDelayMs = 0;
|
||||||
|
@ -69,8 +100,8 @@ exports.init = async function () {
|
||||||
await server.exit();
|
await server.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
agentResolve!(exports.agent);
|
agentResolve!(agent);
|
||||||
return exports.agent;
|
return agent;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,7 +112,7 @@ exports.init = async function () {
|
||||||
* @param {string} event - The socket.io Socket event to listen for.
|
* @param {string} event - The socket.io Socket event to listen for.
|
||||||
* @returns The argument(s) passed to the event handler.
|
* @returns The argument(s) passed to the event handler.
|
||||||
*/
|
*/
|
||||||
exports.waitForSocketEvent = async (socket: any, event:string) => {
|
export const waitForSocketEvent = async (socket: any, event:string) => {
|
||||||
const errorEvents = [
|
const errorEvents = [
|
||||||
'error',
|
'error',
|
||||||
'connect_error',
|
'connect_error',
|
||||||
|
@ -136,7 +167,7 @@ exports.waitForSocketEvent = async (socket: any, event:string) => {
|
||||||
* nullish, no cookies are passed to the server.
|
* nullish, no cookies are passed to the server.
|
||||||
* @returns {io.Socket} A socket.io client Socket object.
|
* @returns {io.Socket} A socket.io client Socket object.
|
||||||
*/
|
*/
|
||||||
exports.connect = async (res:any = null) => {
|
export const connect = async (res:any = null) => {
|
||||||
// Convert the `set-cookie` header(s) into a `cookie` header.
|
// Convert the `set-cookie` header(s) into a `cookie` header.
|
||||||
const resCookies = (res == null) ? {} : setCookieParser.parse(res, {map: true});
|
const resCookies = (res == null) ? {} : setCookieParser.parse(res, {map: true});
|
||||||
const reqCookieHdr = Object.entries(resCookies).map(
|
const reqCookieHdr = Object.entries(resCookies).map(
|
||||||
|
@ -148,14 +179,14 @@ exports.connect = async (res:any = null) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
padId = res.req.path.split('/p/')[1];
|
padId = res.req.path.split('/p/')[1];
|
||||||
}
|
}
|
||||||
const socket = io(`${exports.baseUrl}/`, {
|
const socket = io(`${baseUrl}/`, {
|
||||||
forceNew: true, // Different tests will have different query parameters.
|
forceNew: true, // Different tests will have different query parameters.
|
||||||
// socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the
|
// socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the
|
||||||
// express_sid cookie must be passed as a query parameter.
|
// express_sid cookie must be passed as a query parameter.
|
||||||
query: {cookie: reqCookieHdr, padId},
|
query: {cookie: reqCookieHdr, padId},
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await exports.waitForSocketEvent(socket, 'connect');
|
await waitForSocketEvent(socket, 'connect');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
socket.close();
|
socket.close();
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -173,7 +204,7 @@ exports.connect = async (res:any = null) => {
|
||||||
* @param token
|
* @param token
|
||||||
* @returns The CLIENT_VARS message from the server.
|
* @returns The CLIENT_VARS message from the server.
|
||||||
*/
|
*/
|
||||||
exports.handshake = async (socket: any, padId:string, token = padutils.generateAuthorToken()) => {
|
export const handshake = async (socket: any, padId:string, token = padutils.generateAuthorToken()) => {
|
||||||
logger.debug('sending CLIENT_READY...');
|
logger.debug('sending CLIENT_READY...');
|
||||||
socket.emit('message', {
|
socket.emit('message', {
|
||||||
component: 'pad',
|
component: 'pad',
|
||||||
|
@ -183,7 +214,7 @@ exports.handshake = async (socket: any, padId:string, token = padutils.generateA
|
||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
logger.debug('waiting for CLIENT_VARS response...');
|
logger.debug('waiting for CLIENT_VARS response...');
|
||||||
const msg = await exports.waitForSocketEvent(socket, 'message');
|
const msg = await waitForSocketEvent(socket, 'message');
|
||||||
logger.debug('received CLIENT_VARS message');
|
logger.debug('received CLIENT_VARS message');
|
||||||
return msg;
|
return msg;
|
||||||
};
|
};
|
||||||
|
@ -191,7 +222,7 @@ exports.handshake = async (socket: any, padId:string, token = padutils.generateA
|
||||||
/**
|
/**
|
||||||
* Convenience wrapper around `socket.send()` that waits for acknowledgement.
|
* Convenience wrapper around `socket.send()` that waits for acknowledgement.
|
||||||
*/
|
*/
|
||||||
exports.sendMessage = async (socket: any, message:any) => await new Promise<void>((resolve, reject) => {
|
export const sendMessage = async (socket: any, message:any) => await new Promise<void>((resolve, reject) => {
|
||||||
socket.emit('message', message, (errInfo:{
|
socket.emit('message', message, (errInfo:{
|
||||||
name: string,
|
name: string,
|
||||||
message: string,
|
message: string,
|
||||||
|
@ -210,7 +241,7 @@ exports.sendMessage = async (socket: any, message:any) => await new Promise<void
|
||||||
/**
|
/**
|
||||||
* Convenience function to send a USER_CHANGES message. Waits for acknowledgement.
|
* Convenience function to send a USER_CHANGES message. Waits for acknowledgement.
|
||||||
*/
|
*/
|
||||||
exports.sendUserChanges = async (socket:any, data:any) => await exports.sendMessage(socket, {
|
export const sendUserChanges = async (socket:any, data:any) => await sendMessage(socket, {
|
||||||
type: 'COLLABROOM',
|
type: 'COLLABROOM',
|
||||||
component: 'pad',
|
component: 'pad',
|
||||||
data: {
|
data: {
|
||||||
|
@ -232,8 +263,8 @@ exports.sendUserChanges = async (socket:any, data:any) => await exports.sendMess
|
||||||
* common.sendUserChanges(socket, {baseRev: rev, changeset}),
|
* common.sendUserChanges(socket, {baseRev: rev, changeset}),
|
||||||
* ]);
|
* ]);
|
||||||
*/
|
*/
|
||||||
exports.waitForAcceptCommit = async (socket:any, wantRev: number) => {
|
export const waitForAcceptCommit = async (socket:any, wantRev: number) => {
|
||||||
const msg = await exports.waitForSocketEvent(socket, 'message');
|
const msg = await waitForSocketEvent(socket, 'message');
|
||||||
assert.deepEqual(msg, {
|
assert.deepEqual(msg, {
|
||||||
type: 'COLLABROOM',
|
type: 'COLLABROOM',
|
||||||
data: {
|
data: {
|
||||||
|
@ -252,7 +283,7 @@ const alphabet = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
* @param {string} [charset] - Characters to pick from.
|
* @param {string} [charset] - Characters to pick from.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
exports.randomString = (len: number = 10, charset: string = `${alphabet}${alphabet.toUpperCase()}0123456789`): string => {
|
export const randomString = (len: number = 10, charset: string = `${alphabet}${alphabet.toUpperCase()}0123456789`): string => {
|
||||||
let ret = '';
|
let ret = '';
|
||||||
while (ret.length < len) ret += charset[Math.floor(Math.random() * charset.length)];
|
while (ret.length < len) ret += charset[Math.floor(Math.random() * charset.length)];
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -7,13 +7,12 @@ const common = require('./common');
|
||||||
const host = `http://${settings.ip}:${settings.port}`;
|
const host = `http://${settings.ip}:${settings.port}`;
|
||||||
const froth = require('mocha-froth');
|
const froth = require('mocha-froth');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const apiKey = common.apiKey;
|
|
||||||
const apiVersion = 1;
|
const apiVersion = 1;
|
||||||
const testPadId = `TEST_fuzz${makeid()}`;
|
const testPadId = `TEST_fuzz${makeid()}`;
|
||||||
|
|
||||||
const endPoint = function (point: string, version?:number) {
|
const endPoint = function (point: string, version?:number) {
|
||||||
version = version || apiVersion;
|
version = version || apiVersion;
|
||||||
return `/api/${version}/${point}?apikey=${apiKey}`;
|
return `/api/${version}/${point}}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Testing against padID', testPadId);
|
console.log('Testing against padID', testPadId);
|
||||||
|
@ -29,7 +28,12 @@ setTimeout(() => {
|
||||||
}, 5000); // wait 5 seconds
|
}, 5000); // wait 5 seconds
|
||||||
|
|
||||||
async function runTest(number: number) {
|
async function runTest(number: number) {
|
||||||
await axios.get(`${host + endPoint('createPad')}&padID=${testPadId}`)
|
await axios
|
||||||
|
.get(`${host + endPoint('createPad')}?padID=${testPadId}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: await common.generateJWTToken(),
|
||||||
|
}
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const req = axios.post(`${host}/p/${testPadId}/import`)
|
const req = axios.post(`${host}/p/${testPadId}/import`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -12,7 +12,6 @@ const common = require('../../common');
|
||||||
const validateOpenAPI = require('openapi-schema-validation').validate;
|
const validateOpenAPI = require('openapi-schema-validation').validate;
|
||||||
|
|
||||||
let agent: any;
|
let agent: any;
|
||||||
const apiKey = common.apiKey;
|
|
||||||
let apiVersion = 1;
|
let apiVersion = 1;
|
||||||
|
|
||||||
const makeid = () => {
|
const makeid = () => {
|
||||||
|
@ -27,7 +26,7 @@ const makeid = () => {
|
||||||
|
|
||||||
const testPadId = makeid();
|
const testPadId = makeid();
|
||||||
|
|
||||||
const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`;
|
const endPoint = (point:string) => `/api/${apiVersion}/${point}`;
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
before(async function () { agent = await common.init(); });
|
before(async function () { agent = await common.init(); });
|
||||||
|
|
|
@ -6,17 +6,18 @@
|
||||||
* TODO: maybe unify those two files and merge in a single one.
|
* TODO: maybe unify those two files and merge in a single one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {generateJWTToken, generateJWTTokenUser} from "../../common";
|
||||||
|
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const common = require('../../common');
|
const common = require('../../common');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const fsp = fs.promises;
|
const fsp = fs.promises;
|
||||||
|
|
||||||
let agent:any;
|
let agent:any;
|
||||||
const apiKey = common.apiKey;
|
|
||||||
let apiVersion = 1;
|
let apiVersion = 1;
|
||||||
const testPadId = makeid();
|
const testPadId = makeid();
|
||||||
|
|
||||||
const endPoint = (point:string, version?:number) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
|
const endPoint = (point:string, version?:number) => `/api/${version || apiVersion}/${point}`;
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
before(async function () { agent = await common.init(); });
|
before(async function () { agent = await common.init(); });
|
||||||
|
@ -24,28 +25,38 @@ describe(__filename, function () {
|
||||||
describe('Sanity checks', function () {
|
describe('Sanity checks', function () {
|
||||||
it('can connect', async function () {
|
it('can connect', async function () {
|
||||||
await agent.get('/api/')
|
await agent.get('/api/')
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('finds the version tag', async function () {
|
it('finds the version tag', async function () {
|
||||||
const res = await agent.get('/api/')
|
const res = await agent.get('/api/')
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200);
|
.expect(200);
|
||||||
apiVersion = res.body.currentVersion;
|
apiVersion = res.body.currentVersion;
|
||||||
assert(apiVersion);
|
assert(apiVersion);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors with invalid APIKey', async function () {
|
it('errors with invalid OAuth token', async function () {
|
||||||
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343
|
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343
|
||||||
// If your APIKey is password you deserve to fail all tests anyway
|
await agent.get(`/api/${apiVersion}/createPad?padID=test`)
|
||||||
await agent.get(`/api/${apiVersion}/createPad?apikey=password&padID=test`)
|
.set("Authorization", (await generateJWTToken()).substring(0,10))
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors with unprivileged OAuth token', async function () {
|
||||||
|
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343
|
||||||
|
await agent.get(`/api/${apiVersion}/createPad?padID=test`)
|
||||||
|
.set("Authorization", (await generateJWTTokenUser()).substring(0,10))
|
||||||
.expect(401);
|
.expect(401);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Tests', function () {
|
describe('Tests', function () {
|
||||||
it('creates a new Pad', async function () {
|
it('creates a new Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -53,6 +64,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Sets the HTML of a Pad attempting to weird utf8 encoded content', async function () {
|
it('Sets the HTML of a Pad attempting to weird utf8 encoded content', async function () {
|
||||||
const res = await agent.post(endPoint('setHTML'))
|
const res = await agent.post(endPoint('setHTML'))
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.send({
|
.send({
|
||||||
padID: testPadId,
|
padID: testPadId,
|
||||||
html: await fsp.readFile('tests/backend/specs/api/emojis.html', 'utf8'),
|
html: await fsp.readFile('tests/backend/specs/api/emojis.html', 'utf8'),
|
||||||
|
@ -63,7 +75,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get the HTML of Pad with emojis', async function () {
|
it('get the HTML of Pad with emojis', async function () {
|
||||||
const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.match(res.body.data.html, /🇼/);
|
assert.match(res.body.data.html, /🇼/);
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import {generateJWTToken} from "../../common";
|
||||||
|
|
||||||
const common = require('../../common');
|
const common = require('../../common');
|
||||||
|
|
||||||
import {strict as assert} from "assert";
|
import {strict as assert} from "assert";
|
||||||
|
|
||||||
let agent:any;
|
let agent:any;
|
||||||
const apiKey = common.apiKey;
|
|
||||||
let apiVersion = 1;
|
let apiVersion = 1;
|
||||||
let authorID = '';
|
let authorID = '';
|
||||||
const padID = makeid();
|
const padID = makeid();
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
|
|
||||||
const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`;
|
const endPoint = (point:string) => `/api/${apiVersion}/${point}`;
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
before(async function () { agent = await common.init(); });
|
before(async function () { agent = await common.init(); });
|
||||||
|
@ -42,16 +43,18 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('Chat functionality', function () {
|
describe('Chat functionality', function () {
|
||||||
it('creates a new Pad', async function () {
|
it('creates a new Pad', async function () {
|
||||||
await agent.get(`${endPoint('createPad')}&padID=${padID}`)
|
await agent.get(`${endPoint('createPad')}?padID=${padID}`)
|
||||||
|
.set("authorization", await generateJWTToken())
|
||||||
|
.expect(200)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
|
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
|
||||||
})
|
})
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/);
|
||||||
.expect(200);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Creates an author with a name set', async function () {
|
it('Creates an author with a name set', async function () {
|
||||||
await agent.get(endPoint('createAuthor'))
|
await agent.get(endPoint('createAuthor'))
|
||||||
|
.set("authorization", await generateJWTToken())
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
if (res.body.code !== 0 || !res.body.data.authorID) {
|
if (res.body.code !== 0 || !res.body.data.authorID) {
|
||||||
throw new Error('Unable to create author');
|
throw new Error('Unable to create author');
|
||||||
|
@ -63,7 +66,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets the head of chat before the first chat msg', async function () {
|
it('Gets the head of chat before the first chat msg', async function () {
|
||||||
await agent.get(`${endPoint('getChatHead')}&padID=${padID}`)
|
await agent.get(`${endPoint('getChatHead')}?padID=${padID}`)
|
||||||
|
.set("authorization", await generateJWTToken())
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
if (res.body.data.chatHead !== -1) throw new Error('Chat Head Length is wrong');
|
if (res.body.data.chatHead !== -1) throw new Error('Chat Head Length is wrong');
|
||||||
if (res.body.code !== 0) throw new Error('Unable to get chat head');
|
if (res.body.code !== 0) throw new Error('Unable to get chat head');
|
||||||
|
@ -73,8 +77,9 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Adds a chat message to the pad', async function () {
|
it('Adds a chat message to the pad', async function () {
|
||||||
await agent.get(`${endPoint('appendChatMessage')}&padID=${padID}&text=blalblalbha` +
|
await agent.get(`${endPoint('appendChatMessage')}?padID=${padID}&text=blalblalbha` +
|
||||||
`&authorID=${authorID}&time=${timestamp}`)
|
`&authorID=${authorID}&time=${timestamp}`)
|
||||||
|
.set("authorization", await generateJWTToken())
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
if (res.body.code !== 0) throw new Error('Unable to create chat message');
|
if (res.body.code !== 0) throw new Error('Unable to create chat message');
|
||||||
})
|
})
|
||||||
|
@ -83,7 +88,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets the head of chat', async function () {
|
it('Gets the head of chat', async function () {
|
||||||
await agent.get(`${endPoint('getChatHead')}&padID=${padID}`)
|
await agent.get(`${endPoint('getChatHead')}?padID=${padID}`)
|
||||||
|
.set("authorization", await generateJWTToken())
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
if (res.body.data.chatHead !== 0) throw new Error('Chat Head Length is wrong');
|
if (res.body.data.chatHead !== 0) throw new Error('Chat Head Length is wrong');
|
||||||
|
|
||||||
|
@ -94,7 +100,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets Chat History of a Pad', async function () {
|
it('Gets Chat History of a Pad', async function () {
|
||||||
await agent.get(`${endPoint('getChatHistory')}&padID=${padID}`)
|
await agent.get(`${endPoint('getChatHistory')}?padID=${padID}`)
|
||||||
|
.set("authorization", await generateJWTToken())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
|
|
@ -9,7 +9,6 @@ const settings = require('../../../container/loadSettings.js').loadSettings();
|
||||||
|
|
||||||
const host = "http://" + settings.ip + ":" + settings.port;
|
const host = "http://" + settings.ip + ":" + settings.port;
|
||||||
|
|
||||||
const apiKey = common.apiKey;
|
|
||||||
var apiVersion = 1;
|
var apiVersion = 1;
|
||||||
var testPadId = "TEST_fuzz" + makeid();
|
var testPadId = "TEST_fuzz" + makeid();
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,9 @@ import {MapArrayType} from "../../../../node/types/MapType";
|
||||||
const common = require('../../common');
|
const common = require('../../common');
|
||||||
|
|
||||||
let agent:any;
|
let agent:any;
|
||||||
const apiKey = common.apiKey;
|
|
||||||
const apiVersion = 1;
|
const apiVersion = 1;
|
||||||
|
|
||||||
const endPoint = (point: string, version?:string) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
|
const endPoint = (point: string, version?:string) => `/api/${version || apiVersion}/${point}`;
|
||||||
|
|
||||||
const testImports:MapArrayType<any> = {
|
const testImports:MapArrayType<any> = {
|
||||||
'malformed': {
|
'malformed': {
|
||||||
|
@ -243,29 +242,33 @@ describe(__filename, function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('createPad', async function () {
|
it('createPad', async function () {
|
||||||
const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setHTML', async function () {
|
it('setHTML', async function () {
|
||||||
const res = await agent.get(`${endPoint('setHTML')}&padID=${testPadId}` +
|
const res = await agent.get(`${endPoint('setHTML')}?padID=${testPadId}` +
|
||||||
`&html=${encodeURIComponent(test.input)}`)
|
`&html=${encodeURIComponent(test.input)}`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getHTML', async function () {
|
it('getHTML', async function () {
|
||||||
const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.html, test.wantHTML);
|
assert.equal(res.body.data.html, test.wantHTML);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getText', async function () {
|
it('getText', async function () {
|
||||||
const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.text, test.wantText);
|
assert.equal(res.body.data.text, test.wantText);
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MapArrayType} from "../../../../node/types/MapType";
|
import {MapArrayType} from "../../../../node/types/MapType";
|
||||||
|
import {SuperTestStatic} from "supertest";
|
||||||
|
import TestAgent from "supertest/lib/agent";
|
||||||
|
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const common = require('../../common');
|
const common = require('../../common');
|
||||||
|
@ -21,8 +23,7 @@ const wordXDoc = fs.readFileSync(`${__dirname}/test.docx`);
|
||||||
const odtDoc = fs.readFileSync(`${__dirname}/test.odt`);
|
const odtDoc = fs.readFileSync(`${__dirname}/test.odt`);
|
||||||
const pdfDoc = fs.readFileSync(`${__dirname}/test.pdf`);
|
const pdfDoc = fs.readFileSync(`${__dirname}/test.pdf`);
|
||||||
|
|
||||||
let agent:any;
|
let agent: TestAgent;
|
||||||
const apiKey = common.apiKey;
|
|
||||||
const apiVersion = 1;
|
const apiVersion = 1;
|
||||||
const testPadId = makeid();
|
const testPadId = makeid();
|
||||||
const testPadIdEnc = encodeURIComponent(testPadId);
|
const testPadIdEnc = encodeURIComponent(testPadId);
|
||||||
|
@ -41,6 +42,7 @@ describe(__filename, function () {
|
||||||
describe('Connectivity', function () {
|
describe('Connectivity', function () {
|
||||||
it('can connect', async function () {
|
it('can connect', async function () {
|
||||||
await agent.get('/api/')
|
await agent.get('/api/')
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
});
|
});
|
||||||
|
@ -49,6 +51,7 @@ describe(__filename, function () {
|
||||||
describe('API Versioning', function () {
|
describe('API Versioning', function () {
|
||||||
it('finds the version tag', async function () {
|
it('finds the version tag', async function () {
|
||||||
await agent.get('/api/')
|
await agent.get('/api/')
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => assert(res.body.currentVersion));
|
.expect((res:any) => assert(res.body.currentVersion));
|
||||||
});
|
});
|
||||||
|
@ -103,14 +106,17 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a new Pad, imports content to it, checks that content', async function () {
|
it('creates a new Pad, imports content to it, checks that content', async function () {
|
||||||
await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
|
await agent.get(`${endPoint('createPad')}?padID=${testPadId}`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => assert.equal(res.body.code, 0));
|
.expect((res:any) => assert.equal(res.body.code, 0));
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => assert.equal(res.body.data.text, padText.toString()));
|
.expect((res:any) => assert.equal(res.body.data.text, padText.toString()));
|
||||||
});
|
});
|
||||||
|
@ -122,9 +128,11 @@ describe(__filename, function () {
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
if (readOnlyId != null) return;
|
if (readOnlyId != null) return;
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getReadOnlyID')}?padID=${testPadId}`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => assert.equal(res.body.code, 0));
|
.expect((res:any) => assert.equal(res.body.code, 0));
|
||||||
|
@ -145,7 +153,8 @@ describe(__filename, function () {
|
||||||
// This ought to be before(), but it must run after the top-level beforeEach() above.
|
// This ought to be before(), but it must run after the top-level beforeEach() above.
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
if (text != null) return;
|
if (text != null) return;
|
||||||
let req = agent.get(`/p/${readOnlyId}/export/${exportType}`);
|
let req = agent.get(`/p/${readOnlyId}/export/${exportType}`)
|
||||||
|
.set("authorization", await common.generateJWTToken());
|
||||||
if (authn) req = req.auth('user', 'user-password');
|
if (authn) req = req.auth('user', 'user-password');
|
||||||
const res = await req
|
const res = await req
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
@ -163,6 +172,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('re-import to read-only pad ID gives 403 forbidden', async function () {
|
it('re-import to read-only pad ID gives 403 forbidden', async function () {
|
||||||
let req = agent.post(`/p/${readOnlyId}/import`)
|
let req = agent.post(`/p/${readOnlyId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', Buffer.from(text), {
|
.attach('file', Buffer.from(text), {
|
||||||
filename: `/test.${exportType}`,
|
filename: `/test.${exportType}`,
|
||||||
contentType: 'text/plain',
|
contentType: 'text/plain',
|
||||||
|
@ -175,6 +185,7 @@ describe(__filename, function () {
|
||||||
// The new pad ID must differ from testPadId because Etherpad refuses to import
|
// The new pad ID must differ from testPadId because Etherpad refuses to import
|
||||||
// .etherpad files on top of a pad that already has edits.
|
// .etherpad files on top of a pad that already has edits.
|
||||||
let req = agent.post(`/p/${testPadId}_import/import`)
|
let req = agent.post(`/p/${testPadId}_import/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', Buffer.from(text), {
|
.attach('file', Buffer.from(text), {
|
||||||
filename: `/test.${exportType}`,
|
filename: `/test.${exportType}`,
|
||||||
contentType: 'text/plain',
|
contentType: 'text/plain',
|
||||||
|
@ -200,6 +211,7 @@ describe(__filename, function () {
|
||||||
// TODO: fix support for .doc files..
|
// TODO: fix support for .doc files..
|
||||||
it('Tries to import .doc that uses soffice or abiword', async function () {
|
it('Tries to import .doc that uses soffice or abiword', async function () {
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'})
|
.attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -212,6 +224,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('exports DOC', async function () {
|
it('exports DOC', async function () {
|
||||||
await agent.get(`/p/${testPadId}/export/doc`)
|
await agent.get(`/p/${testPadId}/export/doc`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => assert(res.body.length >= 9000));
|
.expect((res:any) => assert(res.body.length >= 9000));
|
||||||
|
@ -219,6 +232,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Tries to import .docx that uses soffice or abiword', async function () {
|
it('Tries to import .docx that uses soffice or abiword', async function () {
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', wordXDoc, {
|
.attach('file', wordXDoc, {
|
||||||
filename: '/test.docx',
|
filename: '/test.docx',
|
||||||
contentType:
|
contentType:
|
||||||
|
@ -235,6 +249,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('exports DOC from imported DOCX', async function () {
|
it('exports DOC from imported DOCX', async function () {
|
||||||
await agent.get(`/p/${testPadId}/export/doc`)
|
await agent.get(`/p/${testPadId}/export/doc`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => assert(res.body.length >= 9100));
|
.expect((res:any) => assert(res.body.length >= 9100));
|
||||||
|
@ -242,6 +257,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Tries to import .pdf that uses soffice or abiword', async function () {
|
it('Tries to import .pdf that uses soffice or abiword', async function () {
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'})
|
.attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -254,6 +270,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('exports PDF', async function () {
|
it('exports PDF', async function () {
|
||||||
await agent.get(`/p/${testPadId}/export/pdf`)
|
await agent.get(`/p/${testPadId}/export/pdf`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => assert(res.body.length >= 1000));
|
.expect((res:any) => assert(res.body.length >= 1000));
|
||||||
|
@ -261,6 +278,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Tries to import .odt that uses soffice or abiword', async function () {
|
it('Tries to import .odt that uses soffice or abiword', async function () {
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'})
|
.attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -273,6 +291,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('exports ODT', async function () {
|
it('exports ODT', async function () {
|
||||||
await agent.get(`/p/${testPadId}/export/odt`)
|
await agent.get(`/p/${testPadId}/export/odt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
.buffer(true).parse(superagent.parse['application/octet-stream'])
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => assert(res.body.length >= 7000));
|
.expect((res:any) => assert(res.body.length >= 7000));
|
||||||
|
@ -282,6 +301,7 @@ describe(__filename, function () {
|
||||||
it('Tries to import .etherpad', async function () {
|
it('Tries to import .etherpad', async function () {
|
||||||
this.timeout(3000);
|
this.timeout(3000);
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', etherpadDoc, {
|
.attach('file', etherpadDoc, {
|
||||||
filename: '/test.etherpad',
|
filename: '/test.etherpad',
|
||||||
contentType: 'application/etherpad',
|
contentType: 'application/etherpad',
|
||||||
|
@ -298,6 +318,7 @@ describe(__filename, function () {
|
||||||
it('exports Etherpad', async function () {
|
it('exports Etherpad', async function () {
|
||||||
this.timeout(3000);
|
this.timeout(3000);
|
||||||
await agent.get(`/p/${testPadId}/export/etherpad`)
|
await agent.get(`/p/${testPadId}/export/etherpad`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect(/hello/);
|
.expect(/hello/);
|
||||||
|
@ -306,6 +327,7 @@ describe(__filename, function () {
|
||||||
it('exports HTML for this Etherpad file', async function () {
|
it('exports HTML for this Etherpad file', async function () {
|
||||||
this.timeout(3000);
|
this.timeout(3000);
|
||||||
await agent.get(`/p/${testPadId}/export/html`)
|
await agent.get(`/p/${testPadId}/export/html`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('content-type', 'text/html; charset=utf-8')
|
.expect('content-type', 'text/html; charset=utf-8')
|
||||||
.expect(/<ul class="bullet"><li><ul class="bullet"><li>hello<\/ul><\/li><\/ul>/);
|
.expect(/<ul class="bullet"><li><ul class="bullet"><li>hello<\/ul><\/li><\/ul>/);
|
||||||
|
@ -315,6 +337,7 @@ describe(__filename, function () {
|
||||||
this.timeout(3000);
|
this.timeout(3000);
|
||||||
settings.allowUnknownFileEnds = false;
|
settings.allowUnknownFileEnds = false;
|
||||||
await agent.post(`/p/${testPadId}/import`)
|
await agent.post(`/p/${testPadId}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', padText, {filename: '/test.xasdasdxx', contentType: 'weirdness/jobby'})
|
.attach('file', padText, {filename: '/test.xasdasdxx', contentType: 'weirdness/jobby'})
|
||||||
.expect(400)
|
.expect(400)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -380,6 +403,8 @@ describe(__filename, function () {
|
||||||
// that a buggy makeGoodExport() doesn't cause checks to accidentally pass.
|
// that a buggy makeGoodExport() doesn't cause checks to accidentally pass.
|
||||||
const records = makeGoodExport();
|
const records = makeGoodExport();
|
||||||
await deleteTestPad();
|
await deleteTestPad();
|
||||||
|
const importedPads = await importEtherpad(records)
|
||||||
|
console.log(importedPads)
|
||||||
await importEtherpad(records)
|
await importEtherpad(records)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -389,6 +414,7 @@ describe(__filename, function () {
|
||||||
data: {directDatabaseAccess: true},
|
data: {directDatabaseAccess: true},
|
||||||
}));
|
}));
|
||||||
await agent.get(`/p/${testPadId}/export/txt`)
|
await agent.get(`/p/${testPadId}/export/txt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.match(res.text, /foo/));
|
.expect((res:any) => assert.match(res.text, /foo/));
|
||||||
|
@ -397,19 +423,19 @@ describe(__filename, function () {
|
||||||
it('missing rev', async function () {
|
it('missing rev', async function () {
|
||||||
const records:MapArrayType<any> = makeGoodExport();
|
const records:MapArrayType<any> = makeGoodExport();
|
||||||
delete records['pad:testing:revs:0'];
|
delete records['pad:testing:revs:0'];
|
||||||
await importEtherpad(records).expect(500);
|
importEtherpad(records).expect(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('bad changeset', async function () {
|
it('bad changeset', async function () {
|
||||||
const records = makeGoodExport();
|
const records = makeGoodExport();
|
||||||
records['pad:testing:revs:0'].changeset = 'garbage';
|
records['pad:testing:revs:0'].changeset = 'garbage';
|
||||||
await importEtherpad(records).expect(500);
|
importEtherpad(records).expect(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('missing attrib in pool', async function () {
|
it('missing attrib in pool', async function () {
|
||||||
const records = makeGoodExport();
|
const records = makeGoodExport();
|
||||||
records['pad:testing'].pool.nextNum++;
|
records['pad:testing'].pool.nextNum++;
|
||||||
await importEtherpad(records).expect(500);
|
(importEtherpad(records)).expect(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('extra attrib in pool', async function () {
|
it('extra attrib in pool', async function () {
|
||||||
|
@ -417,7 +443,7 @@ describe(__filename, function () {
|
||||||
const pool = records['pad:testing'].pool;
|
const pool = records['pad:testing'].pool;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
pool.numToAttrib[pool.nextNum] = ['key', 'value'];
|
pool.numToAttrib[pool.nextNum] = ['key', 'value'];
|
||||||
await importEtherpad(records).expect(500);
|
(importEtherpad(records)).expect(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changeset refers to non-existent attrib', async function () {
|
it('changeset refers to non-existent attrib', async function () {
|
||||||
|
@ -434,19 +460,19 @@ describe(__filename, function () {
|
||||||
text: 'asdffoo\n',
|
text: 'asdffoo\n',
|
||||||
attribs: '*1+4|1+4',
|
attribs: '*1+4|1+4',
|
||||||
};
|
};
|
||||||
await importEtherpad(records).expect(500);
|
(importEtherpad(records)).expect(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pad atext does not match', async function () {
|
it('pad atext does not match', async function () {
|
||||||
const records = makeGoodExport();
|
const records = makeGoodExport();
|
||||||
records['pad:testing'].atext.attribs = `*0${records['pad:testing'].atext.attribs}`;
|
records['pad:testing'].atext.attribs = `*0${records['pad:testing'].atext.attribs}`;
|
||||||
await importEtherpad(records).expect(500);
|
(importEtherpad(records)).expect(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('missing chat message', async function () {
|
it('missing chat message', async function () {
|
||||||
const records:MapArrayType<any> = makeGoodExport();
|
const records:MapArrayType<any> = makeGoodExport();
|
||||||
delete records['pad:testing:chat:0'];
|
delete records['pad:testing:chat:0'];
|
||||||
await importEtherpad(records).expect(500);
|
importEtherpad(records).expect(500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -523,7 +549,7 @@ describe(__filename, function () {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const importEtherpad = (records:MapArrayType<any>) => agent.post(`/p/${testPadId}/import`)
|
const importEtherpad = (records: MapArrayType<any>) => agent.post(`/p/${testPadId}/import`)
|
||||||
.attach('file', Buffer.from(JSON.stringify(records), 'utf8'), {
|
.attach('file', Buffer.from(JSON.stringify(records), 'utf8'), {
|
||||||
filename: '/test.etherpad',
|
filename: '/test.etherpad',
|
||||||
contentType: 'application/etherpad',
|
contentType: 'application/etherpad',
|
||||||
|
@ -543,6 +569,7 @@ describe(__filename, function () {
|
||||||
data: {directDatabaseAccess: true},
|
data: {directDatabaseAccess: true},
|
||||||
}));
|
}));
|
||||||
await agent.get(`/p/${testPadId}/export/txt`)
|
await agent.get(`/p/${testPadId}/export/txt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.equal(res.text, 'oofoo\n'));
|
.expect((res:any) => assert.equal(res.text, 'oofoo\n'));
|
||||||
|
@ -550,6 +577,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('txt request rev 1', async function () {
|
it('txt request rev 1', async function () {
|
||||||
await agent.get(`/p/${testPadId}/1/export/txt`)
|
await agent.get(`/p/${testPadId}/1/export/txt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.equal(res.text, 'ofoo\n'));
|
.expect((res:any) => assert.equal(res.text, 'ofoo\n'));
|
||||||
|
@ -557,6 +585,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('txt request rev 2', async function () {
|
it('txt request rev 2', async function () {
|
||||||
await agent.get(`/p/${testPadId}/2/export/txt`)
|
await agent.get(`/p/${testPadId}/2/export/txt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.equal(res.text, 'oofoo\n'));
|
.expect((res:any) => assert.equal(res.text, 'oofoo\n'));
|
||||||
|
@ -564,6 +593,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('txt request rev 1test returns rev 1', async function () {
|
it('txt request rev 1test returns rev 1', async function () {
|
||||||
await agent.get(`/p/${testPadId}/1test/export/txt`)
|
await agent.get(`/p/${testPadId}/1test/export/txt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.equal(res.text, 'ofoo\n'));
|
.expect((res:any) => assert.equal(res.text, 'ofoo\n'));
|
||||||
|
@ -571,6 +601,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('txt request rev test1 is 403', async function () {
|
it('txt request rev test1 is 403', async function () {
|
||||||
await agent.get(`/p/${testPadId}/test1/export/txt`)
|
await agent.get(`/p/${testPadId}/test1/export/txt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(500)
|
.expect(500)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.match(res.text, /rev is not a number/));
|
.expect((res:any) => assert.match(res.text, /rev is not a number/));
|
||||||
|
@ -578,6 +609,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('txt request rev 5 returns head rev', async function () {
|
it('txt request rev 5 returns head rev', async function () {
|
||||||
await agent.get(`/p/${testPadId}/5/export/txt`)
|
await agent.get(`/p/${testPadId}/5/export/txt`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.equal(res.text, 'oofoo\n'));
|
.expect((res:any) => assert.equal(res.text, 'oofoo\n'));
|
||||||
|
@ -585,6 +617,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('html request rev 1', async function () {
|
it('html request rev 1', async function () {
|
||||||
await agent.get(`/p/${testPadId}/1/export/html`)
|
await agent.get(`/p/${testPadId}/1/export/html`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.match(res.text, /ofoo<br>/));
|
.expect((res:any) => assert.match(res.text, /ofoo<br>/));
|
||||||
|
@ -592,6 +625,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('html request rev 2', async function () {
|
it('html request rev 2', async function () {
|
||||||
await agent.get(`/p/${testPadId}/2/export/html`)
|
await agent.get(`/p/${testPadId}/2/export/html`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.match(res.text, /oofoo<br>/));
|
.expect((res:any) => assert.match(res.text, /oofoo<br>/));
|
||||||
|
@ -599,6 +633,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('html request rev 1test returns rev 1', async function () {
|
it('html request rev 1test returns rev 1', async function () {
|
||||||
await agent.get(`/p/${testPadId}/1test/export/html`)
|
await agent.get(`/p/${testPadId}/1test/export/html`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.match(res.text, /ofoo<br>/));
|
.expect((res:any) => assert.match(res.text, /ofoo<br>/));
|
||||||
|
@ -606,6 +641,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('html request rev test1 results in 500 response', async function () {
|
it('html request rev test1 results in 500 response', async function () {
|
||||||
await agent.get(`/p/${testPadId}/test1/export/html`)
|
await agent.get(`/p/${testPadId}/test1/export/html`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(500)
|
.expect(500)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.match(res.text, /rev is not a number/));
|
.expect((res:any) => assert.match(res.text, /rev is not a number/));
|
||||||
|
@ -613,6 +649,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('html request rev 5 returns head rev', async function () {
|
it('html request rev 5 returns head rev', async function () {
|
||||||
await agent.get(`/p/${testPadId}/5/export/html`)
|
await agent.get(`/p/${testPadId}/5/export/html`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.buffer(true).parse(superagent.parse.text)
|
.buffer(true).parse(superagent.parse.text)
|
||||||
.expect((res:any) => assert.match(res.text, /oofoo<br>/));
|
.expect((res:any) => assert.match(res.text, /oofoo<br>/));
|
||||||
|
@ -643,6 +680,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('!authn !exist -> create', async function () {
|
it('!authn !exist -> create', async function () {
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
assert(await padManager.doesPadExist(testPadId));
|
assert(await padManager.doesPadExist(testPadId));
|
||||||
|
@ -653,6 +691,7 @@ describe(__filename, function () {
|
||||||
it('!authn exist -> replace', async function () {
|
it('!authn exist -> replace', async function () {
|
||||||
const pad = await createTestPad('before import');
|
const pad = await createTestPad('before import');
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
assert(await padManager.doesPadExist(testPadId));
|
assert(await padManager.doesPadExist(testPadId));
|
||||||
|
@ -662,6 +701,7 @@ describe(__filename, function () {
|
||||||
it('authn anonymous !exist -> fail', async function () {
|
it('authn anonymous !exist -> fail', async function () {
|
||||||
settings.requireAuthentication = true;
|
settings.requireAuthentication = true;
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(401);
|
.expect(401);
|
||||||
assert(!(await padManager.doesPadExist(testPadId)));
|
assert(!(await padManager.doesPadExist(testPadId)));
|
||||||
|
@ -671,6 +711,7 @@ describe(__filename, function () {
|
||||||
settings.requireAuthentication = true;
|
settings.requireAuthentication = true;
|
||||||
const pad = await createTestPad('before import\n');
|
const pad = await createTestPad('before import\n');
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(401);
|
.expect(401);
|
||||||
assert.equal(pad.text(), 'before import\n');
|
assert.equal(pad.text(), 'before import\n');
|
||||||
|
@ -679,6 +720,7 @@ describe(__filename, function () {
|
||||||
it('authn user create !exist -> create', async function () {
|
it('authn user create !exist -> create', async function () {
|
||||||
settings.requireAuthentication = true;
|
settings.requireAuthentication = true;
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.auth('user', 'user-password')
|
.auth('user', 'user-password')
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
@ -691,6 +733,7 @@ describe(__filename, function () {
|
||||||
settings.requireAuthentication = true;
|
settings.requireAuthentication = true;
|
||||||
authorize = () => 'modify';
|
authorize = () => 'modify';
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.auth('user', 'user-password')
|
.auth('user', 'user-password')
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(403);
|
.expect(403);
|
||||||
|
@ -701,6 +744,7 @@ describe(__filename, function () {
|
||||||
settings.requireAuthentication = true;
|
settings.requireAuthentication = true;
|
||||||
authorize = () => 'readOnly';
|
authorize = () => 'readOnly';
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.auth('user', 'user-password')
|
.auth('user', 'user-password')
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(403);
|
.expect(403);
|
||||||
|
@ -711,6 +755,7 @@ describe(__filename, function () {
|
||||||
settings.requireAuthentication = true;
|
settings.requireAuthentication = true;
|
||||||
const pad = await createTestPad('before import\n');
|
const pad = await createTestPad('before import\n');
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.auth('user', 'user-password')
|
.auth('user', 'user-password')
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
@ -722,6 +767,7 @@ describe(__filename, function () {
|
||||||
authorize = () => 'modify';
|
authorize = () => 'modify';
|
||||||
const pad = await createTestPad('before import\n');
|
const pad = await createTestPad('before import\n');
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.auth('user', 'user-password')
|
.auth('user', 'user-password')
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
@ -733,6 +779,7 @@ describe(__filename, function () {
|
||||||
settings.requireAuthentication = true;
|
settings.requireAuthentication = true;
|
||||||
authorize = () => 'readOnly';
|
authorize = () => 'readOnly';
|
||||||
await agent.post(`/p/${testPadIdEnc}/import`)
|
await agent.post(`/p/${testPadIdEnc}/import`)
|
||||||
|
.set("authorization", await common.generateJWTToken())
|
||||||
.auth('user', 'user-password')
|
.auth('user', 'user-password')
|
||||||
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
|
||||||
.expect(403);
|
.expect(403);
|
||||||
|
@ -744,7 +791,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
|
|
||||||
const endPoint = (point: string, version?:string) => {
|
const endPoint = (point: string, version?:string) => {
|
||||||
return `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
|
return `/api/${version || apiVersion}/${point}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
function makeid() {
|
function makeid() {
|
||||||
|
|
|
@ -8,10 +8,9 @@
|
||||||
const common = require('../../common');
|
const common = require('../../common');
|
||||||
|
|
||||||
let agent:any;
|
let agent:any;
|
||||||
const apiKey = common.apiKey;
|
|
||||||
const apiVersion = '1.2.14';
|
const apiVersion = '1.2.14';
|
||||||
|
|
||||||
const endPoint = (point: string, version?: number) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
|
const endPoint = (point: string, version?: number) => `/api/${version || apiVersion}/${point}`;
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
before(async function () { agent = await common.init(); });
|
before(async function () { agent = await common.init(); });
|
||||||
|
@ -27,6 +26,7 @@ describe(__filename, function () {
|
||||||
describe('getStats', function () {
|
describe('getStats', function () {
|
||||||
it('Gets the stats of a running instance', async function () {
|
it('Gets the stats of a running instance', async function () {
|
||||||
await agent.get(endPoint('getStats'))
|
await agent.get(endPoint('getStats'))
|
||||||
|
.set("Authorization", await common.generateJWTToken())
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
if (res.body.code !== 0) throw new Error('getStats() failed');
|
if (res.body.code !== 0) throw new Error('getStats() failed');
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ const common = require('../../common');
|
||||||
const padManager = require('../../../../node/db/PadManager');
|
const padManager = require('../../../../node/db/PadManager');
|
||||||
|
|
||||||
let agent:any;
|
let agent:any;
|
||||||
const apiKey = common.apiKey;
|
|
||||||
let apiVersion = 1;
|
let apiVersion = 1;
|
||||||
const testPadId = makeid();
|
const testPadId = makeid();
|
||||||
const newPadId = makeid();
|
const newPadId = makeid();
|
||||||
|
@ -21,7 +20,7 @@ const anotherPadId = makeid();
|
||||||
let lastEdited = '';
|
let lastEdited = '';
|
||||||
const text = generateLongText();
|
const text = generateLongText();
|
||||||
|
|
||||||
const endPoint = (point: string, version?: string) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
|
const endPoint = (point: string, version?: string) => `/api/${version || apiVersion}/${point}`;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Html document with nested lists of different types, to test its import and
|
* Html document with nested lists of different types, to test its import and
|
||||||
|
@ -60,10 +59,10 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Sanity checks', function () {
|
describe('Sanity checks', function () {
|
||||||
it('errors with invalid APIKey', async function () {
|
it('errors with invalid oauth token', async function () {
|
||||||
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343
|
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343
|
||||||
// If your APIKey is password you deserve to fail all tests anyway
|
await agent.get(`/api/${apiVersion}/createPad?padID=test`)
|
||||||
await agent.get(`/api/${apiVersion}/createPad?apikey=password&padID=test`)
|
.set("Authorization", (await common.generateJWTToken()).substring(0, 10))
|
||||||
.expect(401);
|
.expect(401);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -113,20 +112,23 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('Tests', function () {
|
describe('Tests', function () {
|
||||||
it('deletes a Pad that does not exist', async function () {
|
it('deletes a Pad that does not exist', async function () {
|
||||||
await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`)
|
await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200) // @TODO: we shouldn't expect 200 here since the pad may not exist
|
.expect(200) // @TODO: we shouldn't expect 200 here since the pad may not exist
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a new Pad', async function () {
|
it('creates a new Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets revision count of Pad', async function () {
|
it('gets revision count of Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -134,7 +136,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets saved revisions count of Pad', async function () {
|
it('gets saved revisions count of Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getSavedRevisionsCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -142,7 +145,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets saved revision list of Pad', async function () {
|
it('gets saved revision list of Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('listSavedRevisions')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -150,7 +154,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get the HTML of Pad', async function () {
|
it('get the HTML of Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert(res.body.data.html.length > 1);
|
assert(res.body.data.html.length > 1);
|
||||||
|
@ -158,13 +163,15 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('list all pads', async function () {
|
it('list all pads', async function () {
|
||||||
const res = await agent.get(endPoint('listAllPads'))
|
const res = await agent.get(endPoint('listAllPads'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert(res.body.data.padIDs.includes(testPadId));
|
assert(res.body.data.padIDs.includes(testPadId));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deletes the Pad', async function () {
|
it('deletes the Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -172,27 +179,31 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('list all pads again', async function () {
|
it('list all pads again', async function () {
|
||||||
const res = await agent.get(endPoint('listAllPads'))
|
const res = await agent.get(endPoint('listAllPads'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert(!res.body.data.padIDs.includes(testPadId));
|
assert(!res.body.data.padIDs.includes(testPadId));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get the HTML of a Pad -- Should return a failure', async function () {
|
it('get the HTML of a Pad -- Should return a failure', async function () {
|
||||||
const res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 1);
|
assert.equal(res.body.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a new Pad with text', async function () {
|
it('creates a new Pad with text', async function () {
|
||||||
const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}&text=testText`)
|
const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}&text=testText`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets the Pad text and expect it to be testText with trailing \\n', async function () {
|
it('gets the Pad text and expect it to be testText with trailing \\n', async function () {
|
||||||
const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.text, 'testText\n');
|
assert.equal(res.body.data.text, 'testText\n');
|
||||||
|
@ -200,6 +211,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('set text', async function () {
|
it('set text', async function () {
|
||||||
const res = await agent.post(endPoint('setText'))
|
const res = await agent.post(endPoint('setText'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.send({
|
.send({
|
||||||
padID: testPadId,
|
padID: testPadId,
|
||||||
text: 'testTextTwo',
|
text: 'testTextTwo',
|
||||||
|
@ -210,28 +222,32 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets the Pad text', async function () {
|
it('gets the Pad text', async function () {
|
||||||
const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.text, 'testTextTwo\n');
|
assert.equal(res.body.data.text, 'testTextTwo\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets Revision Count of a Pad', async function () {
|
it('gets Revision Count of a Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.revisions, 1);
|
assert.equal(res.body.data.revisions, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('saves Revision', async function () {
|
it('saves Revision', async function () {
|
||||||
const res = await agent.get(`${endPoint('saveRevision')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('saveRevision')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets saved revisions count of Pad again', async function () {
|
it('gets saved revisions count of Pad again', async function () {
|
||||||
const res = await agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getSavedRevisionsCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -239,7 +255,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets saved revision list of Pad again', async function () {
|
it('gets saved revision list of Pad again', async function () {
|
||||||
const res = await agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('listSavedRevisions')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -247,28 +264,32 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets User Count of a Pad', async function () {
|
it('gets User Count of a Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('padUsersCount')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('padUsersCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.padUsersCount, 0);
|
assert.equal(res.body.data.padUsersCount, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets the Read Only ID of a Pad', async function () {
|
it('Gets the Read Only ID of a Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getReadOnlyID')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert(res.body.data.readOnlyID);
|
assert(res.body.data.readOnlyID);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get Authors of the Pad', async function () {
|
it('Get Authors of the Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('listAuthorsOfPad')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('listAuthorsOfPad')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.authorIDs.length, 0);
|
assert.equal(res.body.data.authorIDs.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get When Pad was left Edited', async function () {
|
it('Get When Pad was left Edited', async function () {
|
||||||
const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert(res.body.data.lastEdited);
|
assert(res.body.data.lastEdited);
|
||||||
|
@ -277,6 +298,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('set text again', async function () {
|
it('set text again', async function () {
|
||||||
const res = await agent.post(endPoint('setText'))
|
const res = await agent.post(endPoint('setText'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.send({
|
.send({
|
||||||
padID: testPadId,
|
padID: testPadId,
|
||||||
text: 'testTextThree',
|
text: 'testTextThree',
|
||||||
|
@ -287,35 +309,40 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get When Pad was left Edited again', async function () {
|
it('Get When Pad was left Edited again', async function () {
|
||||||
const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert(res.body.data.lastEdited > lastEdited);
|
assert(res.body.data.lastEdited > lastEdited);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gets User Count of a Pad again', async function () {
|
it('gets User Count of a Pad again', async function () {
|
||||||
const res = await agent.get(`${endPoint('padUsers')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('padUsers')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.padUsers.length, 0);
|
assert.equal(res.body.data.padUsers.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deletes a Pad', async function () {
|
it('deletes a Pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('deletePad')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates the Pad again', async function () {
|
it('creates the Pad again', async function () {
|
||||||
const res = await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('createPad')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sets text on a pad Id', async function () {
|
it('Sets text on a pad Id', async function () {
|
||||||
const res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`)
|
const res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.field({text})
|
.field({text})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
|
@ -323,7 +350,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets text on a pad Id', async function () {
|
it('Gets text on a pad Id', async function () {
|
||||||
const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -331,7 +359,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sets text on a pad Id including an explicit newline', async function () {
|
it('Sets text on a pad Id including an explicit newline', async function () {
|
||||||
const res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`)
|
const res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.field({text: `${text}\n`})
|
.field({text: `${text}\n`})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
|
@ -339,7 +368,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Gets text on a pad Id and doesn't have an excess newline", async function () {
|
it("Gets text on a pad Id and doesn't have an excess newline", async function () {
|
||||||
const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -347,7 +377,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets when pad was last edited', async function () {
|
it('Gets when pad was last edited', async function () {
|
||||||
const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.notEqual(res.body.lastEdited, 0);
|
assert.notEqual(res.body.lastEdited, 0);
|
||||||
|
@ -355,14 +386,16 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Move a Pad to a different Pad ID', async function () {
|
it('Move a Pad to a different Pad ID', async function () {
|
||||||
const res = await agent.get(
|
const res = await agent.get(
|
||||||
`${endPoint('movePad')}&sourceID=${testPadId}&destinationID=${newPadId}&force=true`)
|
`${endPoint('movePad')}?sourceID=${testPadId}&destinationID=${newPadId}&force=true`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets text from new pad', async function () {
|
it('Gets text from new pad', async function () {
|
||||||
const res = await agent.get(`${endPoint('getText')}&padID=${newPadId}`)
|
const res = await agent.get(`${endPoint('getText')}?padID=${newPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.text, `${text}\n`);
|
assert.equal(res.body.data.text, `${text}\n`);
|
||||||
|
@ -370,21 +403,24 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Move pad back to original ID', async function () {
|
it('Move pad back to original ID', async function () {
|
||||||
const res = await agent.get(
|
const res = await agent.get(
|
||||||
`${endPoint('movePad')}&sourceID=${newPadId}&destinationID=${testPadId}&force=false`)
|
`${endPoint('movePad')}?sourceID=${newPadId}&destinationID=${testPadId}&force=false`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get text using original ID', async function () {
|
it('Get text using original ID', async function () {
|
||||||
const res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.text, `${text}\n`);
|
assert.equal(res.body.data.text, `${text}\n`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get last edit of original ID', async function () {
|
it('Get last edit of original ID', async function () {
|
||||||
const res = await agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
|
const res = await agent.get(`${endPoint('getLastEdited')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.notEqual(res.body.lastEdited, 0);
|
assert.notEqual(res.body.lastEdited, 0);
|
||||||
|
@ -392,11 +428,13 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Append text to a pad Id', async function () {
|
it('Append text to a pad Id', async function () {
|
||||||
let res = await agent.get(
|
let res = await agent.get(
|
||||||
`${endPoint('appendText', '1.2.13')}&padID=${testPadId}&text=hello`)
|
`${endPoint('appendText', '1.2.13')}?padID=${testPadId}&text=hello`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -404,7 +442,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getText of old revision', async function () {
|
it('getText of old revision', async function () {
|
||||||
let res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
|
let res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -412,7 +451,8 @@ describe(__filename, function () {
|
||||||
assert(rev != null);
|
assert(rev != null);
|
||||||
assert(Number.isInteger(rev));
|
assert(Number.isInteger(rev));
|
||||||
assert(rev > 0);
|
assert(rev > 0);
|
||||||
res = await agent.get(`${endPoint('getText')}&padID=${testPadId}&rev=${rev - 1}`)
|
res = await agent.get(`${endPoint('getText')}?padID=${testPadId}&rev=${rev - 1}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -422,6 +462,7 @@ describe(__filename, function () {
|
||||||
it('Sets the HTML of a Pad attempting to pass ugly HTML', async function () {
|
it('Sets the HTML of a Pad attempting to pass ugly HTML', async function () {
|
||||||
const html = '<div><b>Hello HTML</title></head></div>';
|
const html = '<div><b>Hello HTML</title></head></div>';
|
||||||
const res = await agent.post(endPoint('setHTML'))
|
const res = await agent.post(endPoint('setHTML'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.send({
|
.send({
|
||||||
padID: testPadId,
|
padID: testPadId,
|
||||||
html,
|
html,
|
||||||
|
@ -433,6 +474,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('Pad with complex nested lists of different types', async function () {
|
it('Pad with complex nested lists of different types', async function () {
|
||||||
let res = await agent.post(endPoint('setHTML'))
|
let res = await agent.post(endPoint('setHTML'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.send({
|
.send({
|
||||||
padID: testPadId,
|
padID: testPadId,
|
||||||
html: ulHtml,
|
html: ulHtml,
|
||||||
|
@ -440,7 +482,8 @@ describe(__filename, function () {
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
|
res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase();
|
const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase();
|
||||||
|
@ -448,11 +491,13 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Pad with white space between list items', async function () {
|
it('Pad with white space between list items', async function () {
|
||||||
let res = await agent.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${ulSpaceHtml}`)
|
let res = await agent.get(`${endPoint('setHTML')}?padID=${testPadId}&html=${ulSpaceHtml}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
res = await agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
|
res = await agent.get(`${endPoint('getHTML')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase();
|
const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase();
|
||||||
|
@ -461,7 +506,8 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('errors if pad can be created', async function () {
|
it('errors if pad can be created', async function () {
|
||||||
await Promise.all(['/', '%23', '%3F', '%26'].map(async (badUrlChar) => {
|
await Promise.all(['/', '%23', '%3F', '%26'].map(async (badUrlChar) => {
|
||||||
const res = await agent.get(`${endPoint('createPad')}&padID=${badUrlChar}`)
|
const res = await agent.get(`${endPoint('createPad')}?padID=${badUrlChar}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 1);
|
assert.equal(res.body.code, 1);
|
||||||
}));
|
}));
|
||||||
|
@ -469,49 +515,57 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('copies the content of a existent pad', async function () {
|
it('copies the content of a existent pad', async function () {
|
||||||
const res = await agent.get(
|
const res = await agent.get(
|
||||||
`${endPoint('copyPad')}&sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`)
|
`${endPoint('copyPad')}?sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not add an useless revision', async function () {
|
it('does not add an useless revision', async function () {
|
||||||
let res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`)
|
let res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.field({text: 'identical text\n'})
|
.field({text: 'identical text\n'})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
|
||||||
res = await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
|
res = await agent.get(`${endPoint('getText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.text, 'identical text\n');
|
assert.equal(res.body.data.text, 'identical text\n');
|
||||||
|
|
||||||
res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
|
res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
const revCount = res.body.data.revisions;
|
const revCount = res.body.data.revisions;
|
||||||
|
|
||||||
res = await agent.post(`${endPoint('setText')}&padID=${testPadId}`)
|
res = await agent.post(`${endPoint('setText')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.field({text: 'identical text\n'})
|
.field({text: 'identical text\n'})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
|
||||||
res = await agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
|
res = await agent.get(`${endPoint('getRevisionsCount')}?padID=${testPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.data.revisions, revCount);
|
assert.equal(res.body.data.revisions, revCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a new Pad with empty text', async function () {
|
it('creates a new Pad with empty text', async function () {
|
||||||
await agent.get(`${endPoint('createPad')}&padID=${anotherPadId}&text=`)
|
await agent.get(`${endPoint('createPad')}?padID=${anotherPadId}&text=`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
assert.equal(res.body.code, 0, 'Unable to create new Pad');
|
assert.equal(res.body.code, 0, 'Unable to create new Pad');
|
||||||
});
|
});
|
||||||
await agent.get(`${endPoint('getText')}&padID=${anotherPadId}`)
|
await agent.get(`${endPoint('getText')}?padID=${anotherPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -521,7 +575,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deletes with empty text', async function () {
|
it('deletes with empty text', async function () {
|
||||||
await agent.get(`${endPoint('deletePad')}&padID=${anotherPadId}`)
|
await agent.get(`${endPoint('deletePad')}?padID=${anotherPadId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res: any) => {
|
.expect((res: any) => {
|
||||||
|
@ -543,8 +598,9 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a successful response', async function () {
|
it('returns a successful response', async function () {
|
||||||
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` +
|
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` +
|
||||||
`&destinationID=${newPad}&force=false`)
|
`&destinationID=${newPad}&force=false`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -552,10 +608,12 @@ describe(__filename, function () {
|
||||||
|
|
||||||
// this test validates if the source pad's text and attributes are kept
|
// this test validates if the source pad's text and attributes are kept
|
||||||
it('creates a new pad with the same content as the source pad', async function () {
|
it('creates a new pad with the same content as the source pad', async function () {
|
||||||
let res = await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` +
|
let res = await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` +
|
||||||
`&destinationID=${newPad}&force=false`);
|
`&destinationID=${newPad}&force=false`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()));
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
res = await agent.get(`${endPoint('getHTML')}&padID=${newPad}`)
|
res = await agent.get(`${endPoint('getHTML')}?padID=${newPad}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200);
|
.expect(200);
|
||||||
const receivedHtml = res.body.data.html.replace('<br><br></body>', '</body>').toLowerCase();
|
const receivedHtml = res.body.data.html.replace('<br><br></body>', '</body>').toLowerCase();
|
||||||
assert.equal(receivedHtml, expectedHtml);
|
assert.equal(receivedHtml, expectedHtml);
|
||||||
|
@ -564,8 +622,9 @@ describe(__filename, function () {
|
||||||
it('copying to a non-existent group throws an error', async function () {
|
it('copying to a non-existent group throws an error', async function () {
|
||||||
const padWithNonExistentGroup = `notExistentGroup$${newPad}`;
|
const padWithNonExistentGroup = `notExistentGroup$${newPad}`;
|
||||||
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` +
|
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` +
|
||||||
`&sourceID=${sourcePadId}` +
|
`?sourceID=${sourcePadId}` +
|
||||||
`&destinationID=${padWithNonExistentGroup}&force=true`)
|
`&destinationID=${padWithNonExistentGroup}&force=true`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200);
|
.expect(200);
|
||||||
assert.equal(res.body.code, 1);
|
assert.equal(res.body.code, 1);
|
||||||
});
|
});
|
||||||
|
@ -577,16 +636,18 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('force=false fails', async function () {
|
it('force=false fails', async function () {
|
||||||
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` +
|
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` +
|
||||||
`&sourceID=${sourcePadId}` +
|
`?sourceID=${sourcePadId}` +
|
||||||
`&destinationID=${newPad}&force=false`)
|
`&destinationID=${newPad}&force=false`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200);
|
.expect(200);
|
||||||
assert.equal(res.body.code, 1);
|
assert.equal(res.body.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('force=true succeeds', async function () {
|
it('force=true succeeds', async function () {
|
||||||
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` +
|
const res = await agent.get(`${endPoint('copyPadWithoutHistory')}` +
|
||||||
`&sourceID=${sourcePadId}` +
|
`?sourceID=${sourcePadId}` +
|
||||||
`&destinationID=${newPad}&force=true`)
|
`&destinationID=${newPad}&force=true`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200);
|
.expect(200);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
});
|
});
|
||||||
|
@ -613,7 +674,8 @@ describe(__filename, function () {
|
||||||
// state between the two attribute pools caused corruption.
|
// state between the two attribute pools caused corruption.
|
||||||
|
|
||||||
const getHtml = async (padId:string) => {
|
const getHtml = async (padId:string) => {
|
||||||
const res = await agent.get(`${endPoint('getHTML')}&padID=${padId}`)
|
const res = await agent.get(`${endPoint('getHTML')}?padID=${padId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
@ -622,6 +684,7 @@ describe(__filename, function () {
|
||||||
|
|
||||||
const setBody = async (padId: string, bodyHtml: string) => {
|
const setBody = async (padId: string, bodyHtml: string) => {
|
||||||
await agent.post(endPoint('setHTML'))
|
await agent.post(endPoint('setHTML'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.send({padID: padId, html: `<!DOCTYPE HTML><html><body>${bodyHtml}</body></html>`})
|
.send({padID: padId, html: `<!DOCTYPE HTML><html><body>${bodyHtml}</body></html>`})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -631,8 +694,9 @@ describe(__filename, function () {
|
||||||
const origHtml = await getHtml(sourcePadId);
|
const origHtml = await getHtml(sourcePadId);
|
||||||
assert.doesNotMatch(origHtml, /<strong>/);
|
assert.doesNotMatch(origHtml, /<strong>/);
|
||||||
assert.doesNotMatch(origHtml, /<em>/);
|
assert.doesNotMatch(origHtml, /<em>/);
|
||||||
await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` +
|
await agent.get(`${endPoint('copyPadWithoutHistory')}?sourceID=${sourcePadId}` +
|
||||||
`&destinationID=${newPad}&force=false`)
|
`&destinationID=${newPad}&force=false`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => assert.equal(res.body.code, 0));
|
.expect((res:any) => assert.equal(res.body.code, 0));
|
||||||
|
@ -672,8 +736,10 @@ describe(__filename, function () {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const createNewPadWithHtml = async (padId: string, html: string) => {
|
const createNewPadWithHtml = async (padId: string, html: string) => {
|
||||||
await agent.get(`${endPoint('createPad')}&padID=${padId}`);
|
await agent.get(`${endPoint('createPad')}?padID=${padId}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()));
|
||||||
await agent.post(endPoint('setHTML'))
|
await agent.post(endPoint('setHTML'))
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.send({
|
.send({
|
||||||
padID: padId,
|
padID: padId,
|
||||||
html,
|
html,
|
||||||
|
|
|
@ -14,13 +14,14 @@ describe(__filename, function () {
|
||||||
let pad: PadType;
|
let pad: PadType;
|
||||||
|
|
||||||
const restoreRevision = async (v:string, padId: string, rev: number, authorId:string|null = null) => {
|
const restoreRevision = async (v:string, padId: string, rev: number, authorId:string|null = null) => {
|
||||||
|
// @ts-ignore
|
||||||
const p = new URLSearchParams(Object.entries({
|
const p = new URLSearchParams(Object.entries({
|
||||||
apikey: common.apiKey,
|
|
||||||
padID: padId,
|
padID: padId,
|
||||||
rev,
|
rev,
|
||||||
...(authorId == null ? {} : {authorId}),
|
...(authorId == null ? {} : {authorId}),
|
||||||
}));
|
}));
|
||||||
const res = await agent.get(`/api/${v}/restoreRevision?${p}`)
|
const res = await agent.get(`/api/${v}/restoreRevision?${p}`)
|
||||||
|
.set("Authorization", (await common.generateJWTToken()))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/);
|
.expect('Content-Type', /json/);
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
|
|
|
@ -1,25 +1,33 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import {agent, generateJWTToken, init, logger} from "../../common";
|
||||||
|
|
||||||
|
import TestAgent from "supertest/lib/agent";
|
||||||
|
import supertest from "supertest";
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const common = require('../../common');
|
|
||||||
const db = require('../../../../node/db/DB');
|
const db = require('../../../../node/db/DB');
|
||||||
|
|
||||||
let agent:any;
|
|
||||||
const apiKey = common.apiKey;
|
|
||||||
let apiVersion = 1;
|
let apiVersion = 1;
|
||||||
let groupID = '';
|
let groupID = '';
|
||||||
let authorID = '';
|
let authorID = '';
|
||||||
let sessionID = '';
|
let sessionID = '';
|
||||||
let padID = makeid();
|
let padID = makeid();
|
||||||
|
|
||||||
const endPoint = (point:string) => `/api/${apiVersion}/${point}?apikey=${apiKey}`;
|
const endPoint = (point:string) => {
|
||||||
|
return `/api/${apiVersion}/${point}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let preparedAgent: TestAgent<supertest.Test>
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
before(async function () { agent = await common.init(); });
|
before(async function () {
|
||||||
|
preparedAgent = await init();
|
||||||
|
});
|
||||||
|
|
||||||
describe('API Versioning', function () {
|
describe('API Versioning', function () {
|
||||||
it('errors if can not connect', async function () {
|
it('errors if can not connect', async function () {
|
||||||
await agent.get('/api/')
|
await agent!.get('/api/')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
assert(res.body.currentVersion);
|
assert(res.body.currentVersion);
|
||||||
|
@ -60,7 +68,8 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('API: Group creation and deletion', function () {
|
describe('API: Group creation and deletion', function () {
|
||||||
it('createGroup', async function () {
|
it('createGroup', async function () {
|
||||||
await agent.get(endPoint('createGroup'))
|
await agent!.get(endPoint('createGroup'))
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -71,7 +80,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('listSessionsOfGroup for empty group', async function () {
|
it('listSessionsOfGroup for empty group', async function () {
|
||||||
await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
|
await agent!.get(`${endPoint('listSessionsOfGroup')}?groupID=${groupID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -81,7 +91,9 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deleteGroup', async function () {
|
it('deleteGroup', async function () {
|
||||||
await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
|
await agent!
|
||||||
|
.get(`${endPoint('deleteGroup')}?groupID=${groupID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -92,7 +104,8 @@ describe(__filename, function () {
|
||||||
it('createGroupIfNotExistsFor', async function () {
|
it('createGroupIfNotExistsFor', async function () {
|
||||||
const mapper = makeid();
|
const mapper = makeid();
|
||||||
let groupId: string;
|
let groupId: string;
|
||||||
await agent.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=${mapper}`)
|
await preparedAgent.get(`${endPoint('createGroupIfNotExistsFor')}?groupMapper=${mapper}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -101,7 +114,8 @@ describe(__filename, function () {
|
||||||
assert(groupId);
|
assert(groupId);
|
||||||
});
|
});
|
||||||
// Passing the same mapper should return the same group ID.
|
// Passing the same mapper should return the same group ID.
|
||||||
await agent.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=${mapper}`)
|
await preparedAgent.get(`${endPoint('createGroupIfNotExistsFor')}?groupMapper=${mapper}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -110,7 +124,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
// Deleting the group should clean up the mapping.
|
// Deleting the group should clean up the mapping.
|
||||||
assert.equal(await db.get(`mapper2group:${mapper}`), groupId!);
|
assert.equal(await db.get(`mapper2group:${mapper}`), groupId!);
|
||||||
await agent.get(`${endPoint('deleteGroup')}&groupID=${groupId!}`)
|
await preparedAgent.get(`${endPoint('deleteGroup')}?groupID=${groupId!}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -122,7 +137,8 @@ describe(__filename, function () {
|
||||||
// Test coverage for https://github.com/ether/etherpad-lite/issues/4227
|
// Test coverage for https://github.com/ether/etherpad-lite/issues/4227
|
||||||
// Creates a group, creates 2 sessions, 2 pads and then deletes the group.
|
// Creates a group, creates 2 sessions, 2 pads and then deletes the group.
|
||||||
it('createGroup', async function () {
|
it('createGroup', async function () {
|
||||||
await agent.get(endPoint('createGroup'))
|
await preparedAgent.get(endPoint('createGroup'))
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -133,7 +149,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createAuthor', async function () {
|
it('createAuthor', async function () {
|
||||||
await agent.get(endPoint('createAuthor'))
|
await preparedAgent.get(endPoint('createAuthor'))
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -144,8 +161,9 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createSession', async function () {
|
it('createSession', async function () {
|
||||||
await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` +
|
await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` +
|
||||||
'&validUntil=999999999999')
|
'&validUntil=999999999999')
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -156,8 +174,9 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createSession', async function () {
|
it('createSession', async function () {
|
||||||
await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` +
|
await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` +
|
||||||
'&validUntil=999999999999')
|
'&validUntil=999999999999')
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -168,7 +187,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createGroupPad', async function () {
|
it('createGroupPad', async function () {
|
||||||
await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x1234567`)
|
await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=x1234567`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -177,7 +197,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createGroupPad', async function () {
|
it('createGroupPad', async function () {
|
||||||
await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x12345678`)
|
await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=x12345678`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -186,7 +207,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deleteGroup', async function () {
|
it('deleteGroup', async function () {
|
||||||
await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
|
await preparedAgent.get(`${endPoint('deleteGroup')}?groupID=${groupID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -198,7 +220,8 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('API: Author creation', function () {
|
describe('API: Author creation', function () {
|
||||||
it('createGroup', async function () {
|
it('createGroup', async function () {
|
||||||
await agent.get(endPoint('createGroup'))
|
await preparedAgent.get(endPoint('createGroup'))
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -209,7 +232,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createAuthor', async function () {
|
it('createAuthor', async function () {
|
||||||
await agent.get(endPoint('createAuthor'))
|
await preparedAgent.get(endPoint('createAuthor'))
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -219,7 +243,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createAuthor with name', async function () {
|
it('createAuthor with name', async function () {
|
||||||
await agent.get(`${endPoint('createAuthor')}&name=john`)
|
await preparedAgent.get(`${endPoint('createAuthor')}?name=john`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -230,7 +255,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createAuthorIfNotExistsFor', async function () {
|
it('createAuthorIfNotExistsFor', async function () {
|
||||||
await agent.get(`${endPoint('createAuthorIfNotExistsFor')}&authorMapper=chris`)
|
await preparedAgent.get(`${endPoint('createAuthorIfNotExistsFor')}?authorMapper=chris`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -240,7 +266,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getAuthorName', async function () {
|
it('getAuthorName', async function () {
|
||||||
await agent.get(`${endPoint('getAuthorName')}&authorID=${authorID}`)
|
await preparedAgent.get(`${endPoint('getAuthorName')}?authorID=${authorID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -252,8 +279,9 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('API: Sessions', function () {
|
describe('API: Sessions', function () {
|
||||||
it('createSession', async function () {
|
it('createSession', async function () {
|
||||||
await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` +
|
await preparedAgent.get(`${endPoint('createSession')}?authorID=${authorID}&groupID=${groupID}` +
|
||||||
'&validUntil=999999999999')
|
'&validUntil=999999999999')
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -264,7 +292,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getSessionInfo', async function () {
|
it('getSessionInfo', async function () {
|
||||||
await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
|
await preparedAgent.get(`${endPoint('getSessionInfo')}?sessionID=${sessionID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -276,7 +305,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('listSessionsOfGroup', async function () {
|
it('listSessionsOfGroup', async function () {
|
||||||
await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
|
await preparedAgent.get(`${endPoint('listSessionsOfGroup')}?groupID=${groupID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -286,7 +316,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deleteSession', async function () {
|
it('deleteSession', async function () {
|
||||||
await agent.get(`${endPoint('deleteSession')}&sessionID=${sessionID}`)
|
await preparedAgent.get(`${endPoint('deleteSession')}?sessionID=${sessionID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -295,7 +326,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getSessionInfo of deleted session', async function () {
|
it('getSessionInfo of deleted session', async function () {
|
||||||
await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
|
await preparedAgent.get(`${endPoint('getSessionInfo')}?sessionID=${sessionID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -306,7 +338,8 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('API: Group pad management', function () {
|
describe('API: Group pad management', function () {
|
||||||
it('listPads', async function () {
|
it('listPads', async function () {
|
||||||
await agent.get(`${endPoint('listPads')}&groupID=${groupID}`)
|
await preparedAgent.get(`${endPoint('listPads')}?groupID=${groupID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -316,7 +349,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('createGroupPad', async function () {
|
it('createGroupPad', async function () {
|
||||||
await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=${padID}`)
|
await preparedAgent.get(`${endPoint('createGroupPad')}?groupID=${groupID}&padName=${padID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -326,10 +360,11 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('listPads after creating a group pad', async function () {
|
it('listPads after creating a group pad', async function () {
|
||||||
await agent.get(`${endPoint('listPads')}&groupID=${groupID}`)
|
await preparedAgent.get(`${endPoint('listPads')}?groupID=${groupID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res) => {
|
||||||
assert.equal(res.body.code, 0);
|
assert.equal(res.body.code, 0);
|
||||||
assert.equal(res.body.data.padIDs.length, 1);
|
assert.equal(res.body.data.padIDs.length, 1);
|
||||||
});
|
});
|
||||||
|
@ -338,7 +373,8 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('API: Pad security', function () {
|
describe('API: Pad security', function () {
|
||||||
it('getPublicStatus', async function () {
|
it('getPublicStatus', async function () {
|
||||||
await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
|
await preparedAgent.get(`${endPoint('getPublicStatus')}?padID=${padID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -348,7 +384,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setPublicStatus', async function () {
|
it('setPublicStatus', async function () {
|
||||||
await agent.get(`${endPoint('setPublicStatus')}&padID=${padID}&publicStatus=true`)
|
await preparedAgent.get(`${endPoint('setPublicStatus')}?padID=${padID}&publicStatus=true`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -357,7 +394,8 @@ describe(__filename, function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getPublicStatus after changing public status', async function () {
|
it('getPublicStatus after changing public status', async function () {
|
||||||
await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
|
await preparedAgent.get(`${endPoint('getPublicStatus')}?padID=${padID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
@ -373,7 +411,8 @@ describe(__filename, function () {
|
||||||
|
|
||||||
describe('API: Misc', function () {
|
describe('API: Misc', function () {
|
||||||
it('listPadsOfAuthor', async function () {
|
it('listPadsOfAuthor', async function () {
|
||||||
await agent.get(`${endPoint('listPadsOfAuthor')}&authorID=${authorID}`)
|
await preparedAgent.get(`${endPoint('listPadsOfAuthor')}?authorID=${authorID}`)
|
||||||
|
.set("Authorization", await generateJWTToken())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect((res:any) => {
|
.expect((res:any) => {
|
||||||
|
|
|
@ -31,8 +31,8 @@ describe('API Versioning', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Permission', function () {
|
describe('Permission', function () {
|
||||||
it('errors with invalid APIKey', function (done) {
|
it('errors with invalid OAuth token', function (done) {
|
||||||
api.get(`/api/${apiVersion}/createPad?apikey=wrong_password&padID=test`)
|
api.get(`/api/${apiVersion}/createPad?padID=test`)
|
||||||
.expect(401, done);
|
.expect(401, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -650,5 +650,24 @@
|
||||||
/*
|
/*
|
||||||
* Enable/Disable case-insensitive pad names.
|
* Enable/Disable case-insensitive pad names.
|
||||||
*/
|
*/
|
||||||
"lowerCasePadIds": false
|
"lowerCasePadIds": false,
|
||||||
|
"sso": {
|
||||||
|
"issuer": "${SSO_ISSUER:http://localhost:9001}",
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"client_id": "${ADMIN_CLIENT:admin_client}",
|
||||||
|
"client_secret": "${ADMIN_SECRET:admin}",
|
||||||
|
"grant_types": ["authorization_code"],
|
||||||
|
"response_types": ["code"],
|
||||||
|
"redirect_uris": ["${ADMIN_REDIRECT:http://localhost:9001/admin/}", "https://oauth.pstmn.io/v1/callback"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "${USER_CLIENT:user_client}",
|
||||||
|
"client_secret": "${USER_SECRET:user}",
|
||||||
|
"grant_types": ["authorization_code"],
|
||||||
|
"response_types": ["code"],
|
||||||
|
"redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
24
ui/.gitignore
vendored
Normal file
24
ui/.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
24
ui/consent.html
Normal file
24
ui/consent.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Consent Etherpad</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<div class="login-background login-page">
|
||||||
|
<div class="login-box login-form">
|
||||||
|
<h1 class="login-title">Etherpad <span id="client"></span></h1>
|
||||||
|
<form class="login-inner-box input-control" method="post">
|
||||||
|
<input type="hidden" name="prompt" value="consent"/>
|
||||||
|
<input type="submit" value="Login" class="login-button"/>
|
||||||
|
<div id="error"></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="module" src="/src/consent.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
38
ui/login.html
Normal file
38
ui/login.html
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title>SSO Etherpad</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<div class="login-background login-page">
|
||||||
|
<div class="login-box login-form">
|
||||||
|
<h1 class="login-title">Etherpad <span id="client"></span></h1>
|
||||||
|
<form class="login-inner-box input-control">
|
||||||
|
<label>
|
||||||
|
<input class="login-textinput input-control" required type="text" name="login" placeholder="Username"/>
|
||||||
|
</label>
|
||||||
|
<div class="icon-input">
|
||||||
|
<label class="password-label">
|
||||||
|
<input class="login-textinput" type="password" required name="password" placeholder="Password"/>
|
||||||
|
<svg id="eye-visible" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 toggle-password-visibility">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||||
|
</svg>
|
||||||
|
<svg id="eye-hide" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" />
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Login" class="login-button"/>
|
||||||
|
<div id="error"></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
15
ui/package.json
Normal file
15
ui/package.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "ui",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.2.0"
|
||||||
|
}
|
||||||
|
}
|
35
ui/src/consent.ts
Normal file
35
ui/src/consent.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import "./style.css"
|
||||||
|
//import {MapArrayType} from "ep_etherpad-lite/node/types/MapType";
|
||||||
|
|
||||||
|
const form = document.querySelector('form')!;
|
||||||
|
const sessionId = new URLSearchParams(window.location.search).get('state');
|
||||||
|
|
||||||
|
form.action = '/interaction/' + sessionId;
|
||||||
|
|
||||||
|
/*form.addEventListener('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const data: MapArrayType<any> = {};
|
||||||
|
formData.forEach((value, key) => {
|
||||||
|
data[key] = value;
|
||||||
|
});
|
||||||
|
const sessionId = new URLSearchParams(window.location.search).get('state');
|
||||||
|
|
||||||
|
fetch('/interaction/' + sessionId, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
if (response.redirected) {
|
||||||
|
window.location.href = response.url;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('error')!.innerText = "Error signing in";
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
document.getElementById('error')!.innerText = "Error signing in" + error;
|
||||||
|
})
|
||||||
|
});*/
|
58
ui/src/main.ts
Normal file
58
ui/src/main.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import './style.css'
|
||||||
|
import {MapArrayType} from "ep_etherpad-lite/node/types/MapType.ts";
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById('client')!.innerText = searchParams.get('client_id')!;
|
||||||
|
|
||||||
|
const form = document.querySelector('form')!;
|
||||||
|
form.addEventListener('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const data: MapArrayType<any> = {};
|
||||||
|
formData.forEach((value, key) => {
|
||||||
|
data[key] = value;
|
||||||
|
});
|
||||||
|
const sessionId = new URLSearchParams(window.location.search).get('state');
|
||||||
|
|
||||||
|
fetch('/interaction/' + sessionId, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
redirect: 'follow',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
if (response.redirected) {
|
||||||
|
window.location.href = response.url;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('error')!.innerText = "Error signing in";
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
document.getElementById('error')!.innerText = "Error signing in" + error;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const hidePassword = document.querySelector('.toggle-password-visibility')! as HTMLElement
|
||||||
|
const showPassword = document.getElementById('eye-hide')! as HTMLElement
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
const passwordInput = document.getElementsByName('password')[0] as HTMLInputElement;
|
||||||
|
if (passwordInput.type === 'password') {
|
||||||
|
showPassword.style.display = 'block';
|
||||||
|
hidePassword.style.display = 'none';
|
||||||
|
passwordInput.type = 'text';
|
||||||
|
} else {
|
||||||
|
showPassword.style.display = 'none';
|
||||||
|
hidePassword.style.display = 'block';
|
||||||
|
passwordInput.type = 'password';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hidePassword.addEventListener('click', togglePasswordVisibility);
|
||||||
|
showPassword.addEventListener('click', togglePasswordVisibility);
|
||||||
|
|
||||||
|
|
125
ui/src/style.css
Normal file
125
ui/src/style.css
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
:root {
|
||||||
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
--color-etherpad: #0f775b;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus,
|
||||||
|
button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
color: #213547;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #747bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-box {
|
||||||
|
background-color: #f2f6f7;
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
color: #607278;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: radial-gradient(100% 100% at 50% 0%, var(--color-etherpad) 0%, #003A47 100%) fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #d1d1d1;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-inner-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-inner-box input[type=submit] {
|
||||||
|
background-color: var(--color-etherpad);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-label {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-label svg {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
cursor: pointer;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#eye-hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
label input {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
1
ui/src/typescript.svg
Normal file
1
ui/src/typescript.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#007ACC" d="M0 128v128h256V0H0z"></path><path fill="#FFF" d="m56.612 128.85l-.081 10.483h33.32v94.68h23.568v-94.68h33.321v-10.28c0-5.69-.122-10.444-.284-10.566c-.122-.162-20.4-.244-44.983-.203l-44.74.122l-.121 10.443Zm149.955-10.742c6.501 1.625 11.459 4.51 16.01 9.224c2.357 2.52 5.851 7.111 6.136 8.208c.08.325-11.053 7.802-17.798 11.988c-.244.162-1.22-.894-2.317-2.52c-3.291-4.795-6.745-6.867-12.028-7.233c-7.76-.528-12.759 3.535-12.718 10.321c0 1.992.284 3.17 1.097 4.795c1.707 3.536 4.876 5.649 14.832 9.956c18.326 7.883 26.168 13.084 31.045 20.48c5.445 8.249 6.664 21.415 2.966 31.208c-4.063 10.646-14.14 17.879-28.323 20.276c-4.388.772-14.79.65-19.504-.203c-10.28-1.828-20.033-6.908-26.047-13.572c-2.357-2.6-6.949-9.387-6.664-9.874c.122-.163 1.178-.813 2.356-1.504c1.138-.65 5.446-3.129 9.509-5.485l7.355-4.267l1.544 2.276c2.154 3.29 6.867 7.801 9.712 9.305c8.167 4.307 19.383 3.698 24.909-1.26c2.357-2.153 3.332-4.388 3.332-7.68c0-2.966-.366-4.266-1.91-6.501c-1.99-2.845-6.054-5.242-17.595-10.24c-13.206-5.69-18.895-9.224-24.096-14.832c-3.007-3.25-5.852-8.452-7.03-12.8c-.975-3.617-1.22-12.678-.447-16.335c2.723-12.76 12.353-21.659 26.25-24.3c4.51-.853 14.994-.528 19.424.569Z"></path></svg>
|
After Width: | Height: | Size: 1.4 KiB |
1
ui/src/vite-env.d.ts
vendored
Normal file
1
ui/src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="vite/client" />
|
23
ui/tsconfig.json
Normal file
23
ui/tsconfig.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
17
ui/vite.config.ts
Normal file
17
ui/vite.config.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// vite.config.js
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
base: '/views/',
|
||||||
|
build: {
|
||||||
|
outDir: resolve(__dirname, '../src/static/oidc'),
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
main: resolve(__dirname, 'consent.html'),
|
||||||
|
nested: resolve(__dirname, 'login.html'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emptyOutDir: true,
|
||||||
|
},
|
||||||
|
})
|
Loading…
Reference in a new issue