Advertisement
Guest User

Untitled

a guest
Jun 27th, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.97 KB | None | 0 0
  1. import 'rxjs/add/operator/map';
  2. import 'rxjs/add/operator/toPromise';
  3. import {Injectable} from '@angular/core';
  4. import {UserAccountInfo} from "../../models/UserAccountInfo";
  5. import {CryptoService} from "../crypto/crypto";
  6. import {str2Uint8Array, uint8Array2Str} from "../../utils/StringUtils";
  7. import {PredefinedUserType} from "../../app/setup/models/InitialSetupModel";
  8. import {GlobalSettingsProvider} from "../global-settings/global-settings-provider";
  9. import {isNullOrUndefined} from "util";
  10. import {Storage} from "@ionic/storage"
  11. import {LogHandler, LogLevel} from "../../handlers/log-handler";
  12. import {LoggableEntity} from "../../models/interfaces/LoggableEntity";
  13.  
  14. const USER_SECURE_STORAGE_NAME = 'fp-user-secure-storage';
  15. const NULL_DATA_FOR_KEY = "NULL_DATA_FOR_KEY";
  16.  
  17. export const USER_ACCOUNT_SUFFIX = '.user.account';
  18. export const SAMPLE_USER_ACCOUNT_SUFFIX = '.samp.account';
  19. export const SAMPLE_USER_ACCOUNT_PASSWORD = 'qwer';
  20.  
  21. /**
  22. * -- User --
  23. * Singleton provider that represents the access point of the FinancialProfile information
  24. */
  25. @Injectable()
  26. export class User implements LoggableEntity {
  27.  
  28. TAG: string = User.name;
  29. currentUser: UserAccountInfo;
  30. encryptionKey; //FIXME remove
  31.  
  32. constructor(protected storage: Storage,
  33. protected logger: LogHandler,
  34. protected cryptoService: CryptoService,
  35. protected globalSettingsProvider: GlobalSettingsProvider) {
  36. this.logger.log("Running " + this.TAG + " constructor");
  37. if (this.logger.level >= LogLevel.LOG) {
  38. this.storage.keys().then((keys) => this.logger.log('Available keys', keys));
  39. }
  40. }
  41.  
  42. /**
  43. * Method used to check if the user is logged in or not
  44. * @returns {boolean} true if the user is logged in, false if not
  45. */
  46. public hasCurrentUser(): boolean {
  47. return this.currentUser != null;
  48. }
  49.  
  50. /**
  51. * Method used to the save the current user on the persistence layer
  52. * @param {string} suffix optional param
  53. * @returns {Promise<boolean>} True/False depending on the result of the save
  54. */
  55. public saveCurrentUser(suffix: string = USER_ACCOUNT_SUFFIX): Promise<boolean> {
  56. this.logger.log("Saving Current User", this.currentUser.userId);
  57. return new Promise((resolve, reject) => {
  58. this.convertUserId(this.currentUser.userId).then(userId => {
  59. let accountKey = this.calculateAccountKey(userId, suffix);
  60. this.encrypt(this.currentUser, this.encryptionKey).then(encryptedAccount => {
  61. this.storage.get(accountKey).then((data) => {
  62. if (data) {
  63. this.storage.set(accountKey, encryptedAccount).then(() => {
  64. resolve(true);
  65. }).catch(reason => {
  66. this.logger.error(`Unable to save current user`, reason);
  67. reject(false);
  68. });
  69. }
  70.  
  71. else {
  72. this.handleUnableToFindUser(NULL_DATA_FOR_KEY, reject);
  73. }
  74. }
  75. );
  76. });
  77. });
  78. });
  79. }
  80.  
  81. /**
  82. * Uses the combination of the provided UserID and Password to login the user into the system and set maintaining the `currentUser` with that user's account information.
  83. * @param {string} userId username
  84. * @param {string} password Password provided by the user
  85. * @param {string} suffix optional param
  86. * @returns {Promise<UserAccountInfo>} Promised logged in user
  87. */
  88. public login(userId: string, password: string, suffix: string = USER_ACCOUNT_SUFFIX): Promise<UserAccountInfo> {
  89. this.logger.debug('Request to login user', userId);
  90. return new Promise((resolve, reject) => {
  91. this.getEncryptedUserData(userId, suffix).then((encryptedValue) => {
  92. this.decrypt(encryptedValue, password).then((decryptedValue) => {
  93. this.logger.debug('Found and loaded account info for user', userId);
  94. this.currentUser = decryptedValue;
  95. this.currentUser.isSampleProfile = (suffix != USER_ACCOUNT_SUFFIX);
  96. this.encryptionKey = password; //FIXME REMOVE!!
  97. if (!isNullOrUndefined(this.currentUser) && suffix != SAMPLE_USER_ACCOUNT_SUFFIX) {
  98. this.globalSettingsProvider.globalSettingsInstance.lastLoginUserId = this.currentUser.userId;
  99. this.globalSettingsProvider.saveToStorage();
  100. }
  101. resolve(this.currentUser);
  102. }).catch((reason) => {
  103. let error: Error = {
  104. message: "Unable to read user account",
  105. stack: reason.toString(),
  106. name: "unable_read_account"
  107. };
  108. this.logger.error(error.message, [error, reason]);
  109. reject(error);
  110. });
  111. });
  112. });
  113. }
  114.  
  115. /**
  116. * Uses the combination of the provided AccountInfo and Password to create a new UserAccountInfo instance, save it on the persistence layer and login into the system.
  117. * @param {UserAccountInfo} accountInfo
  118. * @param {string} password
  119. * @param {string} suffix
  120. * @returns {Promise<UserAccountInfo>} Promised logged in user
  121. */
  122. public signup(accountInfo: UserAccountInfo, password: string, suffix: string = USER_ACCOUNT_SUFFIX): Promise<UserAccountInfo> {
  123. this.logger.debug('Request to signup user', accountInfo.userId);
  124. this.logger.log("user account info", JSON.stringify(accountInfo));
  125. return new Promise((resolve, reject) => {
  126. // Hash user
  127. this.convertUserId(accountInfo.userId).then((cvtUserId) => {
  128. // this.createSecureStorage(this.storage)
  129. // .then((storage) => {
  130. let accountKey = this.calculateAccountKey(cvtUserId, suffix);
  131. this.encryptionKey = password; //FIXME REMOVE!!
  132. this.bootstrapFinancialProfile(accountInfo);
  133. this.encrypt(accountInfo, password).then((encryptedAccountInfo) => {
  134. // Get data from secure storage -> hash_username.user.account
  135. this.storage.get(accountKey).then((data) => {
  136. if (data) {
  137. let error: Error = new Error("User account already exists");
  138. this.logger.error("Account already exists.", error);
  139. reject(error);
  140. }
  141. else {
  142. this.handleCreateUserAccount(accountKey, encryptedAccountInfo, cvtUserId, accountInfo, suffix, resolve);
  143. }
  144. }
  145. ).catch((reason) => {
  146. this.handleCreateUserAccount(accountKey, encryptedAccountInfo, cvtUserId, accountInfo, suffix, resolve);
  147. })
  148. })
  149. .catch((reason) => {
  150. let error: Error = {
  151. message: "Unable to encrypt account.",
  152. stack: reason.toString(),
  153. name: "unable_write_account"
  154. };
  155. this.logger.error(error.message, [error, reason]);
  156. reject(error);
  157. })
  158. })
  159. });
  160. }
  161.  
  162. /**
  163. * Returns the list of available users
  164. * @param {string} filter filter to apply on the search
  165. * @returns {Promise<any[]>}
  166. */
  167. public async availableUsers(filter?: string): Promise<any[]> {
  168. let list = await this.storage.keys();
  169. // Filter values
  170. let users = this.extractUsers(list, filter);
  171. this.logger.log("Available users", users);
  172. return Promise.resolve(users);
  173. }
  174.  
  175. /**
  176. * Log the user out, which forgets the session
  177. */
  178. public logout(): void {
  179. this.logger.debug("Logging current user out.");
  180. this.currentUser = null;
  181. }
  182.  
  183. public async removeUser(userId: string) {
  184. let cvtUserId = await this.convertUserId(userId);
  185. let accountKey = await this.calculateAccountKey(cvtUserId, "");
  186. try {
  187. await this.storage.remove(accountKey);
  188. }
  189. catch (reason) {
  190. this.logger.debug(`Unable to remove user ${userId}`, {
  191. accountKey: accountKey,
  192. cvtUserId: cvtUserId,
  193. reason: reason
  194. })
  195. }
  196. this.logger.info(`Removed user profile ${userId}`);
  197. }
  198.  
  199. /**
  200. * Returns the list of existing sample users
  201. * @returns {Promise<PredefinedUserType[]>}
  202. */
  203. public getSampleUsersList(): Promise<PredefinedUserType[]> {
  204. return new Promise<PredefinedUserType[]>((resolve, reject) => {
  205. let sampleProfiles: PredefinedUserType[] = [];
  206. this.availableUsers(SAMPLE_USER_ACCOUNT_SUFFIX).then(availableUsers => {
  207. for (let i = 0; i < availableUsers.length; i++) {
  208. this.getEncryptedUserData(availableUsers[i], SAMPLE_USER_ACCOUNT_SUFFIX).then(encryptedValue => {
  209. this.decrypt(encryptedValue, SAMPLE_USER_ACCOUNT_PASSWORD).then(userInstance => {
  210. let sample: PredefinedUserType = {
  211. userId: userInstance.userId,
  212. userName: userInstance.userName,
  213. description: userInstance.description,
  214. short_description: userInstance.short_description,
  215. photoUrl: userInstance.photoUrl
  216. };
  217. sampleProfiles.push(sample);
  218.  
  219. // NAU21.HD: Stopping condition. Inside the loop due to promises.
  220. if (i == availableUsers.length - 1) {
  221. resolve(sampleProfiles);
  222. }
  223. });
  224. });
  225. }
  226. }).catch((err) => {
  227. reject(err);
  228. });
  229. });
  230. }
  231.  
  232. /**
  233. * Verifies if the current user is a sample profile
  234. * @returns {boolean}
  235. */
  236. public isSampleProfile(): boolean {
  237. if (this.currentUser) {
  238. return this.currentUser.isSampleProfile;
  239. }
  240. return false;
  241. }
  242.  
  243. /**
  244. * Creates the necessary objects to have a qualified financial profile
  245. * @param {UserAccountInfo} user
  246. */
  247. public bootstrapFinancialProfile(user: UserAccountInfo) {
  248. if (isNullOrUndefined(user.financialProfiles && user.financialProfiles[0])) {
  249. user.financialProfiles[0] = {
  250. calculations: {
  251. overview: {
  252. income: {},
  253. financialAssets: {},
  254. debts: {},
  255. expenses: {}
  256. }
  257. }
  258. };
  259. }
  260. }
  261.  
  262. /**
  263. * Method used to get user's data from the persistence layer and return it as encrypted.
  264. * @param {string} userId username
  265. * @param {string} suffix Optinal parameter - user account's suffix to distinct from user accounts and example accounts
  266. * @returns {Promise<string>}
  267. */
  268. private getEncryptedUserData(userId: string, suffix: string = USER_ACCOUNT_SUFFIX): Promise<string> {
  269. return new Promise((resolve, reject) => {
  270. // Hash user
  271. this.convertUserId(userId).then((hashUser) => {
  272. this.storage.get(this.calculateAccountKey(hashUser, suffix)).then((encryptedValue) => {
  273. if (encryptedValue) {
  274. resolve(encryptedValue);
  275. }
  276. else {
  277. this.handleUnableToFindUser(NULL_DATA_FOR_KEY, reject);
  278. }
  279. }).catch((reason) => {
  280. this.handleUnableToFindUser(reason, reject);
  281. });
  282. })
  283. });
  284. }
  285.  
  286. /**
  287. * Method used to handle errors related to being unable to find a user account
  288. * @param reason Reason to provide
  289. * @param reject Promise to reject
  290. */
  291. private handleUnableToFindUser(reason, reject) {
  292. let error: Error = {
  293. message: "unable to find user account",
  294. stack: reason.toString(),
  295. name: "no_account_found"
  296. };
  297. this.logger.error(error.message, [error, reason]);
  298. reject(error);
  299. }
  300.  
  301. /**
  302. * Method used to handle the creation of the user account
  303. * @param {string} accountKey
  304. * @param encryptedAccountInfo
  305. * @param cvtUserId
  306. * @param {UserAccountInfo} accountInfo
  307. * @param {string} suffix
  308. * @param resolve
  309. */
  310. private handleCreateUserAccount(accountKey: string, encryptedAccountInfo, cvtUserId, accountInfo: UserAccountInfo, suffix: string, resolve) {
  311. this.storage.set(accountKey, encryptedAccountInfo).then(() => {
  312. this.logger.debug('Create account info for user', cvtUserId);
  313. this.currentUser = accountInfo;
  314. this.currentUser.isSampleProfile = (suffix != USER_ACCOUNT_SUFFIX);
  315. resolve(this.currentUser);
  316. });
  317. }
  318.  
  319. /**
  320. * Calculates the user account's key
  321. * @param {string} hashUser
  322. * @param {string} suffix
  323. * @returns {string}
  324. */
  325. private calculateAccountKey(hashUser: string, suffix: string): string {
  326. return hashUser + suffix;
  327. }
  328.  
  329. private convertUserId(userId: string): Promise<string> {
  330. // this.log.debug("Hashing user name");
  331. // return this.cryptoService.hashSHAString(userId).then(
  332. // (hashedUserId) => uint8Array2Str(hashedUserId)
  333. // );
  334.  
  335. return Promise.resolve(userId);
  336. }
  337.  
  338. private async encrypt(accountInfo: UserAccountInfo, password: string): Promise<string> {
  339. this.logger.debug("Encrypting information");
  340.  
  341. let cryptoKey = await this.cryptoService.generateKeyFromPassword(password);
  342. let data = str2Uint8Array(JSON.stringify(accountInfo));
  343. let encryptedContent = await this.cryptoService.encrypt(data.buffer, cryptoKey);
  344.  
  345. return Promise.resolve(uint8Array2Str(encryptedContent));
  346. }
  347.  
  348. private async decrypt(encryptedValue: string, password: string): Promise<UserAccountInfo> {
  349. this.logger.debug("Decrypting content");
  350.  
  351. let uint8Array = str2Uint8Array(encryptedValue);
  352. let cryptoKey = await this.cryptoService.generateKeyFromPassword(password);
  353. let decryptedData = await this.cryptoService.decrypt(uint8Array.buffer, cryptoKey);
  354. let s = uint8Array2Str(decryptedData);
  355.  
  356. return Promise.resolve(JSON.parse(s));
  357. }
  358.  
  359. private extractUsers(list: string[], filter?: string) {
  360. let users = [];
  361. for (let item of list) {
  362. if (filter) {
  363. if (item.indexOf(filter) > 0) {
  364. users.push(this.getReadableUsername(item, filter));
  365. }
  366. } else {
  367. if (item.endsWith(USER_ACCOUNT_SUFFIX) && !(item.indexOf(SAMPLE_USER_ACCOUNT_SUFFIX) > 0)) {
  368. users.push(this.getReadableUsername(item, SAMPLE_USER_ACCOUNT_SUFFIX));
  369. }
  370. }
  371. }
  372. users = users.sort();
  373. return users;
  374. }
  375.  
  376. private getReadableUsername(item: string, suffix: string): string {
  377. return item.substring(0, item.length - suffix.length);
  378. }
  379. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement