UNPKG

homebridge-config-ui-x

Version:

A web based management, configuration and control platform for Homebridge

256 lines • 10.6 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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs-extra"); const crypto = require("crypto"); const jwt = require("jsonwebtoken"); const jwt_1 = require("@nestjs/jwt"); const common_1 = require("@nestjs/common"); const config_service_1 = require("../config/config.service"); const logger_service_1 = require("../logger/logger.service"); const websockets_1 = require("@nestjs/websockets"); let AuthService = class AuthService { constructor(jwtService, configService, logger) { this.jwtService = jwtService; this.configService = configService; this.logger = logger; this.setupAuthFile(); } authenticate(username, password) { return __awaiter(this, void 0, void 0, function* () { try { const user = yield this.doLogin(username, password); if (user) { return { username: user.username, name: user.name, admin: user.admin, instanceId: this.configService.instanceId, }; } } catch (e) { this.logger.warn(`Failed login attempt`); this.logger.warn(`If you've forgotten your password you can reset to the default ` + `of admin/admin by deleting the "auth.json" file (${this.configService.authPath}) and then restarting Homebridge.`); throw new common_1.ForbiddenException(); } }); } signIn(username, password) { return __awaiter(this, void 0, void 0, function* () { const user = yield this.authenticate(username, password); const token = yield this.jwtService.sign(user); return { access_token: token, token_type: 'Bearer', expires_in: this.configService.ui.sessionTimeout, }; }); } generateNoAuthToken() { return __awaiter(this, void 0, void 0, function* () { if (this.configService.ui.auth !== 'none') { throw new common_1.UnauthorizedException(); } const users = yield this.getUsers(); const user = users.find(x => x.admin === true); const token = yield this.jwtService.sign({ username: user.username, name: user.name, admin: user.admin, instanceId: this.configService.instanceId, }); return { access_token: token, token_type: 'Bearer', expires_in: this.configService.ui.sessionTimeout, }; }); } validateUser(payload) { return __awaiter(this, void 0, void 0, function* () { return payload; }); } verifyWsConnection(client) { return __awaiter(this, void 0, void 0, function* () { try { return jwt.verify(client.handshake.query.token, this.configService.secrets.secretKey); } catch (e) { client.disconnect(); throw new websockets_1.WsException('Unauthorized'); } }); } getUsers() { return __awaiter(this, void 0, void 0, function* () { const users = yield fs.readJson(this.configService.authPath); return users; }); } findById(id) { return __awaiter(this, void 0, void 0, function* () { const users = yield this.getUsers(); const user = users.find(x => x.id === id); return user; }); } findByUsername(username) { return __awaiter(this, void 0, void 0, function* () { const users = yield this.getUsers(); const user = users.find(x => x.username === username); return user; }); } saveUserFile(users) { return __awaiter(this, void 0, void 0, function* () { return yield fs.writeJson(this.configService.authPath, users, { spaces: 4 }); }); } hashPassword(password, salt) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { crypto.pbkdf2(password, salt, 1000, 64, 'sha512', (err, derivedKey) => { if (err) return reject(err); return resolve(derivedKey.toString('hex')); }); }); }); } genSalt() { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { crypto.randomBytes(32, (err, buf) => { if (err) return reject(err); return resolve(buf.toString('hex')); }); }); }); } doLogin(username, password) { return __awaiter(this, void 0, void 0, function* () { const user = yield this.findByUsername(username); if (!user) { throw new common_1.ForbiddenException(); } const hashedPassword = yield this.hashPassword(password, user.salt); if (hashedPassword === user.hashedPassword) { return user; } else { throw new common_1.ForbiddenException(); } }); } addUser(user) { return __awaiter(this, void 0, void 0, function* () { const authfile = yield this.getUsers(); const salt = yield this.genSalt(); const newUser = { id: authfile.length ? Math.max.apply(Math, authfile.map(x => x.id)) + 1 : 1, username: user.username, name: user.name, hashedPassword: yield this.hashPassword(user.password, salt), salt, admin: user.admin, }; authfile.push(newUser); yield this.saveUserFile(authfile); this.logger.warn(`Added new user: ${user.username}`); }); } deleteUser(id) { return __awaiter(this, void 0, void 0, function* () { const authfile = yield this.getUsers(); const index = authfile.findIndex(x => x.id === parseInt(id, 10)); if (index < 0) { throw new common_1.BadRequestException('User Not Found'); } if (authfile[index].admin && authfile.filter(x => x.admin === true).length < 2) { throw new common_1.BadRequestException('Cannot delete only admin user'); } authfile.splice(index, 1); yield this.saveUserFile(authfile); this.logger.warn(`Deleted user with ID ${id}`); }); } updateUser(id, update) { return __awaiter(this, void 0, void 0, function* () { const authfile = yield this.getUsers(); const user = authfile.find(x => x.id === parseInt(id, 10)); if (!user) { throw new common_1.BadRequestException('User Not Found'); } user.name = update.name || user.name; user.admin = (update.admin === undefined) ? user.admin : update.admin; if (update.password) { const salt = yield this.genSalt(); user.hashedPassword = yield this.hashPassword(update.password, salt); user.salt = salt; } this.saveUserFile(authfile); this.logger.log(`Updated user: ${user.username}`); }); } setupDefaultUser() { return __awaiter(this, void 0, void 0, function* () { yield this.addUser({ username: 'admin', password: 'admin', name: 'Administrator', admin: true, }); this.logger.log(`Username and password have been set to default:`); this.logger.log('Username: admin'); this.logger.log('Password: admin'); }); } setupAuthFile() { return __awaiter(this, void 0, void 0, function* () { if (!(yield fs.pathExists(this.configService.authPath))) { yield fs.writeJson(this.configService.authPath, []); } let authfile; try { authfile = yield this.getUsers(); } catch (e) { this.logger.error(`Failed to read auth.json file at ${this.configService.authPath}.`); yield fs.writeJson(this.configService.authPath, []); authfile = yield this.getUsers(); } if (!authfile.find(x => x.admin === true || x.username === 'admin')) { yield this.setupDefaultUser(); } }); } }; AuthService = __decorate([ common_1.Injectable(), __metadata("design:paramtypes", [jwt_1.JwtService, config_service_1.ConfigService, logger_service_1.Logger]) ], AuthService); exports.AuthService = AuthService; //# sourceMappingURL=auth.service.js.map