@iotize/tap
Version:
IoTize Device client for Javascript
236 lines • 17.9 kB
JavaScript
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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