Overview
Similar to my other post, I am using Content Selectors to provide filtered access for Company A to Company B’s hosted repositories. While in the previous post, I was able to get Maven builds to work properly, I am now running into an issue with NPM builds. When attempting to install an artifact, it results in a 403 error (forbidden). The Content Selector provides read+browse access to Company A users to the Company B’s companyB-npm-hosted
repository and, for the time being, provides access to everything in that repository. The only solution to get this working is to circumvent the Content Selector altogether and provide nx-repository-view-npm-companyB-npm-hosted-read
privilege (not ideal).
The error:
403 Forbidden - GET https://my.company.com/repository/companyB-npm-hosted/@companyB%2fnpm-dep-1
In most cases, you or one of your dependencies are requesting
a package version that is forbidden by your security policy, or
on a server you do not have access to.
Desired Result
When using a Content Selector to expose only certain artifacts in a repository, using npm install @scoped/artifact
should complete successfully assuming the dependencies of that @scoped/artifact
are also provided either in the Content Selector or from a proxy repository which the user can see.
Repositories
I’ll provide a general overview of the current layout of the repositories and the artifact dependencies so that the problem can be better understood.
Firstly, the repositories in question. Company A has the following NPM repositories:
companyA-npm-group
- companyA-npm-hosted
- global-npm-proxy (shared amongst all companies)
Company B has the following NPM repositories:
companyB-npm-group
- companyB-npm-hosted
- global-npm-proxy (shared amongst all companies)
Content
Considering this example, I’ll provide the contents of Company B’s companyB-npm-hosted
repository and the dependencies in question. I’ll also provide where these dependencies live. Finally, I’ll provide the RBAC privileges/roles applied to Company A users to provide the full picture.
Firstly, the contents of Company B’s companyB-npm-hosted
repository:
@companyB
- npm-dep-1
- { versions of npm-dep-1 }
- npm-dep-2
- { versions of npm-dep-2 }
- npm-dep-3
- { versions of npm-dep-3 }
The artifact that is being installed is npm-dep-1
which has a dependency graph as follows:
@angular/common@12.2.0 (exists in `global-npm-proxy`)
@angular/core@12.2.0 (exists in `global-npm-proxy`)
@companyB/npm-dep-2
@companyB/npm-dep-3
Now for the RBAC privileges and roles in question. Company A users have the following RBAC setup:
companyA-role:
- nx-component-upload
- nx-search-read
- companyA-read-npm
- companyA-write-npm
- companyA-read-npm-companyB
companyA-read-npm:
- nx-repository-view-npm-companyA-npm-group-read
- nx-repository-view-npm-companyA-npm-group-browse
companyA-write-npm:
- nx-repository-view-npm-companyA-npm-hosted-*
companyA-read-npm-companyB:
- read-companyB-public-deps (this is the Content Selector privilege)
The Content Selector read-companyB-public-deps
has the following expression:
(format == "npm") and
(path =~ "/|/@companyB/|/@companyB/npm-dep-1/.*" or
path =~ "/|/@companyB/|/@companyB/npm-dep-2/.*" or
path =~ "/|/@companyB/|/@companyB/npm-dep-3/.*")
The Content Selector is applied to the companyB-npm-hosted
repository and provides read+browse actions.
As you can see, I’ve provided the path to all of the dependencies within companyB-npm-hosted
via the Content Selector. However, when I attempt to install @companyB/npm-dep-1
the error mentioned earlier continues to prevail. As mentioned, I was able to “alleviate” this problem by completely circumventing the Content Selector by giving all Company A users the ability to read from companyB-npm-hosted
entirely via the nx-repository-view-npm-companyB-npm-hosted-read
privilege. Because this alternative privilege fixes the issue, this leads me to believe that the issue lies with resolving the dependency tree during installation. Company A users can see the companyB-npm-hosted
repository and its contents. However, during installation it is failing to pull the dependencies of @companyB/npm-dep-1
which includes other artifacts from the companyB-npm-hosted
repository. Are Content Selectors applied for each and every attempt to pull from a repository in Nexus? It seems to me that the Content Selector is not being applied when attempting to resolve dependencies of a particular artifact.
The log that outlines the resolution of npm install @companyB/npm-dep-1
is as follows:
silly fetch manifest @companyB/npm-dep-1@*
http fetch GET 403 https://my.company.com/repository/companyB-npm-hosted/@companyB%2fnpm-dep-1 239ms (cache skip)
silly placeDep ROOT @companyB/npm-dep-1@ OK for: want: *
timing idealTree:#root Completed in 247ms
timing idealTree:node_modules/@companyB/npm-dep-1 Completed in 0ms
timing idealTree:buildDeps Completed in 247ms
timing idealTree:fixDepFlags Completed in 0ms
timing idealTree Completed in 252ms
timing command:install Completed in 255ms
verbose stack HttpErrorGeneral: 403 Forbidden - GET https://my.company.com/repository/companyB-npm-hosted/@companyB%2fnpm-dep-1
verbose stack at /opt/homebrew/lib/node_modules/npm/node_modules/npm-registry-fetch/lib/check-response.js:93:15
verbose stack at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
verbose stack at async [nodeFromEdge] (/opt/homebrew/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js:1101:19)
verbose stack at async [buildDepStep] (/opt/homebrew/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js:970:11)
verbose stack at async Arborist.buildIdealTree (/opt/homebrew/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js:216:7)
verbose stack at async Promise.all (index 1)
verbose stack at async Arborist.reify (/opt/homebrew/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js:153:5)
verbose stack at async Install.exec (/opt/homebrew/lib/node_modules/npm/lib/commands/install.js:159:5)
verbose stack at async module.exports (/opt/homebrew/lib/node_modules/npm/lib/cli.js:78:5)
verbose statusCode 403
verbose pkgid @companyB/npm-dep-1@*
verbose cwd /Users/{ my user directory }
verbose Darwin 21.4.0
verbose node v18.0.0
verbose npm v8.6.0
error code E403
error 403 403 Forbidden - GET https://my.company.com/repository/companyB-npm-hosted/@companyB%2fnpm-dep-1
error 403 In most cases, you or one of your dependencies are requesting
error 403 a package version that is forbidden by your security policy, or
error 403 on a server you do not have access to.
The .npmrc
file used to install this artifact is as follows:
registry=https://my.company.com/repository/companyA-npm-group
@companyB:registry=https://my.company.com/repository/companyB-npm-hosted
//my.company.com/repository/companyA-npm-group:_auth={ user:pass base64 encoded }
//my.company.com/repository/companyB-npm-hosted:_auth={ user:pass base64 encoded }
The user:password that is encoded in this .npmrc
file can see the contents of the companyB-npm-hosted
repository thanks to the Content Selector. I’m making sure that NPM knows about both repositories so that the proxy artifacts (like @angular/core
) can be discovered through the global-npm-proxy
that is part of each Company’s group repository.
Any help would be much appreciated. Thank you.