Application Android et IOS pour l'amicale des élèves
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ConnectionManager.js 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Copyright (c) 2019 - 2020 Arnaud Vergnet.
  3. *
  4. * This file is part of Campus INSAT.
  5. *
  6. * Campus INSAT is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Campus INSAT is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. // @flow
  20. import * as Keychain from 'react-native-keychain';
  21. import type {ApiDataLoginType, ApiGenericDataType} from '../utils/WebData';
  22. import {apiRequest, ERROR_TYPE} from '../utils/WebData';
  23. /**
  24. * champ: error
  25. *
  26. * 0 : SUCCESS -> pas d'erreurs
  27. * 1 : BAD_CREDENTIALS -> email ou mdp invalide
  28. * 2 : BAD_TOKEN -> session expirée
  29. * 3 : NO_CONSENT
  30. * 403 : FORBIDDEN -> accès a la ressource interdit
  31. * 500 : SERVER_ERROR -> pb coté serveur
  32. */
  33. const SERVER_NAME = 'amicale-insat.fr';
  34. const AUTH_PATH = 'password';
  35. export default class ConnectionManager {
  36. static instance: ConnectionManager | null = null;
  37. #email: string;
  38. #token: string | null;
  39. constructor() {
  40. this.#token = null;
  41. }
  42. /**
  43. * Gets this class instance or create one if none is found
  44. *
  45. * @returns {ConnectionManager}
  46. */
  47. static getInstance(): ConnectionManager {
  48. if (ConnectionManager.instance == null)
  49. ConnectionManager.instance = new ConnectionManager();
  50. return ConnectionManager.instance;
  51. }
  52. /**
  53. * Gets the current token
  54. *
  55. * @returns {string | null}
  56. */
  57. getToken(): string | null {
  58. return this.#token;
  59. }
  60. /**
  61. * Tries to recover login token from the secure keychain
  62. *
  63. * @returns Promise<string>
  64. */
  65. async recoverLogin(): Promise<string> {
  66. return new Promise(
  67. (resolve: (token: string) => void, reject: () => void) => {
  68. const token = this.getToken();
  69. if (token != null) resolve(token);
  70. else {
  71. Keychain.getInternetCredentials(SERVER_NAME)
  72. .then((data: Keychain.UserCredentials | false) => {
  73. if (
  74. data != null &&
  75. data.password != null &&
  76. typeof data.password === 'string'
  77. ) {
  78. this.#token = data.password;
  79. resolve(this.#token);
  80. } else reject();
  81. })
  82. .catch((): void => reject());
  83. }
  84. },
  85. );
  86. }
  87. /**
  88. * Check if the user has a valid token
  89. *
  90. * @returns {boolean}
  91. */
  92. isLoggedIn(): boolean {
  93. return this.getToken() !== null;
  94. }
  95. /**
  96. * Saves the login token in the secure keychain
  97. *
  98. * @param email
  99. * @param token
  100. * @returns Promise<void>
  101. */
  102. async saveLogin(email: string, token: string): Promise<void> {
  103. return new Promise((resolve: () => void, reject: () => void) => {
  104. Keychain.setInternetCredentials(SERVER_NAME, 'token', token)
  105. .then(() => {
  106. this.#token = token;
  107. this.#email = email;
  108. resolve();
  109. })
  110. .catch((): void => reject());
  111. });
  112. }
  113. /**
  114. * Deletes the login token from the keychain
  115. *
  116. * @returns Promise<void>
  117. */
  118. async disconnect(): Promise<void> {
  119. return new Promise((resolve: () => void, reject: () => void) => {
  120. Keychain.resetInternetCredentials(SERVER_NAME)
  121. .then(() => {
  122. this.#token = null;
  123. resolve();
  124. })
  125. .catch((): void => reject());
  126. });
  127. }
  128. /**
  129. * Sends the given login and password to the api.
  130. * If the combination is valid, the login token is received and saved in the secure keychain.
  131. * If not, the promise is rejected with the corresponding error code.
  132. *
  133. * @param email
  134. * @param password
  135. * @returns Promise<void>
  136. */
  137. async connect(email: string, password: string): Promise<void> {
  138. return new Promise(
  139. (resolve: () => void, reject: (error: number) => void) => {
  140. const data = {
  141. email,
  142. password,
  143. };
  144. apiRequest(AUTH_PATH, 'POST', data)
  145. .then((response: ApiDataLoginType) => {
  146. if (response.token != null) {
  147. this.saveLogin(email, response.token)
  148. .then((): void => resolve())
  149. .catch((): void => reject(ERROR_TYPE.TOKEN_SAVE));
  150. } else reject(ERROR_TYPE.SERVER_ERROR);
  151. })
  152. .catch((error: number): void => reject(error));
  153. },
  154. );
  155. }
  156. /**
  157. * Sends an authenticated request with the login token to the API
  158. *
  159. * @param path
  160. * @param params
  161. * @returns Promise<ApiGenericDataType>
  162. */
  163. async authenticatedRequest(
  164. path: string,
  165. params: {...},
  166. ): Promise<ApiGenericDataType> {
  167. return new Promise(
  168. (
  169. resolve: (response: ApiGenericDataType) => void,
  170. reject: (error: number) => void,
  171. ) => {
  172. if (this.getToken() !== null) {
  173. const data = {
  174. ...params,
  175. token: this.getToken(),
  176. };
  177. apiRequest(path, 'POST', data)
  178. .then((response: ApiGenericDataType): void => resolve(response))
  179. .catch((error: number): void => reject(error));
  180. } else reject(ERROR_TYPE.TOKEN_RETRIEVE);
  181. },
  182. );
  183. }
  184. }