Compare commits

...

54 Commits

Author SHA1 Message Date
Juan Picado
f0501eea78 chore: update versions (next-8) (#5278)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-06-17 21:03:37 +02:00
Marc Bernard
d22ef4e61e fix: config web login default (#5280) 2025-06-17 20:03:49 +02:00
Alan Agius
96d2f0f24d fix(core): remove lodash from a dependency (#5277)
This removes the usage of `lodash`.
2025-06-16 20:45:16 +02:00
verdacciobot
0b66de1274 chore: updated static data 2025-06-16 00:17:11 +00:00
renovate[bot]
897ca09828 chore(deps): update dependency @crowdin/cli to v4.7.1 (#5274) 2025-06-15 00:08:40 +02:00
renovate[bot]
85bf8dc6d8 fix(deps): update dependency semver to v7.7.2 (#5272) 2025-06-14 20:29:09 +02:00
renovate[bot]
7fa42b5a4e chore(deps): update babel monorepo (#5273) 2025-06-14 20:07:18 +02:00
renovate[bot]
4ec28f20b3 fix(deps): update dependency debug to v4.4.1 (#5271) 2025-06-14 17:24:56 +02:00
renovate[bot]
003be2a561 fix(deps): update dependency marked to v15.0.12 (#5265)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 07:22:42 +02:00
verdacciobot
bd3d50b7c0 chore: updated static data 2025-06-09 00:17:08 +00:00
renovate[bot]
93348f0208 chore(deps): pin actions/checkout action to 11bd719 (#5248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-07 10:26:07 +02:00
Juan Picado
c120b32d07 chore: update versions (next-8) (#5193)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-06-07 10:11:45 +02:00
Juan Picado
47ee18c3dd Update x-smok-test-docker.yml 2025-06-07 10:09:18 +02:00
Juan Picado
0938e965cb chore: fix library import 2025-06-07 09:45:43 +02:00
Marc Bernard
72c3cbb5bf chore(utils): replace @verdaccio/utils dependency with core (#5241) 2025-06-07 09:31:53 +02:00
Marc Bernard
626ae6aae9 feat: web v1 login frontend (experimental) (#5202)
* feat: web v1 login backend (experimental)

* Add missing createUser

* Change ui url

* Update test

* Update test

* Fix server address

* Update middleware login_cli

* Update tests

* Return token, reduce poll time, fix timestamp

* feat: web v1 login frontend (experimental)

* fix codeql

* Update constants.ts

---------

Co-authored-by: Juan Picado <juanpicado19@gmail.com>
2025-06-07 09:09:11 +02:00
renovate[bot]
184bd51418 fix(deps): update dependency pino to v9.7.0 (#5260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-07 08:34:18 +02:00
renovate[bot]
837f00dc56 chore(deps): update dependency jsdom to v26 (#5261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-07 08:34:02 +02:00
renovate[bot]
52c1d22c91 chore(deps): update all build dependencies (#5256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-06 22:11:04 +02:00
dependabot[bot]
09503bcd51 chore(deps): bump github/codeql-action from 3.28.16 to 3.28.18 (#5254)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.16 to 3.28.18.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](28deaeda66...ff0a06e83c)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-06 21:12:21 +02:00
silverwind
1e8e6e4212 docs: add link to verdaccio-static-token (#5257) 2025-06-06 18:40:18 +02:00
renovate[bot]
c7285cf933 chore(deps): pin dependencies (#5255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-06 15:36:43 +02:00
verdacciobot
0f2da1897c chore: updated static data 2025-06-05 00:16:05 +00:00
verdacciobot
06132a2ceb chore: updated static data 2025-06-02 00:16:31 +00:00
verdacciobot
688eb15e77 chore: updated static data 2025-05-29 00:16:12 +00:00
Marc Bernard
2fef671547 chore(signature): remove duplicate code (#5244)
* chore(signature): remove duplicate code

* auth
2025-05-28 00:08:39 +02:00
Marc Bernard
d945bfe99d fix(e2e): test.each was not working as expected (#5246)
* fix(e2e): test.each was not working as expected

* fix unpublish

* remove beta

* fix dist-tags
2025-05-26 20:32:37 +02:00
verdacciobot
610ed5cd04 chore: updated static data 2025-05-26 00:16:18 +00:00
Juan Picado
85d8ea16d6 chore: update docker publish action (#5247) 2025-05-22 08:27:09 +02:00
verdacciobot
08ebbce001 chore: updated static data 2025-05-22 00:16:05 +00:00
Marc Bernard
7513899660 chore(tarball): remove lodash, fix typing (#5243) 2025-05-21 22:32:00 +02:00
Marc Bernard
534fafd741 feat(e2e): add npm11 (#5245)
* feat(e2e): add npm11

* add to workflow

* Remove pre-release from versions

* Fix publish test
2025-05-21 21:44:54 +02:00
Marc Bernard
9509b63345 chore(core): http status codes (#5242) 2025-05-20 09:08:07 +02:00
verdacciobot
4d96585991 chore: updated static data 2025-05-19 00:16:24 +00:00
Marc Bernard
0be45da363 chore(web): move utils to web package (#5234)
* chore(types): use "Person" instead of "Author"

* chore(web): move utils to web package

* avoid redos

* fix plugin_prefix

---------

Co-authored-by: Juan Picado <juanpicado19@gmail.com>
2025-05-18 19:29:46 +02:00
Marc Bernard
7751a55a34 fix(local-storage): uncontrolled data used in path expression (#5159)
* fix(loca-storage): uncontrolled data used in path expression

* update tests

* fix path check

* you're right, copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* sure, copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix storage path

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-17 23:08:22 +02:00
Marc Bernard
acb8a99f55 chore(core): move more utils to core (#5233)
* chore(core): move more utils to core

* pin minimatch

* update lock file
2025-05-17 20:58:01 +02:00
Juan Picado
b776e1f897 chore: enable CI Node.js 24 (#5240)
* chore: enable CI Node.js 24

* add e2e

* docker build on 24
2025-05-17 17:30:59 +02:00
Marc Bernard
d33266b5a6 chore(types): use "Person" instead of "Author" (#5232) 2025-05-17 16:11:01 +02:00
Marc Bernard
b19ddca81f fix(config): server settings (#5238) 2025-05-17 16:00:11 +02:00
Juan Picado
f86da50d91 chore: update e2e nginx test 2025-05-17 15:33:50 +02:00
Juan Picado
7e9fa2a5b3 chore: fix nginx example 2025-05-17 12:15:55 +02:00
Marc Bernard
6add153fde feat(loader): support scoped plugins in plugin folder (#5237) 2025-05-17 10:58:41 +02:00
verdacciobot
0052cc5b97 chore: updated static data 2025-05-17 07:14:02 +00:00
Marc Bernard
5b64bb9e3e fix: monthly npm stats (#5236) 2025-05-16 22:32:28 +02:00
verdacciobot
97df95af40 chore: updated static data 2025-05-15 00:15:56 +00:00
verdacciobot
6c0ba0e3a4 chore: updated static data 2025-05-12 00:16:54 +00:00
Saurabh Gohil
3d113dd38d docs(uplinks): updated the documentation for an example of AWS CodeArtifact in uplinks (#5231)
* - Updated the documentation for an example of AWS CodeArtifact uplink on how to make it work

* - Added the changeset

* - Changes as per the things mentioned in PR comments

* - Updated the other document file as well
2025-05-09 15:50:36 +02:00
verdacciobot
c6dc42e92b chore: updated static data 2025-05-08 00:15:42 +00:00
Marc Bernard
ca0844a45a chore(ui): avoid ts errors for CircularProgres (#5210) 2025-05-06 19:53:09 +02:00
Marc Bernard
3ef4a49bba fix: support JWT sign and verify options (#5227)
* fix: support JWT sign and verify options

* Sync type version
2025-05-06 19:21:48 +02:00
Marc Bernard
387d9f01ea fix(ui): check token expiry on refresh/timer (#5229) 2025-05-05 18:02:00 +02:00
DAmarkday
dedc20b24c docs:add a new link for version v6 to the Docker custom image list. (#5228) 2025-05-05 06:39:31 +02:00
verdacciobot
96b6c404a9 chore: updated static data 2025-05-05 00:16:19 +00:00
284 changed files with 10408 additions and 2993 deletions

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/loaders': minor
---
feat(loader): support scoped plugins in plugin folder

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/ui-components': patch
---
fix(ui): check token expiry on refresh/timer

View File

@@ -0,0 +1,18 @@
---
'@verdaccio/local-storage': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/test-helper': patch
'@verdaccio/ui-components': patch
'@verdaccio/tarball': patch
'@verdaccio/types': patch
'@verdaccio/middleware': patch
'verdaccio': patch
'@verdaccio/config': patch
'@verdaccio/proxy': patch
'@verdaccio/store': patch
'@verdaccio/auth': patch
'@verdaccio/api': patch
---
chore(utils): replace @verdaccio/utils dependency with core

View File

@@ -0,0 +1,8 @@
---
'@verdaccio/ui-theme': minor
'@verdaccio/ui-components': minor
'@verdaccio/middleware': patch
'@verdaccio/ui-i18n': patch
---
feat: web v1 login frontend (experimental)

View File

@@ -0,0 +1,6 @@
---
'@verdaccio/store': patch
'@verdaccio/types': patch
---
chore(types): use "Person" instead of "Author"

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/web': patch
---
chore(web): move utils to web package

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/core': patch
---
chore(core): http status codes

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/local-storage': patch
---
fix(local-storage): uncontrolled data used in path expression

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/e2e-cli-npm-common': patch
---
fix(e2e): test.each was not working as expected

View File

@@ -0,0 +1,6 @@
---
'@verdaccio/website': patch
---
- Updated the docs to show how we can make AWS CodeArtifact work with Verdaccio
- Added the example uplinks configuration

View File

@@ -0,0 +1,6 @@
---
'@verdaccio/e2e-cli-npm11': patch
'@verdaccio/e2e-cli-npm-common': patch
---
feat(e2e): add npm11

View File

@@ -0,0 +1,9 @@
---
'@verdaccio/types': patch
'@verdaccio/middleware': patch
'@verdaccio/core': patch
'@verdaccio/config': patch
'@verdaccio/api': patch
---
feat: web v1 login backend (experimental)

View File

@@ -0,0 +1,6 @@
---
'@verdaccio/signature': patch
'@verdaccio/config': patch
---
chore(signature): remove duplicate code

View File

@@ -61,61 +61,81 @@
"@verdaccio/e2e-cli-pnpm10": "1.0.0",
"docusaurus-plugin-downloads": "2.0.0",
"@verdaccio/local-publish": "0.0.2",
"@verdaccio/e2e-cli-npm10": "1.0.1"
"@verdaccio/e2e-cli-npm10": "1.0.1",
"@verdaccio/e2e-cli-npm11": "1.0.2-next-8.0"
},
"changesets": [
"afraid-cars-sneeze",
"afraid-cheetahs-rule",
"angry-doors-tan",
"beige-lions-type",
"blue-paws-cheer",
"brave-ears-drive",
"breezy-geckos-search",
"breezy-toys-judge",
"bright-bobcats-ring",
"brown-lions-talk",
"brown-planets-approve",
"calm-mangos-compare",
"chatty-apricots-report",
"clean-beds-wash",
"clean-rings-listen",
"clever-bees-happen",
"cool-seals-watch",
"curly-mirrors-smile",
"curvy-rockets-camp",
"cyan-snakes-kiss",
"dirty-countries-play",
"early-eyes-float",
"early-trainers-grin",
"eight-countries-think",
"eighty-apes-think",
"eleven-rocks-dream",
"few-beds-itch",
"few-ears-deny",
"fifty-falcons-design",
"forty-hounds-matter",
"four-buttons-remember",
"fresh-owls-hunt",
"funny-fireants-tan",
"gentle-stingrays-repeat",
"gold-files-speak",
"gold-squids-watch",
"great-candles-hang",
"green-eagles-boil",
"healthy-ducks-drive",
"healthy-zoos-lie",
"hip-deers-join",
"hip-eggs-serve",
"hip-suns-jam",
"hot-crews-live",
"hungry-dolls-destroy",
"itchy-glasses-end",
"large-turkeys-change",
"long-eyes-drum",
"long-singers-drive",
"loud-dingos-cough",
"lucky-crabs-enjoy",
"modern-llamas-know",
"nasty-cooks-watch",
"nasty-experts-bow",
"nice-garlics-tie",
"nine-countries-remember",
"nine-onions-talk",
"ninety-geese-do",
"ninety-hotels-dance",
"odd-fishes-cry",
"old-clocks-destroy",
"pink-jeans-lick",
"pink-pants-try",
"poor-hats-smile",
"poor-queens-shop",
"popular-trees-grin",
"proud-houses-switch",
"purple-planes-do",
"quick-avocados-type",
"quick-seas-deny",
"real-balloons-travel",
"real-seahorses-change",
"red-dolls-repair",
"rotten-melons-notice",
@@ -123,20 +143,27 @@
"rude-birds-design",
"rude-socks-walk",
"serious-apes-rule",
"sharp-queens-rhyme",
"shiny-buttons-laugh",
"short-mails-smoke",
"short-turkeys-boil",
"silent-bags-listen",
"silent-tips-admire",
"silver-houses-remain",
"silver-insects-train",
"slow-cars-guess",
"smooth-games-share",
"strange-pants-chew",
"stupid-camels-build",
"sweet-crabs-deliver",
"swift-knives-shop",
"swift-mangos-grab",
"swift-zebras-cheer",
"ten-jeans-approve",
"tender-buckets-smoke",
"thick-dolphins-rule",
"thirty-comics-trade",
"tidy-socks-battle",
"tricky-impalas-shake",
"tricky-knives-end",
"twelve-games-wonder",
@@ -145,6 +172,8 @@
"violet-boxes-float",
"weak-cherries-serve",
"wet-cats-behave",
"wicked-points-relate",
"wild-coins-lie",
"yellow-flies-sniff"
]
}

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/web': patch
---
fix: config web login default

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/ui-components': patch
---
chore(ui): avoid ts errors for CircularProgress

View File

@@ -0,0 +1,6 @@
---
'@verdaccio/core': patch
'@verdaccio/utils': patch
---
chore(core): move more utils to core

View File

@@ -0,0 +1,12 @@
---
'@verdaccio/server': patch
'@verdaccio/core': patch
'@verdaccio/loaders': patch
'@verdaccio/config': patch
'@verdaccio/store': patch
'@verdaccio/auth': patch
'@verdaccio/api': patch
'@verdaccio/web': patch
---
fix(config): server settings

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/core': patch
---
fix(core): remove `lodash` from a dependency

View File

@@ -0,0 +1,7 @@
---
'@verdaccio/auth': patch
'@verdaccio/signature': patch
'@verdaccio/types': patch
---
fix: support JWT sign and verify options

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/tarball': patch
---
chore(tarball): remove lodash, fix typing

View File

@@ -68,7 +68,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest]
node_version: [18, 20, 21, 22, 23]
node_version: [18, 20, 21, 22, 23, 24]
name: ${{ matrix.os }} / Node ${{ matrix.node_version }}
runs-on: ${{ matrix.os }}
steps:

View File

@@ -37,7 +37,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
with:
config: |
paths-ignore:
@@ -50,7 +50,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -64,4 +64,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18

View File

@@ -2,50 +2,49 @@ name: Docker publish to docker.io
on:
push:
paths:
- .github/workflows/docker-publish.yml
- .github/workflows/docker-publish.yml
- 'packages/**'
- 'docker-bin/**'
- 'package.json'
- 'pnpm-*.yaml'
- 'Dockerfile'
- '.dockerignore'
branches:
- 'master'
tags:
- 'v*'
permissions:
contents: read # to fetch code (actions/checkout)
contents: read
jobs:
docker:
runs-on: ubuntu-latest
if: github.repository == 'verdaccio/verdaccio'
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- uses: docker/setup-buildx-action@v1
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host
- uses: docker/login-action@v1
name: Login Docker Hub
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Prepare docker image tags
- name: Prepare Docker image tags
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
with:
images: ${{ github.repository }}
tag-custom: nightly-master
tag-custom-only: ${{ github.ref == 'refs/heads/master' }}
tag-latest: false
tag-semver: |
{{version}}
{{major}}
{{major}}.{{minor}}
tags: |
type=raw,value=nightly-master
type=raw,value=latest,enable=false
labels: |
org.opencontainers.image.source=${{ github.repositoryUrl }}
org.opencontainers.image.created=${{ steps.docker_meta.outputs.created }}
org.opencontainers.image.version=${{ steps.docker_meta.outputs.version }}
- name: Build & Push
uses: docker/build-push-action@v6
with:

View File

@@ -43,9 +43,10 @@ jobs:
npm7,
npm8,
npm9,
npm10
npm10,
npm11,
]
node: [22]
node: [22, 24]
name: ${{ matrix.pkg }}/ ubuntu-latest / ${{ matrix.node }}
runs-on: ubuntu-latest
steps:
@@ -72,7 +73,7 @@ jobs:
pnpm9,
pnpm10,
]
node: [22]
node: [2, 24]
name: ${{ matrix.pkg }}/ ubuntu-latest / ${{ matrix.node }}
runs-on: ubuntu-latest
steps:
@@ -99,7 +100,7 @@ jobs:
yarn3,
yarn4
]
node: [22]
node: [22, 24]
name: ${{ matrix.pkg }}/ ubuntu-latest / ${{ matrix.node }}
runs-on: ubuntu-latest
steps:

View File

@@ -36,7 +36,7 @@ jobs:
- name: Get docker downloads
run: pnpm --filter @verdaccio/local-scripts run pull:docker
- name: Get npmjs monhtly downloads
run: pnpm --filter @verdaccio/local-scripts run downloads:mounthly
run: pnpm --filter @verdaccio/local-scripts run downloads:monthly
- name: Get npmjs year downloads
run: pnpm --filter @verdaccio/local-scripts run downloads:yearly
- name: update contributors

View File

@@ -16,6 +16,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'

View File

@@ -1,4 +1,4 @@
FROM --platform=${BUILDPLATFORM:-linux/amd64} node:22-alpine AS builder
FROM --platform=${BUILDPLATFORM:-linux/amd64} node:24-alpine AS builder
ENV NODE_ENV=development \
VERDACCIO_BUILD_REGISTRY=https://registry.npmjs.org
@@ -21,7 +21,7 @@ RUN npm -g i corepack && \
# NODE_ENV=production pnpm install --frozen-lockfile --ignore-scripts
# RUN pnpm install --prod --ignore-scripts
FROM node:22-alpine
FROM node:24-alpine
LABEL maintainer="https://github.com/verdaccio/verdaccio"
ENV VERDACCIO_APPDIR=/opt/verdaccio \

View File

@@ -100,19 +100,21 @@ Our goal is to give you the confidence to use your preferred package manager wit
### Commands
| cmd | npm6 | npm7 | npm8 | npm9 | npm10 | pnpm8 | pnpm9 | pnpm10 | yarn1 | yarn2 | yarn3 | yarn4 |
| --------- | ---- | ---- | ---- | ---- | ----- | ----- | ----- | ------ | ----- | ----- | ----- | ----- |
| publish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| unpublish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ❌ | ❌ | ❌ | ❌ |
| info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| audit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| install | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| deprecate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| ping | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| star | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| stars | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| dist-tag | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| cmd | npm6 | npm7 | npm8 | npm9 | npm10 | npm11 | pnpm8 | pnpm9 | pnpm10 | yarn1 | yarn2 | yarn3 | yarn4 |
| --------- | ---- | ---- | ---- | ---- | ----- | ----- | ----- | ----- | ------ | ----- | ----- | ----- | ----- |
| publish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| unpublish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| audit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| install | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| deprecate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| ping | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| star | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| stars | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| dist-tag | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
<!-- n6 n7 n8 n9 n10 n11 p8 p9 p10 y1 y2 y3 y4 -->
> notes:
>

View File

@@ -4,16 +4,16 @@
"version": "2.0.0-next-8.0",
"main": "src/index.ts",
"devDependencies": {
"@verdaccio/config": "workspace:8.0.0-next-8.15",
"@verdaccio/core": "workspace:8.0.0-next-8.15",
"@verdaccio/types": "workspace:13.0.0-next-8.5",
"debug": "4.4.0",
"@verdaccio/config": "workspace:8.0.0-next-8.17",
"@verdaccio/core": "workspace:8.0.0-next-8.17",
"@verdaccio/types": "workspace:13.0.0-next-8.6",
"debug": "4.4.1",
"fs-extra": "11.2.0",
"get-port": "5.1.1",
"got": "11.8.6",
"js-yaml": "4.1.0",
"lodash": "4.17.21",
"verdaccio": "workspace:8.0.0-next-8.15"
"verdaccio": "workspace:8.0.0-next-8.17"
},
"scripts": {
"test": "echo no test",

View File

@@ -0,0 +1,9 @@
# @verdaccio/e2e-cli-npm-common
## 1.0.1-next-8.0
### Patch Changes
- d945bfe: fix(e2e): test.each was not working as expected
- 534fafd: feat(e2e): add npm11
- @verdaccio/test-cli-commons@2.0.0-next-8.0

View File

@@ -12,7 +12,7 @@ export function runAudit(npm) {
await registry.init();
});
test.each([['verdaccio-memory', '@verdaccio/cli']])(
test.each([['verdaccio-memory'], ['@verdaccio/cli']])(
'should audit a package %s',
async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(

View File

@@ -17,26 +17,30 @@ export function runDistTag(npm) {
await registry.init();
});
test.each([['@foo/foo', 'foo']])('should list dist-tags for %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
await npmUtils.bumbUp(npm, tempFolder, registry);
await npmUtils.publish(npm, tempFolder, pkgName, registry, ['--tag', 'beta']);
const resp2 = await npm(
{ cwd: tempFolder },
'dist-tag',
'ls',
'--json',
...addRegistry(registry.getRegistryUrl())
);
expect(resp2.stdout).toEqual('beta: 1.1.0latest: 1.0.0');
});
test.each([['@foo/verdaccio-foo'], ['verdaccio-foo']])(
'should list dist-tags for %s',
async (pkgName) => {
// Packages must not exist in npm registry to avoid conflicts with data from uplink
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
await npmUtils.bumbUp(npm, tempFolder, registry);
await npmUtils.publish(npm, tempFolder, pkgName, registry, ['--tag', 'beta']);
const resp2 = await npm(
{ cwd: tempFolder },
'dist-tag',
'ls',
'--json',
...addRegistry(registry.getRegistryUrl())
);
expect(resp2.stdout).toEqual('beta: 1.1.0latest: 1.0.0');
}
);
test.each([['@verdaccio/bar']])('should remove tag with dist-tags for %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@verdaccio/e2e-cli-npm-common",
"version": "1.0.0",
"version": "1.0.1-next-8.0",
"dependencies": {
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0"
},

View File

@@ -12,12 +12,18 @@ export function runPublish(npm) {
await registry.init();
});
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
test.each([['verdaccio-memory'], ['verdaccio'], ['@verdaccio/foo'], ['@verdaccio/some-foo']])(
'should publish a package %s',
async (pkgName) => {
// As of npm v11, npm will fetch the packument from the npm registry before publishing (from the uplink),
// and there are more checks in the client:
// - "You cannot publish over the previously published versions"
// - "You must specify a tag using --tag when publishing a prerelease version"
// - "Cannot implicitly apply the 'latest' tag because previously published version x.y.z is higher than the new version a.b.c"
// Therefore, we pick a version that is higher than any of the published versions for the test packages (and not a pre-release).
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
'99.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
@@ -31,7 +37,6 @@ export function runPublish(npm) {
const parsedBody = JSON.parse(resp.stdout as string);
expect(parsedBody.name).toEqual(pkgName);
expect(parsedBody.files).toBeDefined();
expect(parsedBody.files).toBeDefined();
}
);

View File

@@ -20,7 +20,7 @@ export function runStar(npm) {
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
'1.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
@@ -39,7 +39,7 @@ export function runStar(npm) {
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
'1.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
@@ -67,7 +67,7 @@ export function runStar(npm) {
const pkgName = '@verdaccio/stars';
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
'1.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()

View File

@@ -17,12 +17,12 @@ export function runUnpublish(npm) {
await registry.init();
});
test.each([['@verdaccio/test1', 'super-package-do-not-exist-spam']])(
test.each([['@verdaccio/test1'], ['super-package-do-not-exist-spam']])(
'should unpublish a full package %s',
async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-beta',
'1.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
@@ -44,16 +44,16 @@ export function runUnpublish(npm) {
'--json',
...addRegistry(registry.getRegistryUrl())
);
expect(resp2.stdout).toEqual('- @verdaccio/test1');
expect(resp2.stdout).toEqual(`- ${pkgName}`);
}
);
test.each([['@verdaccio/test1', 'super-package-do-not-exist-spam']])(
test.each([['@verdaccio/test1'], ['super-package-do-not-exist-spam']])(
'should unpublish a package %s version',
async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-beta',
'1.0.0',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
@@ -69,13 +69,13 @@ export function runUnpublish(npm) {
const resp2 = await npm(
{ cwd: tempFolder },
'unpublish',
`${pkgName}@1.0.0-beta`,
`${pkgName}@1.0.0`,
'--force',
'--loglevel=info',
'--json',
...addRegistry(registry.getRegistryUrl())
);
expect(resp2.stdout).toEqual('- @verdaccio/test1@1.0.0-beta');
expect(resp2.stdout).toEqual(`- ${pkgName}@1.0.0`);
}
);

View File

@@ -1,5 +1,14 @@
# @verdaccio/e2e-cli-npm9
## 1.0.2-next-8.1
### Patch Changes
- Updated dependencies [d945bfe]
- Updated dependencies [534fafd]
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
- @verdaccio/test-cli-commons@2.0.0-next-8.0
## 1.0.2-next-8.0
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@verdaccio/e2e-cli-npm10",
"version": "1.0.2-next-8.0",
"version": "1.0.2-next-8.1",
"dependencies": {
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
"@verdaccio/e2e-cli-npm-common": "workspace:*",

View File

@@ -0,0 +1,3 @@
{
"extends": "../../../.babelrc"
}

View File

@@ -0,0 +1,7 @@
{
"rules": {
"no-console": 0,
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/explicit-member-accessibility": 0
}
}

View File

@@ -0,0 +1,79 @@
# @verdaccio/e2e-cli-npm9
## 1.0.2-next-8.1
### Patch Changes
- 534fafd: feat(e2e): add npm11
- Updated dependencies [d945bfe]
- Updated dependencies [534fafd]
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
- @verdaccio/test-cli-commons@2.0.0-next-8.0
## 1.0.2-next-8.0
### Patch Changes
- d4fc827: fix(e2e): name for npm10 test
- @verdaccio/test-cli-commons@2.0.0-next-8.0
## 1.0.1
### Patch Changes
- Updated dependencies [351aeeaa8]
- Updated dependencies [d167f92e1]
- Updated dependencies [c383eb68c]
- @verdaccio/test-cli-commons@1.1.0
## 1.0.1-6-next.7
### Patch Changes
- Updated dependencies [c383eb68]
- @verdaccio/test-cli-commons@1.1.0-6-next.7
## 1.0.1-6-next.6
### Patch Changes
- Updated dependencies [d167f92e]
- @verdaccio/test-cli-commons@1.1.0-6-next.6
## 1.0.1-6-next.5
### Patch Changes
- @verdaccio/test-cli-commons@1.0.1-6-next.5
## 1.0.1-6-next.4
### Patch Changes
- @verdaccio/test-cli-commons@1.0.1-6-next.4
## 1.0.1-6-next.3
### Patch Changes
- 351aeeaa: fix(deps): @verdaccio/utils should be a prod dep of local-storage
- Updated dependencies [351aeeaa]
- @verdaccio/test-cli-commons@1.0.1-6-next.3
## 1.0.1-6-next.2
### Patch Changes
- @verdaccio/test-cli-commons@1.0.1-6-next.2
## 1.0.1-6-next.1
### Patch Changes
- @verdaccio/test-cli-commons@1.0.1-6-next.1
## 1.0.1-6-next.0
### Patch Changes
- @verdaccio/test-cli-commons@1.0.1-6-next.0

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runAudit } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('audit a package', () => {
runAudit(npm);
});

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runDeprecate } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('deprecate a package', () => {
runDeprecate(npm);
});

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runDistTag } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('dist-tags a package', () => {
runDistTag(npm);
});

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runInfo } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('info a package', () => {
runInfo(npm);
});

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runInstall } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('install a project packages', () => {
runInstall(npm);
});

View File

@@ -0,0 +1,13 @@
{
"private": true,
"name": "@verdaccio/e2e-cli-npm11",
"version": "1.0.2-next-8.1",
"dependencies": {
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
"@verdaccio/e2e-cli-npm-common": "workspace:*",
"npm": "11.4.0"
},
"scripts": {
"test": "vitest run --testTimeout 50000"
}
}

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runPing } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('ping registry', () => {
runPing(npm);
});

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runPublish } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('publish a package', () => {
runPublish(npm);
});

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runSearch } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('search a package', () => {
runSearch(npm);
});

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runStar } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('star a package', () => {
runStar(npm);
});

View File

@@ -0,0 +1,8 @@
{
"extends": "../../../tsconfig.reference.json",
"references": [
{
"path": "../cli-commons"
}
]
}

View File

@@ -0,0 +1,9 @@
import { describe } from 'vitest';
import { runUnpublish } from '@verdaccio/e2e-cli-npm-common';
import { npm } from './utils';
describe('unpublish a package', () => {
runUnpublish(npm);
});

View File

@@ -0,0 +1,41 @@
import { SpawnOptions } from 'child_process';
import { join } from 'path';
import { exec } from '@verdaccio/test-cli-commons';
import { addRegistry } from '@verdaccio/test-cli-commons';
export function getCommand() {
return join(__dirname, './node_modules/.bin/npm');
}
export function npm(options: SpawnOptions, ...args: string[]) {
return exec(options, getCommand(), args);
}
export async function bumbUp(tempFolder, registry) {
await npm({ cwd: tempFolder }, 'version', 'minor', ...addRegistry(registry.getRegistryUrl()));
}
export async function publish(tempFolder, pkgName, registry, arg: string[] = []) {
const resp = await npm(
{ cwd: tempFolder },
'publish',
...arg,
'--json',
...addRegistry(registry.getRegistryUrl())
);
const parsedBody = JSON.parse(resp.stdout as string);
expect(parsedBody.name).toEqual(pkgName);
}
export async function getInfoVersions(pkgName, registry) {
const infoResp = await npm(
{},
'info',
pkgName,
'--json',
...addRegistry(registry.getRegistryUrl())
);
const infoBody = JSON.parse(infoResp.stdout as string);
return infoBody;
}

View File

@@ -1,5 +1,14 @@
# @verdaccio/e2e-cli-npm6
## 1.0.2-next-8.1
### Patch Changes
- Updated dependencies [d945bfe]
- Updated dependencies [534fafd]
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
- @verdaccio/test-cli-commons@2.0.0-next-8.0
## 1.0.2-next-8.0
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@verdaccio/e2e-cli-npm6",
"version": "1.0.2-next-8.0",
"version": "1.0.2-next-8.1",
"dependencies": {
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
"@verdaccio/e2e-cli-npm-common": "workspace:*",

View File

@@ -1,5 +1,14 @@
# @verdaccio/e2e-cli-npm7
## 1.0.2-next-8.1
### Patch Changes
- Updated dependencies [d945bfe]
- Updated dependencies [534fafd]
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
- @verdaccio/test-cli-commons@2.0.0-next-8.0
## 1.0.2-next-8.0
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@verdaccio/e2e-cli-npm7",
"version": "1.0.2-next-8.0",
"version": "1.0.2-next-8.1",
"dependencies": {
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
"@verdaccio/e2e-cli-npm-common": "workspace:*",

View File

@@ -1,5 +1,14 @@
# @verdaccio/e2e-cli-npm8
## 1.0.2-next-8.1
### Patch Changes
- Updated dependencies [d945bfe]
- Updated dependencies [534fafd]
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
- @verdaccio/test-cli-commons@2.0.0-next-8.0
## 1.0.2-next-8.0
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@verdaccio/e2e-cli-npm8",
"version": "1.0.2-next-8.0",
"version": "1.0.2-next-8.1",
"dependencies": {
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
"@verdaccio/e2e-cli-npm-common": "workspace:*",

View File

@@ -1,5 +1,14 @@
# @verdaccio/e2e-cli-npm9
## 1.0.2-next-8.1
### Patch Changes
- Updated dependencies [d945bfe]
- Updated dependencies [534fafd]
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
- @verdaccio/test-cli-commons@2.0.0-next-8.0
## 1.0.2-next-8.0
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@verdaccio/e2e-cli-npm9",
"version": "1.0.2-next-8.0",
"version": "1.0.2-next-8.1",
"dependencies": {
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
"@verdaccio/e2e-cli-npm-common": "workspace:*",

View File

@@ -1,3 +0,0 @@
FROM tutum/nginx
RUN rm /etc/nginx/sites-enabled/default
ADD sites-enabled /etc/nginx/sites-enabled

View File

@@ -1,6 +1,17 @@
version: '2'
version: '3.9'
services:
nginx:
build: ./nginx
container_name: reverse_proxy
ports:
- '80:80'
depends_on:
- verdaccio
volumes:
- logs:/var/log/nginx
restart: unless-stopped
verdaccio:
image: verdaccio/verdaccio:nightly-master
container_name: verdaccio_root_path
@@ -8,19 +19,8 @@ services:
- '4873:4873'
volumes:
- verdaccio:/verdaccio
nginx:
restart: always
build: conf/nginx
ports:
- '80:80'
volumes:
- /www/public
volumes_from:
- verdaccio
links:
- verdaccio:verdaccio
restart: unless-stopped
volumes:
verdaccio:
driver: local
logs:

View File

@@ -0,0 +1,8 @@
# Light, maintained NGINX build
FROM nginx:alpine
# Remove the default vhost
RUN rm /etc/nginx/conf.d/default.conf
# Copy our custom server block
COPY verdaccio.conf /etc/nginx/conf.d/verdaccio.conf

View File

@@ -1,7 +1,7 @@
server {
listen 80 default_server;
access_log /var/log/nginx/verdaccio.log;
charset utf-8;
access_log /var/log/nginx/verdaccio.log;
charset utf-8;
location / {
proxy_pass http://verdaccio:4873/;
proxy_set_header Host $host;
@@ -11,4 +11,4 @@ server {
proxy_set_header Host $http_host;
proxy_redirect off;
}
}
}

View File

@@ -3,11 +3,11 @@
"name": "@verdaccio/e2e-ui",
"version": "2.0.0",
"devDependencies": {
"verdaccio": "workspace:8.0.0-next-8.15",
"@verdaccio/core": "workspace:8.0.0-next-8.15",
"@verdaccio/config": "workspace:8.0.0-next-8.15",
"@verdaccio/test-helper": "workspace:4.0.0-next-8.4",
"debug": "4.4.0",
"verdaccio": "workspace:8.0.0-next-8.17",
"@verdaccio/core": "workspace:8.0.0-next-8.17",
"@verdaccio/config": "workspace:8.0.0-next-8.17",
"@verdaccio/test-helper": "workspace:4.0.0-next-8.5",
"debug": "4.4.1",
"cypress": "^13.6.0",
"get-port": "5.1.1"
},

View File

@@ -15,34 +15,34 @@
"url": "https://opencollective.com/verdaccio"
},
"devDependencies": {
"@babel/cli": "7.26.4",
"@babel/core": "7.26.9",
"@babel/cli": "7.27.2",
"@babel/core": "7.27.4",
"@babel/eslint-parser": "7.25.9",
"@babel/node": "7.26.0",
"@babel/plugin-proposal-decorators": "7.25.9",
"@babel/plugin-proposal-function-sent": "7.25.9",
"@babel/plugin-proposal-throw-expressions": "7.25.9",
"@babel/node": "7.27.1",
"@babel/plugin-proposal-decorators": "7.27.1",
"@babel/plugin-proposal-function-sent": "7.27.1",
"@babel/plugin-proposal-throw-expressions": "7.27.1",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/plugin-syntax-import-meta": "7.10.4",
"@babel/plugin-transform-async-to-generator": "7.25.9",
"@babel/plugin-transform-class-properties": "7.25.9",
"@babel/plugin-transform-classes": "7.25.9",
"@babel/plugin-transform-export-namespace-from": "7.25.9",
"@babel/plugin-transform-json-strings": "7.25.9",
"@babel/plugin-transform-nullish-coalescing-operator": "7.26.6",
"@babel/plugin-transform-numeric-separator": "7.25.9",
"@babel/plugin-transform-object-rest-spread": "7.25.9",
"@babel/plugin-transform-optional-chaining": "7.25.9",
"@babel/plugin-transform-runtime": "7.26.9",
"@babel/preset-env": "7.26.9",
"@babel/preset-react": "7.26.3",
"@babel/plugin-transform-async-to-generator": "7.27.1",
"@babel/plugin-transform-class-properties": "7.27.1",
"@babel/plugin-transform-classes": "7.27.1",
"@babel/plugin-transform-export-namespace-from": "7.27.1",
"@babel/plugin-transform-json-strings": "7.27.1",
"@babel/plugin-transform-nullish-coalescing-operator": "7.27.1",
"@babel/plugin-transform-numeric-separator": "7.27.1",
"@babel/plugin-transform-object-rest-spread": "7.27.3",
"@babel/plugin-transform-optional-chaining": "7.27.1",
"@babel/plugin-transform-runtime": "7.27.4",
"@babel/preset-env": "7.27.2",
"@babel/preset-react": "7.27.1",
"@babel/preset-typescript": "7.24.7",
"@babel/register": "7.25.9",
"@babel/runtime": "7.26.9",
"@babel/register": "7.27.1",
"@babel/runtime": "7.27.6",
"@changesets/changelog-github": "0.5.1",
"@changesets/cli": "2.27.12",
"@changesets/get-dependents-graph": "2.1.3",
"@crowdin/cli": "4.4.1",
"@crowdin/cli": "4.7.1",
"@dianmora/contributors": "5.0.0",
"@emotion/react": "11.10.6",
"@emotion/styled": "11.10.6",
@@ -63,6 +63,7 @@
"@types/lodash": "4.17.7",
"@types/mime": "3.0.4",
"@types/minimatch": "5.1.2",
"@types/ms": "2.1.0",
"@types/node": "20.14.12",
"@types/node-fetch": "2.6.11",
"@types/qs": "6.9.15",
@@ -95,7 +96,7 @@
"babel-plugin-emotion": "11.0.0",
"concurrently": "8.2.2",
"cross-env": "7.0.3",
"debug": "4.4.0",
"debug": "4.4.1",
"detect-secrets": "1.0.6",
"eslint": "8.57.1",
"fs-extra": "11.2.0",

View File

@@ -1,5 +1,42 @@
# @verdaccio/api
## 8.1.0-next-8.17
### Patch Changes
- Updated dependencies [96d2f0f]
- @verdaccio/core@8.0.0-next-8.17
- @verdaccio/auth@8.0.0-next-8.17
- @verdaccio/config@8.0.0-next-8.17
- @verdaccio/middleware@8.0.0-next-8.17
- @verdaccio/store@8.0.0-next-8.17
- @verdaccio/logger@8.0.0-next-8.17
## 8.1.0-next-8.16
### Patch Changes
- 72c3cbb: chore(utils): replace @verdaccio/utils dependency with core
- 626ae6a: feat: web v1 login backend (experimental)
- b19ddca: fix(config): server settings
- 5f036c0: fix(api): cidr whitelist for tokens
- Updated dependencies [72c3cbb]
- Updated dependencies [626ae6a]
- Updated dependencies [d33266b]
- Updated dependencies [9509b63]
- Updated dependencies [626ae6a]
- Updated dependencies [2fef671]
- Updated dependencies [acb8a99]
- Updated dependencies [b19ddca]
- Updated dependencies [fdf44a6]
- Updated dependencies [3ef4a49]
- @verdaccio/middleware@8.0.0-next-8.16
- @verdaccio/config@8.0.0-next-8.16
- @verdaccio/store@8.0.0-next-8.16
- @verdaccio/auth@8.0.0-next-8.16
- @verdaccio/core@8.0.0-next-8.16
- @verdaccio/logger@8.0.0-next-8.16
## 8.1.0-next-8.15
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/api",
"version": "8.1.0-next-8.15",
"version": "8.1.0-next-8.17",
"description": "Verdaccio Registry API",
"main": "./build/index.js",
"types": "build/index.d.ts",
@@ -42,25 +42,24 @@
},
"license": "MIT",
"dependencies": {
"@verdaccio/auth": "workspace:8.0.0-next-8.15",
"@verdaccio/config": "workspace:8.0.0-next-8.15",
"@verdaccio/core": "workspace:8.0.0-next-8.15",
"@verdaccio/logger": "workspace:8.0.0-next-8.15",
"@verdaccio/middleware": "workspace:8.0.0-next-8.15",
"@verdaccio/store": "workspace:8.0.0-next-8.15",
"@verdaccio/utils": "workspace:8.1.0-next-8.15",
"@verdaccio/auth": "workspace:8.0.0-next-8.17",
"@verdaccio/config": "workspace:8.0.0-next-8.17",
"@verdaccio/core": "workspace:8.0.0-next-8.17",
"@verdaccio/logger": "workspace:8.0.0-next-8.17",
"@verdaccio/middleware": "workspace:8.0.0-next-8.17",
"@verdaccio/store": "workspace:8.0.0-next-8.17",
"abortcontroller-polyfill": "1.7.8",
"body-parser": "1.20.3",
"cookies": "0.9.1",
"debug": "4.4.0",
"debug": "4.4.1",
"express": "4.21.2",
"lodash": "4.17.21",
"mime": "2.6.0",
"semver": "7.7.1"
"semver": "7.7.2"
},
"devDependencies": {
"@verdaccio/test-helper": "workspace:4.0.0-next-8.4",
"@verdaccio/types": "workspace:13.0.0-next-8.5",
"@verdaccio/test-helper": "workspace:4.0.0-next-8.5",
"@verdaccio/types": "workspace:13.0.0-next-8.6",
"mockdate": "3.0.5",
"supertest": "7.0.0"
},

View File

@@ -20,6 +20,7 @@ import publish from './publish';
import search from './search';
import stars from './stars';
import user from './user';
import login from './v1/login';
import profile from './v1/profile';
import v1Search from './v1/search';
import token from './v1/token';
@@ -71,6 +72,9 @@ export default function (config: Config, auth: Auth, storage: Storage, logger: L
v1Search(app, auth, storage, logger);
token(app, auth, storage, config, logger);
pkg(app, auth, storage, logger);
if (config.flags?.webLogin) {
login(app, auth, storage, config, logger);
}
return app;
}

View File

@@ -9,13 +9,14 @@ import {
API_MESSAGE,
HEADERS,
HTTP_STATUS,
authUtils,
cryptoUtils,
errorUtils,
validationUtils,
} from '@verdaccio/core';
import { USER_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';
import { Logger } from '@verdaccio/types';
import { Config, RemoteUser } from '@verdaccio/types';
import { getAuthenticatedMessage, mask } from '@verdaccio/utils';
import { $NextFunctionVer, $RequestExtend } from '../types/custom';
@@ -39,7 +40,7 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
}
const username = req.params.org_couchdb_user.split(':')[1];
const message = getAuthenticatedMessage(req.remote_user.name);
const message = authUtils.getAuthenticatedMessage(req.remote_user.name);
debug('user authenticated message %o', message);
res.status(HTTP_STATUS.OK);
next({
@@ -107,7 +108,7 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
res.status(HTTP_STATUS.CREATED);
res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
const message = getAuthenticatedMessage(req.remote_user.name);
const message = authUtils.getAuthenticatedMessage(req.remote_user.name);
debug('login: created user message %o', message);
return next({
@@ -119,10 +120,8 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
} else {
debug('adduser: %o', name);
if (
validationUtils.validatePassword(
password,
config?.serverSettings?.passwordValidationRegex
) === false
validationUtils.validatePassword(password, config?.server?.passwordValidationRegex) ===
false
) {
debug('adduser: invalid password');
// eslint-disable-next-line new-cap
@@ -148,7 +147,7 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
? await getApiToken(auth, config, user as RemoteUser, password)
: undefined;
if (token) {
debug('adduser: new token %o', mask(token as string, 4));
debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));
}
if (!token) {
return next(errorUtils.getUnauthorized());

View File

@@ -0,0 +1,167 @@
import buildDebug from 'debug';
import { Response, Router } from 'express';
import { randomUUID } from 'node:crypto';
import { Auth, getApiToken } from '@verdaccio/auth';
import { createRemoteUser } from '@verdaccio/config';
import { API_ERROR, HEADERS, HTTP_STATUS, authUtils, errorUtils } from '@verdaccio/core';
import { LOGIN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';
import { Storage } from '@verdaccio/store';
import { Config, Logger } from '@verdaccio/types';
import { $NextFunctionVer, $RequestExtend } from '../../types/custom';
const debug = buildDebug('verdaccio:api:login');
const WEB_LOGIN_SESSION_ID = 'web-login-sessionId';
// https://github.com/npm/npm-profile/blob/main/lib/index.js
export default function (
route: Router,
auth: Auth,
storage: Storage,
config: Config,
logger: Logger
): void {
route.post(
LOGIN_API_ENDPOINTS.login,
rateLimit(config?.userRateLimit),
async function (req: $RequestExtend, res: Response): Promise<void> {
// create new login session (without token)
const sessionId = randomUUID();
debug('creating login session %o', sessionId);
await storage.saveToken({
user: sessionId,
token: '',
key: WEB_LOGIN_SESSION_ID,
readonly: false,
created: new Date().getTime(),
});
res.status(HTTP_STATUS.OK);
// Assuming Web UI is running on the same address as API
const protocol = req.protocol;
const host = req.hostname;
const port = req.socket.localPort;
const loginUrl = `${protocol}://${host}:${port}/-/web/login?next=${LOGIN_API_ENDPOINTS.login_cli}/${sessionId}`;
const doneUrl = `${protocol}://${host}:${port}${LOGIN_API_ENDPOINTS.login_done}/${sessionId}`;
debug('loginUrl: %o', loginUrl);
debug('doneUrl: %o', doneUrl);
res.json({
loginUrl,
doneUrl,
});
}
);
route.get(
LOGIN_API_ENDPOINTS.login_done_session,
rateLimit(config?.userRateLimit),
async function (req: $RequestExtend, res: Response, next: $NextFunctionVer): Promise<void> {
debug('polling login session %o', req.params.sessionId);
if (!req.params.sessionId) {
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_REQUIRED));
}
const sessionId = req.params.sessionId;
if (sessionId.length !== 36) {
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));
}
try {
const tokens = await storage.readTokens({ user: sessionId });
if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {
if (tokens[0].token.length === 0) {
debug('waiting for authentication');
// Poll again after short delay
// TODO: make this configurable (default 5 seconds)
res.status(HTTP_STATUS.ACCEPTED);
res.set(HEADERS.RETRY_AFTER, '5');
res.json({});
} else {
// session token can only be used once
await storage.deleteToken(sessionId, tokens[0].key);
// Check if token has expired
// TODO: make this configurable (default 2 minutes)
const tokenCreatedDate = new Date(tokens[0].created);
const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);
if (tokenCreatedDate < minutesAgo) {
debug('session token expired');
return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));
}
debug('session token is valid, login successful');
res.status(HTTP_STATUS.OK);
res.json({ token: tokens[0].token });
}
} else {
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));
}
} catch (error: any) {
logger.error({ error: error.msg }, 'token list has failed: @{error}');
return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
}
}
);
route.post(
LOGIN_API_ENDPOINTS.login_cli_session,
rateLimit(config?.userRateLimit),
async function (req: $RequestExtend, res: Response, next: $NextFunctionVer): Promise<void> {
const { username, password } = req.body;
debug('authenticating login session %o for user %o', req.params.sessionId, username);
if (!req.params.sessionId) {
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_REQUIRED));
}
const sessionId = req.params.sessionId;
if (sessionId.length !== 36) {
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));
}
auth.authenticate(
username,
password,
async function callbackAuthenticate(err, user): Promise<void> {
if (err) {
logger.trace(
{ username, err },
'authenticating for user @{username} failed. Error: @{err.message}'
);
return next(errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, err.message));
}
const remoteUser = createRemoteUser(username, user?.groups || []);
const token = await getApiToken(auth, config, remoteUser, password);
if (!token) {
return next(errorUtils.getUnauthorized());
}
// Replace login session with token (to be picked up by the "done" endpoint)
await storage.deleteToken(sessionId, WEB_LOGIN_SESSION_ID);
await storage.saveToken({
user: sessionId,
token,
key: WEB_LOGIN_SESSION_ID,
readonly: false,
created: new Date().getTime(),
});
const message = authUtils.getAuthenticatedMessage(remoteUser.name ?? '');
res.status(HTTP_STATUS.CREATED);
res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
res.json({ ok: message, token });
}
);
}
);
}

View File

@@ -74,7 +74,7 @@ export default function (route: Router, auth: Auth, config: Config): void {
if (
validationUtils.validatePassword(
password.new,
config?.serverSettings?.passwordValidationRegex
config?.server?.passwordValidationRegex
) === false
) {
/* eslint new-cap:off */

View File

@@ -1,15 +1,11 @@
import { Response, Router } from 'express';
import _ from 'lodash';
import { getApiToken } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, errorUtils } from '@verdaccio/core';
import { rateLimit } from '@verdaccio/middleware';
import { TOKEN_API_ENDPOINTS } from '@verdaccio/middleware';
import { Auth, getApiToken } from '@verdaccio/auth';
import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, cryptoUtils, errorUtils } from '@verdaccio/core';
import { TOKEN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';
import { Storage } from '@verdaccio/store';
import { Config, RemoteUser, Token } from '@verdaccio/types';
import { Logger } from '@verdaccio/types';
import { mask, stringToMD5 } from '@verdaccio/utils';
import { Config, Logger, RemoteUser, Token } from '@verdaccio/types';
import { $NextFunctionVer, $RequestExtend } from '../../types/custom';
@@ -94,9 +90,9 @@ export default function (
throw errorUtils.getInternalError();
}
const key = stringToMD5(token);
const key = cryptoUtils.stringToMD5(token);
// TODO: use a utility here
const maskedToken = mask(token, 5);
const maskedToken = cryptoUtils.mask(token, 5);
const created = new Date().getTime();
/**

View File

@@ -5,7 +5,14 @@ import supertest from 'supertest';
import { expect } from 'vitest';
import { parseConfigFile } from '@verdaccio/config';
import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
import {
HEADERS,
HEADER_TYPE,
HTTP_STATUS,
TOKEN_BEARER,
authUtils,
cryptoUtils,
} from '@verdaccio/core';
import { setup } from '@verdaccio/logger';
import { Storage } from '@verdaccio/store';
import {
@@ -13,17 +20,18 @@ import {
initializeServer as initializeServerHelper,
} from '@verdaccio/test-helper';
import { Author, GenericBody, PackageUsers } from '@verdaccio/types';
import { buildToken, generateRandomHexString } from '@verdaccio/utils';
import apiMiddleware from '../../src';
setup({});
export const buildToken = authUtils.buildToken;
export const getConf = (conf) => {
const configPath = path.join(__dirname, 'config', conf);
const config = parseConfigFile(configPath);
// custom config to avoid conflict with other tests
config.auth.htpasswd.file = `${config.auth.htpasswd.file}-${generateRandomHexString()}`;
config.auth.htpasswd.file = `${config.auth.htpasswd.file}-${cryptoUtils.generateRandomHexString()}`;
return config;
};

View File

@@ -0,0 +1,21 @@
storage: ./storage
auth:
htpasswd:
file: ./htpasswd
packages:
'@*/*':
access: $all
publish: $authenticated
unpublish: $authenticated
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
log: { type: stdout, format: pretty, level: debug }
## enable web login for testing
flags:
webLogin: true

View File

@@ -0,0 +1,105 @@
import supertest from 'supertest';
import { describe, expect, test } from 'vitest';
import { HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
import { initializeServer } from './_helper';
import { createUser } from './_helper';
describe('login', () => {
test('should return login and done urls, set session id', async () => {
const app = await initializeServer('login.yaml');
const response = await supertest(app)
.post('/-/v1/login')
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
.send(JSON.stringify({}));
expect(response.status).toBe(HTTP_STATUS.OK);
expect(response.headers['content-type']).toBe(HEADERS.JSON_CHARSET);
expect(response.body).toEqual({
loginUrl: expect.stringContaining('/-/web/login?next=/-/v1/login_cli/'),
doneUrl: expect.stringContaining('/-/v1/done/'),
});
const sessionId = response.body.doneUrl.split('/-/v1/done/')[1];
expect(sessionId.length).toBe(36);
});
test('should authenticate user using session id', async () => {
const username = 'test';
const password = 'password';
const app = await initializeServer('login.yaml');
await createUser(app, username, password);
const response = await supertest(app)
.post('/-/v1/login')
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
.send(JSON.stringify({}));
expect(response.status).toBe(HTTP_STATUS.OK);
const sessionId = response.body.doneUrl.split('/-/v1/done/')[1];
const response2 = await supertest(app)
.post(`/-/v1/login_cli/${sessionId}`)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
.send(
JSON.stringify({
username,
password,
})
);
expect(response2.status).toBe(HTTP_STATUS.CREATED);
expect(response2.headers['content-type']).toBe(HEADERS.JSON_CHARSET);
expect(response2.body).toEqual({
ok: `you are authenticated as '${username}'`,
token: expect.any(String),
});
});
test('should return session token when polled', async () => {
const username = 'test';
const password = 'password';
const app = await initializeServer('login.yaml');
await createUser(app, username, password);
const response = await supertest(app)
.post('/-/v1/login')
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
.send(JSON.stringify({}));
expect(response.status).toBe(HTTP_STATUS.OK);
const sessionId = response.body.doneUrl.split('/-/v1/done/')[1];
const response2 = await supertest(app)
.post(`/-/v1/login_cli/${sessionId}`)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
.send(
JSON.stringify({
username,
password,
})
);
expect(response2.status).toBe(HTTP_STATUS.CREATED);
const response3 = await supertest(app)
.get(`/-/v1/done/${sessionId}`)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
expect(response3.status).toBe(HTTP_STATUS.OK);
expect(response3.headers['content-type']).toBe(HEADERS.JSON_CHARSET);
expect(response3.body).toEqual({
token: expect.any(String),
});
});
});

View File

@@ -2,9 +2,8 @@ import supertest from 'supertest';
import { describe, test } from 'vitest';
import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
import { buildToken } from '@verdaccio/utils';
import { createUser, initializeServer } from './_helper';
import { buildToken, createUser, initializeServer } from './_helper';
describe('profile ', () => {
describe('get profile ', () => {

View File

@@ -10,9 +10,14 @@ import {
SUPPORT_ERRORS,
TOKEN_BEARER,
} from '@verdaccio/core';
import { buildToken } from '@verdaccio/utils';
import { deleteTokenCLI, generateTokenCLI, getNewToken, initializeServer } from './_helper';
import {
buildToken,
deleteTokenCLI,
generateTokenCLI,
getNewToken,
initializeServer,
} from './_helper';
describe('token', () => {
describe('basics', () => {

View File

@@ -2,9 +2,8 @@ import supertest from 'supertest';
import { describe, expect, test, vi } from 'vitest';
import { API_ERROR, HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
import { buildToken } from '@verdaccio/utils';
import { createUser, getPackage, initializeServer } from './_helper';
import { buildToken, createUser, getPackage, initializeServer } from './_helper';
const FORBIDDEN_VUE = 'authorization required to access package vue';

View File

@@ -2,9 +2,8 @@ import supertest from 'supertest';
import { describe, expect, test } from 'vitest';
import { HEADERS, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
import { buildToken } from '@verdaccio/utils';
import { createUser, initializeServer } from './_helper';
import { buildToken, createUser, initializeServer } from './_helper';
describe('whoami', () => {
test('should return the logged username', async () => {

View File

@@ -1,5 +1,38 @@
# @verdaccio/auth
## 8.0.0-next-8.17
### Patch Changes
- Updated dependencies [96d2f0f]
- @verdaccio/core@8.0.0-next-8.17
- @verdaccio/config@8.0.0-next-8.17
- @verdaccio/loaders@8.0.0-next-8.7
- verdaccio-htpasswd@13.0.0-next-8.17
- @verdaccio/signature@8.0.0-next-8.9
## 8.0.0-next-8.16
### Patch Changes
- 72c3cbb: chore(utils): replace @verdaccio/utils dependency with core
- b19ddca: fix(config): server settings
- 3ef4a49: fix: support JWT sign and verify options
- Updated dependencies [6add153]
- Updated dependencies [72c3cbb]
- Updated dependencies [9509b63]
- Updated dependencies [76308ff]
- Updated dependencies [626ae6a]
- Updated dependencies [2fef671]
- Updated dependencies [acb8a99]
- Updated dependencies [b19ddca]
- Updated dependencies [3ef4a49]
- @verdaccio/loaders@8.0.0-next-8.7
- @verdaccio/config@8.0.0-next-8.16
- @verdaccio/core@8.0.0-next-8.16
- @verdaccio/signature@8.0.0-next-8.8
- verdaccio-htpasswd@13.0.0-next-8.16
## 8.0.0-next-8.15
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/auth",
"version": "8.0.0-next-8.15",
"version": "8.0.0-next-8.17",
"description": "Verdaccio Authentication",
"main": "./build/index.js",
"types": "./build/index.d.ts",
@@ -42,19 +42,18 @@
},
"license": "MIT",
"dependencies": {
"@verdaccio/config": "workspace:8.0.0-next-8.15",
"@verdaccio/core": "workspace:8.0.0-next-8.15",
"@verdaccio/loaders": "workspace:8.0.0-next-8.6",
"@verdaccio/signature": "workspace:8.0.0-next-8.7",
"@verdaccio/utils": "workspace:8.1.0-next-8.15",
"debug": "4.4.0",
"@verdaccio/config": "workspace:8.0.0-next-8.17",
"@verdaccio/core": "workspace:8.0.0-next-8.17",
"@verdaccio/loaders": "workspace:8.0.0-next-8.7",
"@verdaccio/signature": "workspace:8.0.0-next-8.9",
"debug": "4.4.1",
"lodash": "4.17.21",
"verdaccio-htpasswd": "workspace:13.0.0-next-8.15"
"verdaccio-htpasswd": "workspace:13.0.0-next-8.17"
},
"devDependencies": {
"@verdaccio/middleware": "workspace:8.0.0-next-8.15",
"@verdaccio/types": "workspace:13.0.0-next-8.5",
"@verdaccio/logger": "workspace:8.0.0-next-8.15",
"@verdaccio/middleware": "workspace:8.0.0-next-8.17",
"@verdaccio/types": "workspace:13.0.0-next-8.6",
"@verdaccio/logger": "workspace:8.0.0-next-8.17",
"express": "4.21.2",
"supertest": "7.0.0"
},

View File

@@ -1,15 +1,17 @@
import buildDebug from 'debug';
import _ from 'lodash';
import _, { isFunction } from 'lodash';
import { HTPasswd } from 'verdaccio-htpasswd';
import { createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';
import { TOKEN_VALID_LENGTH, createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';
import {
API_ERROR,
PLUGIN_CATEGORY,
PLUGIN_PREFIX,
SUPPORT_ERRORS,
TOKEN_BASIC,
TOKEN_BEARER,
VerdaccioError,
authUtils,
errorUtils,
pluginUtils,
warningUtils,
@@ -20,7 +22,6 @@ import {
aesEncryptDeprecated,
parseBasicPayload,
signPayload,
utils as signatureUtils,
} from '@verdaccio/signature';
import {
AllowAccess,
@@ -32,7 +33,6 @@ import {
RemoteUser,
Security,
} from '@verdaccio/types';
import { getMatchedPackagesSpec, isFunction, isNil } from '@verdaccio/utils';
import {
$RequestExtend,
@@ -126,7 +126,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
);
},
this.options.legacyMergeConfigs,
this.config?.serverSettings?.pluginPrefix,
this.config?.server?.pluginPrefix ?? PLUGIN_PREFIX,
PLUGIN_CATEGORY.AUTHENTICATION
);
}
@@ -142,14 +142,14 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
newPassword: string,
cb: Callback
): void {
const validPlugins = _.filter(this.plugins, (plugin) => isFunction(plugin.changePassword));
const validPlugins = _.filter(this.plugins, (plugin) => _.isFunction(plugin.changePassword));
if (_.isEmpty(validPlugins)) {
return cb(errorUtils.getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));
}
for (const plugin of validPlugins) {
if (isNil(plugin) || isFunction(plugin.changePassword) === false) {
if (_.isNil(plugin) || _.isFunction(plugin.changePassword) === false) {
debug('auth plugin does not implement changePassword, trying next one');
continue;
} else {
@@ -278,7 +278,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
const pkg = Object.assign(
{},
pkgAllowAccess,
getMatchedPackagesSpec(packageName, this.config.packages)
authUtils.getMatchedPackagesSpec(packageName, this.config.packages)
) as AllowAccess & PackageAccess;
debug('allow access for %o', packageName);
@@ -312,7 +312,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
): void {
const pkg = Object.assign(
{ name: packageName, version: packageVersion },
getMatchedPackagesSpec(packageName, this.config.packages)
authUtils.getMatchedPackagesSpec(packageName, this.config.packages)
);
debug('allow unpublish for %o', packageName);
@@ -355,7 +355,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
const plugins = this.plugins.slice(0);
const pkg = Object.assign(
{ name: packageName, version: packageVersion },
getMatchedPackagesSpec(packageName, this.config.packages)
authUtils.getMatchedPackagesSpec(packageName, this.config.packages)
);
debug('allow publish for %o init | plugins: %o', packageName, plugins.length);
@@ -557,7 +557,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
let credentials: RemoteUser | undefined;
try {
credentials = verifyJWTPayload(token, this.config.secret);
credentials = verifyJWTPayload(token, this.config.secret, this.config.security);
} catch (err: any) {
// FIXME: intended behaviour, do we want it?
}
@@ -594,7 +594,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
* Encrypt a string.
*/
public aesEncrypt(value: string): string | void {
if (this.secret.length === signatureUtils.TOKEN_VALID_LENGTH) {
if (this.secret.length === TOKEN_VALID_LENGTH) {
debug('signing with enhanced aes legacy');
const token = aesEncrypt(value, this.secret);
return token;

View File

@@ -61,6 +61,6 @@ export function getMiddlewareCredentials(
debug('is jwt');
if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
return verifyJWTPayload(token, secretKey);
return verifyJWTPayload(token, secretKey, security);
}
}

View File

@@ -93,7 +93,7 @@ export function getMiddlewareCredentials(
debug('is jwt');
if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
return verifyJWTPayload(token, secretKey);
return verifyJWTPayload(token, secretKey, security);
}
}
@@ -131,9 +131,9 @@ export async function getApiToken(
export const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError'];
export function verifyJWTPayload(token: string, secret: string): RemoteUser {
export function verifyJWTPayload(token: string, secret: string, security: Security): RemoteUser {
try {
const payload: RemoteUser = verifyPayload(token, secret);
const payload: RemoteUser = verifyPayload(token, secret, security?.api?.jwt?.verify);
return payload;
} catch (error: any) {

View File

@@ -9,16 +9,17 @@ import {
parseConfigFile,
} from '@verdaccio/config';
import { getDefaultConfig } from '@verdaccio/config';
import { TOKEN_BEARER } from '@verdaccio/core';
import { TOKEN_BEARER, authUtils } from '@verdaccio/core';
import { logger, setup } from '@verdaccio/logger';
import { signPayload } from '@verdaccio/signature';
import { Config, RemoteUser, Security } from '@verdaccio/types';
import { buildToken, buildUserBuffer } from '@verdaccio/utils';
import { Auth, getApiToken, getMiddlewareCredentials, verifyJWTPayload } from '../src';
setup({});
const buildToken = authUtils.buildToken;
const parseConfigurationFile = (conf) => {
const { name, ext } = path.parse(conf);
const format = ext.startsWith('.') ? ext.substring(1) : 'yaml';
@@ -37,6 +38,7 @@ describe('Auth utilities', () => {
const conf = parseConfigFile(parseConfigurationSecurityFile(configFileName));
// @ts-ignore
const secConf = _.merge(getDefaultConfig(), conf);
// @ts-expect-error
secConf.secret = secret;
const config: Config = new AppConfig(secConf);
@@ -101,7 +103,7 @@ describe('Auth utilities', () => {
const user = 'test';
const pass = 'test';
// basic authentication need send user as base64
const token = buildUserBuffer(user, pass).toString('base64');
const token = authUtils.buildUserBuffer(user, pass).toString('base64');
const config: Config = getConfig('security-legacy', secret);
const security: Security = config.security;
const credentials = getMiddlewareCredentials(security, secret, `Basic ${token}`);
@@ -171,15 +173,17 @@ describe('Auth utilities', () => {
describe('verifyJWTPayload', () => {
test('should fail on verify the token and return anonymous users', () => {
expect(verifyJWTPayload('fakeToken', 'b2df428b9929d3ace7c598bbf4e496b2')).toEqual(
createAnonymousRemoteUser()
);
const config: Config = getConfig('security-jwt', '12345');
expect(
verifyJWTPayload('fakeToken', 'b2df428b9929d3ace7c598bbf4e496b2', config.security)
).toEqual(createAnonymousRemoteUser());
});
test('should verify the token and return a remote user', async () => {
const remoteUser = createRemoteUser('foo', []);
const token = await signPayload(remoteUser, '12345');
const verifiedToken = verifyJWTPayload(token, '12345');
const config: Config = getConfig('security-jwt', '12345');
const verifiedToken = verifyJWTPayload(token, '12345', config.security);
expect(verifiedToken.groups).toEqual(remoteUser.groups);
expect(verifiedToken.name).toEqual(remoteUser.name);
});

View File

@@ -7,14 +7,19 @@ import {
ROLES,
createAnonymousRemoteUser,
createRemoteUser,
getDefaultConfig,
parseConfigFile,
} from '@verdaccio/config';
import { getDefaultConfig } from '@verdaccio/config';
import { API_ERROR, CHARACTER_ENCODING, VerdaccioError, errorUtils } from '@verdaccio/core';
import {
API_ERROR,
CHARACTER_ENCODING,
VerdaccioError,
authUtils,
errorUtils,
} from '@verdaccio/core';
import { logger, setup } from '@verdaccio/logger';
import { aesDecrypt, verifyPayload } from '@verdaccio/signature';
import { Config, RemoteUser } from '@verdaccio/types';
import { getAuthenticatedMessage } from '@verdaccio/utils';
import {
ActionsAllowed,
@@ -356,7 +361,7 @@ describe('Auth utilities', () => {
describe('getAuthenticatedMessage test', () => {
test('should sign token with jwt enabled', () => {
expect(getAuthenticatedMessage('test')).toBe("you are authenticated as 'test'");
expect(authUtils.getAuthenticatedMessage('test')).toBe("you are authenticated as 'test'");
});
});
});

View File

@@ -4,11 +4,17 @@ import supertest from 'supertest';
import { describe, expect, test, vi } from 'vitest';
import { Config as AppConfig, ROLES, createRemoteUser, getDefaultConfig } from '@verdaccio/config';
import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, TOKEN_BEARER, errorUtils } from '@verdaccio/core';
import {
HEADERS,
HTTP_STATUS,
SUPPORT_ERRORS,
TOKEN_BEARER,
authUtils,
errorUtils,
} from '@verdaccio/core';
import { logger, setup } from '@verdaccio/logger';
import { errorReportingMiddleware, final, handleError } from '@verdaccio/middleware';
import { Config } from '@verdaccio/types';
import { buildToken } from '@verdaccio/utils';
import { $RequestExtend, Auth } from '../src';
import {
@@ -20,10 +26,12 @@ import {
setup({});
const buildToken = authUtils.buildToken;
// to avoid flaky test generate same ramdom key
vi.mock('@verdaccio/utils', async (importOriginal) => {
vi.mock('@verdaccio/core', async (importOriginal) => {
return {
...(await importOriginal<typeof import('@verdaccio/utils')>()),
...(await importOriginal<typeof import('@verdaccio/core')>()),
// used by enhanced legacy aes signature (minimum 32 characters)
generateRandomSecretKey: () => 'GCYW/3IJzQI6GvPmy9sbMkFoiL7QLVw',
// used by legacy aes signature
@@ -572,19 +580,15 @@ describe('AuthTest', () => {
app.use(express.json({ strict: false, limit: '10mb' }));
app.use(auth.apiJWTmiddleware());
// @ts-expect-error
app.use(errorReportingMiddleware(logger));
app.get('/*', (req, res, next) => {
if ((req as $RequestExtend).remote_user.error) {
next(new Error((req as $RequestExtend).remote_user.error));
} else {
// @ts-expect-error
next({ user: req?.remote_user });
}
});
// @ts-expect-error
app.use(handleError(logger));
// @ts-expect-error
app.use(final);
return app;
};
@@ -706,7 +710,7 @@ describe('AuthTest', () => {
await auth.init();
const token = (await auth.jwtEncrypt(
createRemoteUser('jwt_user', [ROLES.ALL]),
config.security.api.jwt.sign
config.security?.api?.jwt?.sign || {}
)) as string;
const app = await getServer(auth);
const res = await supertest(app)

View File

@@ -1,5 +1,30 @@
# @verdaccio/cli
## 8.0.0-next-8.17
### Patch Changes
- Updated dependencies [96d2f0f]
- @verdaccio/core@8.0.0-next-8.17
- @verdaccio/config@8.0.0-next-8.17
- @verdaccio/node-api@8.0.0-next-8.17
- @verdaccio/logger@8.0.0-next-8.17
## 8.0.0-next-8.16
### Patch Changes
- Updated dependencies [72c3cbb]
- Updated dependencies [9509b63]
- Updated dependencies [626ae6a]
- Updated dependencies [2fef671]
- Updated dependencies [acb8a99]
- Updated dependencies [b19ddca]
- @verdaccio/config@8.0.0-next-8.16
- @verdaccio/core@8.0.0-next-8.16
- @verdaccio/node-api@8.0.0-next-8.16
- @verdaccio/logger@8.0.0-next-8.16
## 8.0.0-next-8.15
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/cli",
"version": "8.0.0-next-8.15",
"version": "8.0.0-next-8.17",
"author": {
"name": "Juan Picado",
"email": "juanpicado19@gmail.com"
@@ -47,14 +47,14 @@
"start": "ts-node src/index.ts"
},
"dependencies": {
"@verdaccio/config": "workspace:8.0.0-next-8.15",
"@verdaccio/core": "workspace:8.0.0-next-8.15",
"@verdaccio/logger": "workspace:8.0.0-next-8.15",
"@verdaccio/node-api": "workspace:8.0.0-next-8.15",
"@verdaccio/config": "workspace:8.0.0-next-8.17",
"@verdaccio/core": "workspace:8.0.0-next-8.17",
"@verdaccio/logger": "workspace:8.0.0-next-8.17",
"@verdaccio/node-api": "workspace:8.0.0-next-8.17",
"clipanion": "4.0.0-rc.4",
"envinfo": "7.14.0",
"kleur": "4.1.5",
"semver": "7.7.1"
"semver": "7.7.2"
},
"devDependencies": {
"ts-node": "10.9.2"

View File

@@ -1,5 +1,26 @@
# @verdaccio/config
## 8.0.0-next-8.17
### Patch Changes
- Updated dependencies [96d2f0f]
- @verdaccio/core@8.0.0-next-8.17
## 8.0.0-next-8.16
### Patch Changes
- 72c3cbb: chore(utils): replace @verdaccio/utils dependency with core
- 626ae6a: feat: web v1 login backend (experimental)
- 2fef671: chore(signature): remove duplicate code
- b19ddca: fix(config): server settings
- Updated dependencies [9509b63]
- Updated dependencies [626ae6a]
- Updated dependencies [acb8a99]
- Updated dependencies [b19ddca]
- @verdaccio/core@8.0.0-next-8.16
## 8.0.0-next-8.15
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/config",
"version": "8.0.0-next-8.15",
"version": "8.0.0-next-8.17",
"description": "Verdaccio Configuration",
"main": "./build/index.js",
"types": "build/index.d.ts",
@@ -42,9 +42,8 @@
"build": "pnpm run build:js && pnpm run build:types"
},
"dependencies": {
"@verdaccio/core": "workspace:8.0.0-next-8.15",
"@verdaccio/utils": "workspace:8.1.0-next-8.15",
"debug": "4.4.0",
"@verdaccio/core": "workspace:8.0.0-next-8.17",
"debug": "4.4.1",
"js-yaml": "4.1.0",
"lodash": "4.17.21",
"minimatch": "7.4.6"

View File

@@ -2,7 +2,7 @@ import assert from 'assert';
import buildDebug from 'debug';
import _ from 'lodash';
import { APP_ERROR, validationUtils, warningUtils } from '@verdaccio/core';
import { APP_ERROR, authUtils, cryptoUtils, validationUtils, warningUtils } from '@verdaccio/core';
import { Codes } from '@verdaccio/core/build/warning-utils';
import {
Config as AppConfig,
@@ -15,12 +15,11 @@ import {
Security,
ServerSettingsConf,
} from '@verdaccio/types';
import { generateRandomHexString, getMatchedPackagesSpec } from '@verdaccio/utils';
import { getUserAgent } from './agent';
import { normalisePackageAccess } from './package-access';
import { defaultSecurity } from './security';
import serverSettings from './serverSettings';
import defaultServerSettings from './serverSettings';
import { generateRandomSecretKey } from './token';
import { sanityCheckUplinksProps, uplinkSanityCheck } from './uplinks';
@@ -63,7 +62,7 @@ class Config implements AppConfig {
public plugins: string | void | null;
public security: Security;
public serverSettings: ServerSettingsConf;
public server: ServerSettingsConf;
private configOverrideOptions: { forceMigrateToSecureLegacySignature: boolean };
// @ts-ignore
public secret: string;
@@ -102,10 +101,11 @@ class Config implements AppConfig {
}),
config.security
);
this.serverSettings = serverSettings;
this.server = { ...defaultServerSettings, ...config.server };
this.flags = {
searchRemote: config.flags?.searchRemote ?? true,
changePassword: config.flags?.changePassword ?? false,
webLogin: config.flags?.webLogin ?? false,
};
this.user_agent = config.user_agent;
@@ -148,7 +148,7 @@ class Config implements AppConfig {
// unique identifier of self server (or a cluster), used to avoid loops
// @ts-ignore
if (!this.server_id) {
this.server_id = generateRandomHexString(6);
this.server_id = cryptoUtils.generateRandomHexString(6);
}
}
@@ -162,10 +162,13 @@ class Config implements AppConfig {
/**
* Check for package spec
* @param pkgName - package name
* @returns package access
* @deprecated use core.authUtils instead
*/
public getMatchedPackagesSpec(pkgName: string): PackageAccess | void {
// TODO: remove this method and replace by library utils
return getMatchedPackagesSpec(pkgName, this.packages);
return authUtils.getMatchedPackagesSpec(pkgName, this.packages);
}
/**

View File

@@ -1,11 +1,9 @@
import { randomBytes } from 'crypto';
// TODO: code duplicated at @verdaccio/signature
export const TOKEN_VALID_LENGTH = 32;
/**
* Secret key must have 32 characters.
* // TODO: code duplicated at @verdaccio/signature
*/
export function generateRandomSecretKey(): string {
return randomBytes(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);

View File

@@ -1,8 +1,8 @@
import assert from 'assert';
import _ from 'lodash';
import { authUtils } from '@verdaccio/core';
import { PackageList, UpLinksConfList } from '@verdaccio/types';
import { getMatchedPackagesSpec } from '@verdaccio/utils';
export const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
export const DEFAULT_UPLINK = 'npmjs';
@@ -49,7 +49,7 @@ export function sanityCheckUplinksProps(configUpLinks: UpLinksConfList): UpLinks
}
export function getProxiesForPackage(pkg: string, packages: PackageList): string[] {
const matchedPkg = getMatchedPackagesSpec(pkg, packages);
const matchedPkg = authUtils.getMatchedPackagesSpec(pkg, packages);
return matchedPkg?.proxy || [];
}

View File

@@ -1,5 +1,20 @@
# @verdaccio/core
## 8.0.0-next-8.17
### Patch Changes
- 96d2f0f: fix(core): remove `lodash` from a dependency
## 8.0.0-next-8.16
### Patch Changes
- 9509b63: chore(core): http status codes
- 626ae6a: feat: web v1 login backend (experimental)
- acb8a99: chore(core): move more utils to core
- b19ddca: fix(config): server settings
## 8.0.0-next-8.15
## 8.0.0-next-8.14

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/core",
"version": "8.0.0-next-8.15",
"version": "8.0.0-next-8.17",
"description": "Verdaccio Core Components",
"keywords": [
"private",
@@ -37,12 +37,12 @@
"core-js": "3.40.0",
"http-errors": "2.0.0",
"http-status-codes": "2.3.0",
"minimatch": "10.0.1",
"process-warning": "1.0.0",
"semver": "7.7.1"
"semver": "7.7.2"
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.5",
"lodash": "4.17.21",
"@verdaccio/types": "workspace:13.0.0-next-8.6",
"typedoc": "0.23.25",
"typedoc-plugin-missing-exports": "latest"
},

Some files were not shown because too many files have changed in this diff Show More