cnpmcore
Version:
190 lines • 15.7 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserRoleManager = void 0;
const tegg_1 = require("@eggjs/tegg");
const egg_errors_1 = require("egg-errors");
const PackageRepository_1 = require("../repository/PackageRepository");
const PackageUtil_1 = require("../common/PackageUtil");
const RegistryManagerService_1 = require("../core/service/RegistryManagerService");
const TokenService_1 = require("../core/service/TokenService");
let UserRoleManager = class UserRoleManager {
constructor() {
this.handleAuthorized = false;
}
// check publish access
// 1. admin has all access
// 2. has published in current registry
// 3. pkg scope is allowed to publish
// use AbstractController#ensurePublishAccess ensure pkg exists;
async checkPublishAccess(ctx, fullname) {
const user = await this.requiredAuthorizedUser(ctx, 'publish');
// 1. admin has all access
const isAdmin = await this.isAdmin(ctx);
if (isAdmin) {
return user;
}
// 2. check for checkGranularTokenAccess
const authorizedUserAndToken = await this.getAuthorizedUserAndToken(ctx);
const { token } = authorizedUserAndToken;
await this.tokenService.checkGranularTokenAccess(token, fullname);
// 3. has published in current registry
const [scope, name] = (0, PackageUtil_1.getScopeAndName)(fullname);
const pkg = await this.packageRepository.findPackage(scope, name);
const selfRegistry = await this.registryManagerService.ensureSelfRegistry();
const inSelfRegistry = pkg?.registryId === selfRegistry.registryId;
if (inSelfRegistry) {
// 3.1 check in Maintainers table
// Higher priority than scope check
await this.requiredPackageMaintainer(pkg, user);
return user;
}
if (pkg && !scope && !inSelfRegistry) {
// 3.2 public package can't publish in other registry
// scope package can be migrated into self registry
throw new egg_errors_1.ForbiddenError(`Can\'t modify npm public package "${fullname}"`);
}
// 4 check scope is allowed to publish
await this.requiredPackageScope(scope, user);
if (pkg) {
// published scoped package
await this.requiredPackageMaintainer(pkg, user);
}
return user;
}
// {
// 'user-agent': 'npm/8.1.2 node/v16.13.1 darwin arm64 workspaces/false',
// 'npm-command': 'adduser',
// authorization: 'Bearer 379f84d8-ba98-480b-909e-a8260af3a3ee',
// 'content-type': 'application/json',
// accept: '*/*',
// 'content-length': '166',
// 'accept-encoding': 'gzip,deflate',
// host: 'localhost:7001',
// connection: 'keep-alive'
// }
async getAuthorizedUserAndToken(ctx) {
if (this.handleAuthorized) {
if (!this.currentAuthorizedUser)
return null;
return {
token: this.currentAuthorizedToken,
user: this.currentAuthorizedUser,
};
}
this.handleAuthorized = true;
const authorization = ctx.get('authorization');
if (!authorization)
return null;
const authorizedUserAndToken = await this.tokenService.getUserAndToken(authorization);
if (!authorizedUserAndToken) {
return null;
}
// check token expired & set lastUsedAt
await this.tokenService.checkTokenStatus(authorizedUserAndToken.token);
this.currentAuthorizedToken = authorizedUserAndToken.token;
this.currentAuthorizedUser = authorizedUserAndToken.user;
ctx.userId = authorizedUserAndToken.user.userId;
return authorizedUserAndToken;
}
async requiredAuthorizedUser(ctx, role) {
const authorizedUserAndToken = await this.getAuthorizedUserAndToken(ctx);
if (!authorizedUserAndToken) {
const authorization = ctx.get('authorization');
const message = authorization ? 'Invalid token' : 'Login first';
throw new egg_errors_1.UnauthorizedError(message);
}
const { user, token } = authorizedUserAndToken;
// only enable npm client and version check setting will go into this condition
if (this.config.cnpmcore.enableNpmClientAndVersionCheck && role === 'publish') {
if (token.isReadonly) {
throw new egg_errors_1.ForbiddenError(`Read-only Token "${token.tokenMark}" can't publish`);
}
// only support npm >= 7.0.0 allow publish action
// user-agent: "npm/6.14.12 node/v10.24.1 darwin x64"
const m = /\bnpm\/(\d{1,5})\./.exec(ctx.get('user-agent'));
if (!m) {
throw new egg_errors_1.ForbiddenError('Only allow npm client to access');
}
const major = parseInt(m[1]);
if (major < 7) {
throw new egg_errors_1.ForbiddenError('Only allow npm@>=7.0.0 client to access');
}
}
if (role === 'setting') {
if (token.isReadonly) {
throw new egg_errors_1.ForbiddenError(`Read-only Token "${token.tokenMark}" can't setting`);
}
if (token.isAutomation) {
throw new egg_errors_1.ForbiddenError(`Automation Token "${token.tokenMark}" can't setting`);
}
}
return user;
}
async requiredPackageMaintainer(pkg, user) {
const maintainers = await this.packageRepository.listPackageMaintainers(pkg.packageId);
const maintainer = maintainers.find(m => m.userId === user.userId);
if (!maintainer) {
const names = maintainers.map(m => m.name).join(', ');
throw new egg_errors_1.ForbiddenError(`"${user.name}" not authorized to modify ${pkg.fullname}, please contact maintainers: "${names}"`);
}
}
async requiredPackageScope(scope, user) {
const cnpmcoreConfig = this.config.cnpmcore;
if (cnpmcoreConfig.allowPublishNonScopePackage) {
return;
}
const allowScopes = user.scopes ?? cnpmcoreConfig.allowScopes;
if (!scope) {
throw new egg_errors_1.ForbiddenError(`Package scope required, legal scopes: "${allowScopes.join(', ')}"`);
}
if (!allowScopes.includes(scope)) {
throw new egg_errors_1.ForbiddenError(`Scope "${scope}" not match legal scopes: "${allowScopes.join(', ')}"`);
}
}
async isAdmin(ctx) {
const authorizedUserAndToken = await this.getAuthorizedUserAndToken(ctx);
if (!authorizedUserAndToken)
return false;
const { user, token } = authorizedUserAndToken;
if (token.isReadonly)
return false;
return user.name in this.config.cnpmcore.admins;
}
};
exports.UserRoleManager = UserRoleManager;
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", PackageRepository_1.PackageRepository)
], UserRoleManager.prototype, "packageRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", Object)
], UserRoleManager.prototype, "config", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", Object)
], UserRoleManager.prototype, "logger", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", RegistryManagerService_1.RegistryManagerService)
], UserRoleManager.prototype, "registryManagerService", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", TokenService_1.TokenService)
], UserRoleManager.prototype, "tokenService", void 0);
exports.UserRoleManager = UserRoleManager = __decorate([
(0, tegg_1.ContextProto)({
// only inject on port module
accessLevel: tegg_1.AccessLevel.PRIVATE,
})
], UserRoleManager);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVXNlclJvbGVNYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vYXBwL3BvcnQvVXNlclJvbGVNYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLHNDQUtxQjtBQUVyQiwyQ0FBK0Q7QUFDL0QsdUVBQW9FO0FBSXBFLHVEQUF3RDtBQUN4RCxtRkFBZ0Y7QUFDaEYsK0RBQTREO0FBU3JELElBQU0sZUFBZSxHQUFyQixNQUFNLGVBQWU7SUFBckI7UUFZRyxxQkFBZ0IsR0FBRyxLQUFLLENBQUM7SUF5Sm5DLENBQUM7SUFySkMsdUJBQXVCO0lBQ3ZCLDBCQUEwQjtJQUMxQix1Q0FBdUM7SUFDdkMscUNBQXFDO0lBQ3JDLGdFQUFnRTtJQUN6RCxLQUFLLENBQUMsa0JBQWtCLENBQUMsR0FBZSxFQUFFLFFBQWdCO1FBRS9ELE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsQ0FBQztRQUUvRCwwQkFBMEI7UUFDMUIsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3hDLElBQUksT0FBTyxFQUFFO1lBQ1gsT0FBTyxJQUFJLENBQUM7U0FDYjtRQUVELHdDQUF3QztRQUN4QyxNQUFNLHNCQUFzQixHQUFHLE1BQU0sSUFBSSxDQUFDLHlCQUF5QixDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3pFLE1BQU0sRUFBRSxLQUFLLEVBQUUsR0FBRyxzQkFBdUIsQ0FBQztRQUMxQyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsd0JBQXdCLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBRWxFLHVDQUF1QztRQUN2QyxNQUFNLENBQUUsS0FBSyxFQUFFLElBQUksQ0FBRSxHQUFHLElBQUEsNkJBQWUsRUFBQyxRQUFRLENBQUMsQ0FBQztRQUNsRCxNQUFNLEdBQUcsR0FBRyxNQUFNLElB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