cnpmcore
Version:
188 lines • 16.5 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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserService = void 0;
const crypto_1 = __importDefault(require("crypto"));
const tegg_1 = require("@eggjs/tegg");
const egg_errors_1 = require("egg-errors");
const UserRepository_1 = require("../../repository/UserRepository");
const User_1 = require("../entity/User");
const Token_1 = require("../entity/Token");
const WebauthnCredential_1 = require("../entity/WebauthnCredential");
const User_2 = require("../../common/enum/User");
const UserUtil_1 = require("../../common/UserUtil");
const AbstractService_1 = require("../../common/AbstractService");
const RegistryManagerService_1 = require("./RegistryManagerService");
const PackageUtil_1 = require("../../common/PackageUtil");
let UserService = class UserService extends AbstractService_1.AbstractService {
checkPassword(user, password) {
const plain = `${user.passwordSalt}${password}`;
return (0, UserUtil_1.checkIntegrity)(plain, user.passwordIntegrity);
}
async findUserByNameOrDisplayName(name) {
const hasPrefix = name.includes(':');
if (hasPrefix) {
return await this.findUserByName(name);
}
const selfRegistry = await this.registryManagerService.ensureSelfRegistry();
const selfUser = await this.findUserByName((0, PackageUtil_1.getPrefixedName)(selfRegistry.userPrefix, name));
if (selfUser) {
return selfUser;
}
const defaultRegistry = await this.registryManagerService.ensureDefaultRegistry();
const defaultUser = await this.findUserByName((0, PackageUtil_1.getPrefixedName)(defaultRegistry.userPrefix, name));
return defaultUser;
}
async findInRegistry(registry, name) {
return await this.findUserByName((0, PackageUtil_1.getPrefixedName)(registry.userPrefix, name));
}
async findUserByName(name) {
return await this.userRepository.findUserByName(name);
}
async login(name, password) {
const user = await this.userRepository.findUserByName(name);
if (!user)
return { code: User_2.LoginResultCode.UserNotFound };
if (!this.checkPassword(user, password)) {
return { code: User_2.LoginResultCode.Fail };
}
const token = await this.createToken(user.userId);
return { code: User_2.LoginResultCode.Success, user, token };
}
async findOrCreateUser({ name, email, ip, password = crypto_1.default.randomUUID() }) {
let user = await this.userRepository.findUserByName(name);
if (!user) {
const createRes = await this.create({
name,
email,
password,
ip,
});
user = createRes.user;
}
return user;
}
async ensureTokenByUser(opts) {
const user = await this.findOrCreateUser(opts);
const token = await this.createToken(user.userId);
return { user, token };
}
async create(createUser) {
const passwordSalt = crypto_1.default.randomBytes(30).toString('hex');
const plain = `${passwordSalt}${createUser.password}`;
const passwordIntegrity = (0, UserUtil_1.integrity)(plain);
const userEntity = User_1.User.create({
name: createUser.name,
email: createUser.email,
ip: createUser.ip,
passwordSalt,
passwordIntegrity,
isPrivate: true,
});
await this.userRepository.saveUser(userEntity);
const token = await this.createToken(userEntity.userId);
return { user: userEntity, token };
}
async saveUser(userPrefix = 'npm:', name, email) {
const storeName = name.startsWith('name:') ? name : `${userPrefix}${name}`;
let user = await this.userRepository.findUserByName(storeName);
if (!user) {
const passwordSalt = crypto_1.default.randomBytes(20).toString('hex');
const passwordIntegrity = (0, UserUtil_1.integrity)(passwordSalt);
user = User_1.User.create({
name: storeName,
email,
ip: '',
passwordSalt,
passwordIntegrity,
isPrivate: false,
});
await this.userRepository.saveUser(user);
return { changed: true, user };
}
if (user.email === email) {
// skip
return { changed: false, user };
}
user.email = email;
await this.userRepository.saveUser(user);
return { changed: true, user };
}
async createToken(userId, options = {}) {
// https://github.blog/2021-09-23-announcing-npms-new-access-token-format/
// https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats/
// https://github.blog/changelog/2022-12-06-limit-scope-of-npm-tokens-with-the-new-granular-access-tokens/
const token = (0, UserUtil_1.randomToken)(this.config.cnpmcore.name);
const tokenKey = (0, UserUtil_1.sha512)(token);
const tokenMark = token.substring(0, token.indexOf('_') + 4);
const tokenEntity = Token_1.Token.create({
tokenKey,
tokenMark,
userId,
...options,
});
await this.userRepository.saveToken(tokenEntity);
tokenEntity.token = token;
return tokenEntity;
}
async removeToken(userId, tokenKeyOrTokenValue) {
let token = await this.userRepository.findTokenByTokenKey(tokenKeyOrTokenValue);
if (!token) {
// tokenKeyOrTokenValue is token value, sha512 and find again
token = await this.userRepository.findTokenByTokenKey((0, UserUtil_1.sha512)(tokenKeyOrTokenValue));
}
if (!token) {
throw new egg_errors_1.NotFoundError(`Token "${tokenKeyOrTokenValue}" not exists`);
}
if (token.userId !== userId) {
throw new egg_errors_1.ForbiddenError(`Not authorized to remove token "${tokenKeyOrTokenValue}"`);
}
await this.userRepository.removeToken(token.tokenId);
}
async findWebauthnCredential(userId, browserType) {
const credential = await this.userRepository.findCredentialByUserIdAndBrowserType(userId, browserType || null);
return credential;
}
async createWebauthnCredential(userId, options) {
const credentialEntity = WebauthnCredential_1.WebauthnCredential.create({
userId: userId,
credentialId: options.credentialId,
publicKey: options.publicKey,
browserType: options.browserType,
});
await this.userRepository.saveCredential(credentialEntity);
return credentialEntity;
}
async removeWebauthnCredential(userId, browserType) {
const credential = await this.userRepository.findCredentialByUserIdAndBrowserType(userId, browserType || null);
if (credential) {
await this.userRepository.removeCredential(credential.wancId);
}
}
};
exports.UserService = UserService;
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", UserRepository_1.UserRepository)
], UserService.prototype, "userRepository", void 0);
__decorate([
(0, tegg_1.Inject)(),
__metadata("design:type", RegistryManagerService_1.RegistryManagerService)
], UserService.prototype, "registryManagerService", void 0);
exports.UserService = UserService = __decorate([
(0, tegg_1.SingletonProto)({
accessLevel: tegg_1.AccessLevel.PUBLIC,
})
], UserService);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVXNlclNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hcHAvY29yZS9zZXJ2aWNlL1VzZXJTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUFBLG9EQUE0QjtBQUM1QixzQ0FJcUI7QUFDckIsMkNBQTJEO0FBQzNELG9FQUFpRTtBQUNqRSx5Q0FBb0Q7QUFDcEQsMkNBQWtFO0FBQ2xFLHFFQUE4RjtBQUM5RixpREFBeUQ7QUFDekQsb0RBQXVGO0FBQ3ZGLGtFQUErRDtBQUMvRCxxRUFBa0U7QUFDbEUsMERBQTJEO0FBOENwRCxJQUFNLFdBQVcsR0FBakIsTUFBTSxXQUFZLFNBQVEsaUNBQWU7SUFNOUMsYUFBYSxDQUFDLElBQWdCLEVBQUUsUUFBZ0I7UUFDOUMsTUFBTSxLQUFLLEdBQUcsR0FBRyxJQUFJLENBQUMsWUFBWSxHQUFHLFFBQVEsRUFBRSxDQUFDO1FBQ2hELE9BQU8sSUFBQSx5QkFBYyxFQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRUQsS0FBSyxDQUFDLDJCQUEyQixDQUFDLElBQVk7UUFDNUMsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNyQyxJQUFJLFNBQVMsRUFBRTtZQUNiLE9BQU8sTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQ3hDO1FBRUQsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsc0JBQXNCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUM1RSxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBQSw2QkFBZSxFQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUMzRixJQUFJLFFBQVEsRUFBRTtZQUNaLE9BQU8sUUFBUSxDQUFDO1NBQ2pCO1FBRUQs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