cnpmcore
Version:
269 lines • 18.9 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);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserController = void 0;
const tegg_1 = require("@eggjs/tegg");
const egg_errors_1 = require("egg-errors");
const typebox_1 = require("@sinclair/typebox");
const AbstractController_1 = require("./AbstractController");
const User_1 = require("../../common/enum/User");
const UserUtil_1 = require("../../common/UserUtil");
const Token_1 = require("../../core/entity/Token");
// body: {
// _id: 'org.couchdb.user:dddd',
// name: 'dddd',
// password: '***',
// type: 'user',
// roles: [],
// date: '2021-12-03T13:14:21.712Z'
// }
// create user will contains email
// {
// _id: 'org.couchdb.user:awldj',
// name: 'awldj',
// password: '***',
// email: 'ddd@dawd.com',
// type: 'user',
// roles: [],
// date: '2021-12-03T13:46:30.644Z'
// }
const UserRule = typebox_1.Type.Object({
type: typebox_1.Type.Literal('user'),
// date: Type.String({ format: 'date-time' }),
name: typebox_1.Type.String({ minLength: 1, maxLength: 100 }),
// https://docs.npmjs.com/policies/security#password-policies
// Passwords should contain alpha-numeric characters and symbols.
// Passwords should be a minimum of 8 characters.
password: typebox_1.Type.String({ minLength: 8, maxLength: 100 }),
email: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'email' })),
});
let UserController = class UserController extends AbstractController_1.AbstractController {
// https://github.com/npm/npm-profile/blob/main/lib/index.js#L127
async loginOrCreateUser(ctx, username, user) {
// headers: {
// 'user-agent': 'npm/8.1.2 node/v16.13.1 darwin arm64 workspaces/false',
// 'npm-command': 'adduser',
// 'content-type': 'application/json',
// accept: '*/*',
// 'content-length': '124',
// 'accept-encoding': 'gzip,deflate',
// host: 'localhost:7001',
// connection: 'keep-alive'
// }
// console.log(username, user, ctx.headers, ctx.href);
ctx.tValidate(UserRule, user);
if (username !== user.name) {
throw new egg_errors_1.UnprocessableEntityError(`username(${username}) not match user.name(${user.name})`);
}
if (this.config.cnpmcore.allowPublicRegistration === false) {
if (!this.config.cnpmcore.admins[user.name]) {
throw new egg_errors_1.ForbiddenError('Public registration is not allowed');
}
}
const result = await this.userService.login(user.name, user.password);
// user exists and password not match
if (result.code === User_1.LoginResultCode.Fail) {
throw new egg_errors_1.UnauthorizedError('Please check your login name and password');
}
if (result.code === User_1.LoginResultCode.Success) {
// login success
// TODO: 2FA feature
ctx.status = 201;
return {
ok: true,
id: `org.couchdb.user:${result.user?.displayName}`,
rev: result.user?.userId,
token: result.token?.token,
};
}
// others: LoginResultCode.UserNotFound
// 1. login request
if (!user.email) {
// user not exists
throw new egg_errors_1.NotFoundError(`User ${user.name} not exists`);
}
// 2. create user request
const { user: userEntity, token } = await this.userService.create({
name: user.name,
password: user.password,
email: user.email,
ip: ctx.ip,
});
ctx.status = 201;
return {
ok: true,
id: `org.couchdb.user:${userEntity.displayName}`,
rev: userEntity.userId,
token: token.token,
};
}
// https://github.com/npm/cli/blob/latest/lib/commands/logout.js#L24
async logout(ctx, token) {
const authorizedUserAndToken = await this.userRoleManager.getAuthorizedUserAndToken(ctx);
if (!authorizedUserAndToken)
return { ok: false };
if (authorizedUserAndToken.token.tokenKey !== (0, UserUtil_1.sha512)(token)) {
throw new egg_errors_1.UnprocessableEntityError('invalid token');
}
await this.userService.removeToken(authorizedUserAndToken.user.userId, token);
return { ok: true };
}
// https://github.com/npm/cli/blob/latest/lib/commands/owner.js#L154
async showUser(ctx, username) {
const user = await this.userService.findUserByNameOrDisplayName(username);
if (!user) {
throw new egg_errors_1.NotFoundError(`User "${username}" not found`);
}
const authorized = await this.userRoleManager.getAuthorizedUserAndToken(ctx);
return {
_id: `org.couchdb.user:${user.displayName}`,
name: user.displayName,
email: authorized ? user.email : undefined,
};
}
// https://github.com/npm/cli/blob/latest/lib/utils/get-identity.js#L20
async whoami(ctx) {
await this.userRoleManager.requiredAuthorizedUser(ctx, 'read');
const authorizedRes = await this.userRoleManager.getAuthorizedUserAndToken(ctx);
const { token, user } = authorizedRes;
if ((0, Token_1.isGranularToken)(token)) {
const { name, description, expiredAt, allowedPackages, allowedScopes, lastUsedAt, type } = token;
return {
username: user.displayName,
name,
description,
allowedPackages,
allowedScopes,
lastUsedAt,
expiredAt,
// do not return token value
// token: token.token,
key: token.tokenKey,
cidr_whitelist: token.cidrWhitelist,
readonly: token.isReadonly,
created: token.createdAt,
updated: token.updatedAt,
type,
};
}
return {
username: user.displayName,
};
}
// https://github.com/cnpm/cnpmcore/issues/64
async starredByUser() {
throw new egg_errors_1.ForbiddenError('npm stars is not allowed');
}
// https://github.com/cnpm/cnpmcore/issues/64
async showProfile(ctx) {
const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'read');
return {
// "tfa": {
// "pending": false,
// "mode": "auth-only"
// },
name: authorizedUser.displayName,
email: authorizedUser.email,
email_verified: false,
created: authorizedUser.createdAt,
updated: authorizedUser.updatedAt,
// fullname: authorizedUser.name,
// twitter: '',
// github: '',
};
}
// https://github.com/cnpm/cnpmcore/issues/64
async saveProfile() {
// Valid properties are: email, password, fullname, homepage, freenode, twitter, github
// { email: 'admin@cnpmjs.org', homepage: 'fengmk2' }
throw new egg_errors_1.ForbiddenError('npm profile set is not allowed');
}
};
exports.UserController = UserController;
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/user/org.couchdb.user::username',
method: tegg_1.HTTPMethodEnum.PUT,
}),
__param(0, (0, tegg_1.Context)()),
__param(1, (0, tegg_1.HTTPParam)()),
__param(2, (0, tegg_1.HTTPBody)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, Object]),
__metadata("design:returntype", Promise)
], UserController.prototype, "loginOrCreateUser", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/user/token/:token',
method: tegg_1.HTTPMethodEnum.DELETE,
}),
__param(0, (0, tegg_1.Context)()),
__param(1, (0, tegg_1.HTTPParam)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], UserController.prototype, "logout", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/user/org.couchdb.user::username',
method: tegg_1.HTTPMethodEnum.GET,
}),
__param(0, (0, tegg_1.Context)()),
__param(1, (0, tegg_1.HTTPParam)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], UserController.prototype, "showUser", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/whoami',
method: tegg_1.HTTPMethodEnum.GET,
}),
__param(0, (0, tegg_1.Context)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], UserController.prototype, "whoami", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/_view/starredByUser',
method: tegg_1.HTTPMethodEnum.GET,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], UserController.prototype, "starredByUser", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/user',
method: tegg_1.HTTPMethodEnum.GET,
}),
__param(0, (0, tegg_1.Context)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], UserController.prototype, "showProfile", null);
__decorate([
(0, tegg_1.HTTPMethod)({
path: '/-/npm/v1/user',
method: tegg_1.HTTPMethodEnum.POST,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], UserController.prototype, "saveProfile", null);
exports.UserController = UserController = __decorate([
(0, tegg_1.HTTPController)()
], UserController);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVXNlckNvbnRyb2xsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hcHAvcG9ydC9jb250cm9sbGVyL1VzZXJDb250cm9sbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHNDQVFxQjtBQUNyQiwyQ0FLb0I7QUFDcEIsK0NBQWlEO0FBQ2pELDZEQUEwRDtBQUMxRCxpREFBeUQ7QUFDekQsb0RBQStDO0FBQy9DLG1EQUEwRDtBQUUxRCxVQUFVO0FBQ1Ysa0NBQWtDO0FBQ2xDLGtCQUFrQjtBQUNsQixxQkFBcUI7QUFDckIsa0JBQWtCO0FBQ2xCLGVBQWU7QUFDZixxQ0FBcUM7QUFDckMsSUFBSTtBQUNKLGtDQUFrQztBQUNsQyxJQUFJO0FBQ0osbUNBQW1DO0FBQ25DLG1CQUFtQjtBQUNuQixxQkFBcUI7QUFDckIsMkJBQTJCO0FBQzNCLGtCQUFrQjtBQUNsQixlQUFlO0FBQ2YscUNBQXFDO0FBQ3JDLElBQUk7QUFDSixNQUFNLFFBQVEsR0FBRyxjQUFJLENBQUMsTUFBTSxDQUFDO0lBQzNCLElBQUksRUFBRSxjQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztJQUMxQiw4Q0FBOEM7SUFDOUMsSUFBSSxFQUFFLGNBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQztJQUNuRCw2REFBNkQ7SUFDN0QsaUVBQWlFO0lBQ2pFLGlEQUFpRDtJQUNqRCxRQUFRLEVBQUUsY0FBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBQ3ZELEtBQUssRUFBRSxjQUFJLENBQUMsUUFBUSxDQUFDLGNBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztDQUN2RCxDQUFDLENBQUM7QUFJSSxJQUFNLGNBQWMsR0FB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