UNPKG

cnpmcore

Version:
269 lines 18.9 kB
"use strict"; 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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