UNPKG

@iotize/tap

Version:

IoTize Device client for Javascript

236 lines 17.9 kB
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()); }); }; import '@iotize/tap/service/impl/group'; import { isCodeError } from '@iotize/common/error'; import { TapError } from '@iotize/tap'; import { ResultCode } from '@iotize/tap/client/api'; import { BehaviorSubject } from 'rxjs'; import { BasicAuth } from './basic-auth'; import { INITIAL_SESSION_STATE } from './config'; import { debug } from './debug'; import { ScramAuth } from './scram-auth'; import { TapAuthError } from './tap-auth-error'; const TAG = 'TapAuth'; export class TapAuth { constructor(tap) { this.tap = tap; this._sessionState = new BehaviorSubject(INITIAL_SESSION_STATE); this._sessionData = new BehaviorSubject({}); } /** * Get current session state snapshot */ get sessionStateSnapshot() { return this._sessionState.value; } /** * Listen to session state changed */ get sessionState() { return this._sessionState.asObservable(); } /** * Get current session data snapshot */ get sessionDataSnapshot() { return this._sessionData.value; } /** * Listen to session data changed */ get sessionData() { return this._sessionData.asObservable(); } get service() { return this.tap.service; } setAuthMethod(method) { this._auth = method; } /** * Clear auth cache. * Usefull for example if Tap configuration has changed */ clearCache() { this._auth = undefined; } // async test() { // const lockInfo = await this.getLockInfo(); // if (userPassword) { // if (currentUserId === undefined) { // currentUserId = (await this.service.interface.getCurrentGroupId()).body(); // } // const currentUsername = (await this.tap.service.group.getName( // currentUserId! // )).body()!; // await this.login(currentUsername, userPassword, false); // } // } login(params, options) { return __awaiter(this, void 0, void 0, function* () { debug(TAG, `Tap login as ${params.username}...`); const auth = yield this.setupTapAuthEngineIfRequired(); try { const sessionData = yield auth.login(params); if (sessionData && sessionData.key) { this.tap.encryption.sessionKey = sessionData.key; this.tap.encryption.resume(); } this._sessionData.next(sessionData); if (!(options === null || options === void 0 ? void 0 : options.noRefreshSessionState)) { yield this.refreshSessionState({ username: params.username, }); } const event = { type: 'tap-login', }; this.tap.notifyEvent(event); return sessionData; } catch (err) { if (isCodeError(TapError.Code.ResponseStatusError, err)) { if (err.response.status === ResultCode.NOT_ACCEPTABLE || err.response.status === ResultCode.UNAUTHORIZED) { throw TapAuthError.invalidCredentialsError(params, err); } else if (err.response.status === ResultCode.SERVICE_UNAVAILABLE) { throw TapAuthError.tooManyLoginFailedError(params, err); } } throw TapAuthError.tapRequestError(params, err); } }); } /** * Tap logout * Reject if logout failed */ logout() { return __awaiter(this, void 0, void 0, function* () { debug(TAG, `Tap logout...`); const auth = yield this.setupTapAuthEngineIfRequired(); yield auth.logout(); // we keep session keys even after logout this._sessionState.next(INITIAL_SESSION_STATE); this._sessionData.next({}); const event = { type: 'tap-logout', }; this.tap.notifyEvent(event); }); } /** * Read session state from the Tap * This will perform a few tap requests * * TODO we should invalidate encryption key if session state changed */ refreshSessionState(defaults) { return __awaiter(this, void 0, void 0, function* () { debug(TAG, '[TAP]', `Refreshing session state (current=${this.sessionStateSnapshot.name})...`); const [groupIdResponse, profileIdResponse] = yield this.service.interface.executeMultipleCalls([ this.service.interface.getCurrentGroupIdCall(), this.service.interface.getCurrentProfileIdCall(), ]); const groupId = groupIdResponse.body(); const profileId = profileIdResponse.body(); if (groupId !== this.sessionStateSnapshot.groupId) { const currentSessionState = Object.assign({}, this.sessionStateSnapshot); currentSessionState.groupId = groupId; currentSessionState.profileId = profileId; const [sessionLifetimeResponse, userNameResponse, profileNameResponse] = yield this.service.interface.executeMultipleCalls([ this.service.group.getSessionLifetimeCall(groupId), this.service.group.getNameCall(groupId), this.service.group.getNameCall(profileId), ]); currentSessionState.name = (defaults === null || defaults === void 0 ? void 0 : defaults.username) || userNameResponse.body(); currentSessionState.profileName = profileNameResponse.body(); currentSessionState.lifeTime = sessionLifetimeResponse.body(); debug(TAG, '[TAP]', `Notifying new session state: ${currentSessionState.name}`); this._sessionState.next(currentSessionState); } else { debug(TAG, '[TAP]', `Session state did not changed: ${this.sessionStateSnapshot.name} (id=${groupId})`); } return this.sessionStateSnapshot; }); } // private _isLoggedIn(): boolean { // return ( // this.sessionStateSnapshot != undefined && // this.sessionStateSnapshot.name !== // INITIAL_SESSION_STATE.name // ); // } /** * Change password for given user. If userIdOrName is not set, it will change password for the currenty * connected user. * @param newPassword * @param userIdOrUndefined user identifier for which password will be change */ changePassword(newPassword, userIdOrUndefined) { return __awaiter(this, void 0, void 0, function* () { // When tap will be able to change password by username, we will be able to pass directly the username instead of the userId let userId; // let username: string; if (userIdOrUndefined === undefined) { userId = (yield this.service.interface.getCurrentGroupId()).body(); // username = (await this.service.group.getName(userId)).body(); } else { // TODO for now we cannot get the user id from the username // if (typeof userIdOrName === "string") { // username = userIdOrName; // userId = (await this.service.group.getUserIdFromName(username)).body(); // } // else { userId = userIdOrUndefined; // username = (await this.service.group.getName(userId)).body(); // } } const auth = yield this.setupTapAuthEngineIfRequired(); yield auth.changePassword(newPassword, userId); const event = { type: 'tap-user-password-change', user: { id: userId, // username, }, }; this.tap.notifyEvent(event); }); } setupTapAuthEngineIfRequired() { return __awaiter(this, void 0, void 0, function* () { if (!this._auth) { this._auth = yield this.setupTapAuthEngine(); } return this._auth; }); } setupTapAuthEngine() { return __awaiter(this, void 0, void 0, function* () { const securityOptions = (yield this.service.interface.getSecurityOptions()).body(); debug(TAG, `securityOptions ${JSON.stringify(securityOptions)}`); if (securityOptions.scramActivated) { debug(TAG, 'Setup with ScramAuth'); return new ScramAuth(this.tap); } else { debug(TAG, 'Setup with BasicAuth'); return new BasicAuth(this.tap, { hashPassword: securityOptions.hashPassword, }); } }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFwLWF1dGguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9hdXRoL3NyYy9saWIvdGFwLWF1dGgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUEsT0FBTyxnQ0FBZ0MsQ0FBQztBQUV4QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbkQsT0FBTyxFQUFPLFFBQVEsRUFBMEIsTUFBTSxhQUFhLENBQUM7QUFDcEUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxlQUFlLEVBQWMsTUFBTSxNQUFNLENBQUM7QUFFbkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN6QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDakQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQVFoQyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVoRCxNQUFNLEdBQUcsR0FBRyxTQUFTLENBQUM7QUEyQnRCLE1BQU0sT0FBTyxPQUFPO0lBeUNsQixZQUFtQixHQUFRO1FBQVIsUUFBRyxHQUFILEdBQUcsQ0FBSztRQXRDakIsa0JBQWEsR0FBRyxJQUFJLGVBQWUsQ0FDM0MscUJBQXFCLENBQ3RCLENBQUM7UUFDUSxpQkFBWSxHQUFHLElBQUksZUFBZSxDQUFzQixFQUFFLENBQUMsQ0FBQztJQW1DeEMsQ0FBQztJQWpDL0I7O09BRUc7SUFDSCxJQUFXLG9CQUFvQjtRQUM3QixPQUFPLElBQUksQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDO0lBQ2xDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsWUFBWTtRQUNyQixPQUFPLElBQUksQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLENBQUM7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