2019-04-22 17:16:26 +00:00
|
|
|
import 'package:comunic/helpers/api_helper.dart';
|
2019-11-01 08:59:05 +00:00
|
|
|
import 'package:comunic/helpers/preferences_helper.dart';
|
2020-04-20 06:47:08 +00:00
|
|
|
import 'package:comunic/helpers/websocket_helper.dart';
|
2019-04-22 17:16:26 +00:00
|
|
|
import 'package:comunic/models/api_request.dart';
|
|
|
|
import 'package:comunic/models/authentication_details.dart';
|
2019-11-02 17:54:30 +00:00
|
|
|
import 'package:comunic/models/new_account.dart';
|
2021-02-18 17:58:47 +00:00
|
|
|
import 'package:comunic/models/res_check_password_reset_token.dart';
|
2019-04-22 17:16:26 +00:00
|
|
|
|
|
|
|
/// Account helper
|
|
|
|
///
|
|
|
|
/// @author Pierre HUBERT
|
|
|
|
|
|
|
|
enum AuthResult {
|
|
|
|
SUCCESS,
|
|
|
|
TOO_MANY_ATTEMPTS,
|
|
|
|
NETWORK_ERROR,
|
|
|
|
INVALID_CREDENTIALS
|
|
|
|
}
|
|
|
|
|
2019-11-02 17:54:30 +00:00
|
|
|
enum CreateAccountResult {
|
|
|
|
SUCCESS,
|
|
|
|
ERROR_TOO_MANY_REQUESTS,
|
|
|
|
ERROR_EXISTING_EMAIL,
|
|
|
|
ERROR
|
|
|
|
}
|
|
|
|
|
2019-04-22 17:16:26 +00:00
|
|
|
class AccountHelper {
|
2019-04-23 16:11:19 +00:00
|
|
|
// Current user ID
|
|
|
|
static int _currentUserID = -1;
|
|
|
|
|
|
|
|
/// Checkout whether current user is signed in or not
|
|
|
|
///
|
|
|
|
/// Warning : This method MUST BE CALLED AT LEAST ONCE AFTER APP START !!!
|
|
|
|
Future<bool> signedIn() async {
|
2019-11-01 08:59:05 +00:00
|
|
|
bool signedIn =
|
2021-02-13 15:03:07 +00:00
|
|
|
(await PreferencesHelper.getInstance()).getLoginToken() != null;
|
2019-04-23 16:11:19 +00:00
|
|
|
|
|
|
|
// Load current user ID for later use
|
|
|
|
if (signedIn && _currentUserID == -1) await _loadCurrentUserID();
|
|
|
|
|
|
|
|
return signedIn;
|
|
|
|
}
|
|
|
|
|
2019-04-22 17:16:26 +00:00
|
|
|
/// Sign in user
|
|
|
|
Future<AuthResult> signIn(AuthenticationDetails auth) async {
|
|
|
|
final request = APIRequest(uri: "account/login");
|
2021-02-13 15:03:07 +00:00
|
|
|
request.addString("mail", auth.email);
|
|
|
|
request.addString("password", auth.password);
|
2019-04-22 17:16:26 +00:00
|
|
|
|
|
|
|
final response = await APIHelper().exec(request);
|
|
|
|
|
|
|
|
// Handle errors
|
|
|
|
if (response.code == 401)
|
|
|
|
return AuthResult.INVALID_CREDENTIALS;
|
|
|
|
else if (response.code == 429)
|
|
|
|
return AuthResult.TOO_MANY_ATTEMPTS;
|
|
|
|
else if (response.code != 200) return AuthResult.NETWORK_ERROR;
|
|
|
|
|
2021-02-13 15:03:07 +00:00
|
|
|
// Save login token
|
2019-11-01 08:59:05 +00:00
|
|
|
await (await PreferencesHelper.getInstance())
|
2021-02-13 15:03:07 +00:00
|
|
|
.setLoginToken(response.getObject()["token"]);
|
2019-04-22 17:16:26 +00:00
|
|
|
|
2019-04-23 16:11:19 +00:00
|
|
|
// Get current user ID
|
|
|
|
final userID = await _downloadCurrentUserID();
|
|
|
|
if (userID == null) {
|
|
|
|
await signOut(); // We can not stay signed in without current user ID
|
|
|
|
return AuthResult.NETWORK_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save current user ID
|
2021-02-19 16:32:38 +00:00
|
|
|
final preferences = await PreferencesHelper.getInstance();
|
|
|
|
await preferences.setInt(PreferencesKeyList.USER_ID, userID);
|
2019-04-23 16:11:19 +00:00
|
|
|
_currentUserID = userID;
|
|
|
|
|
2019-04-22 17:16:26 +00:00
|
|
|
return AuthResult.SUCCESS;
|
|
|
|
}
|
2019-04-23 09:48:49 +00:00
|
|
|
|
|
|
|
/// Sign out user
|
|
|
|
Future<void> signOut() async {
|
2021-02-13 15:07:23 +00:00
|
|
|
await APIRequest.withLogin("account/logout").exec();
|
|
|
|
|
2021-02-19 16:32:38 +00:00
|
|
|
final preferencesHelper = await PreferencesHelper.getInstance();
|
|
|
|
await preferencesHelper.setLoginToken(null);
|
|
|
|
await preferencesHelper.setInt(PreferencesKeyList.USER_ID, -1);
|
2019-04-23 16:11:19 +00:00
|
|
|
_currentUserID = 0;
|
2020-04-20 06:47:08 +00:00
|
|
|
|
|
|
|
// Close current web socket
|
|
|
|
WebSocketHelper.close();
|
2019-04-23 09:48:49 +00:00
|
|
|
}
|
|
|
|
|
2019-11-02 17:54:30 +00:00
|
|
|
/// Create a new user account
|
|
|
|
Future<CreateAccountResult> createAccount(NewAccount info) async {
|
|
|
|
final response = await APIRequest(
|
|
|
|
uri: "account/create",
|
|
|
|
needLogin: false,
|
|
|
|
args: {
|
|
|
|
"firstName": info.firstName,
|
|
|
|
"lastName": info.lastName,
|
|
|
|
"emailAddress": info.email,
|
|
|
|
"password": info.password,
|
|
|
|
},
|
|
|
|
).exec();
|
|
|
|
|
|
|
|
switch (response.code) {
|
|
|
|
case 200:
|
|
|
|
return CreateAccountResult.SUCCESS;
|
|
|
|
|
|
|
|
case 409:
|
|
|
|
return CreateAccountResult.ERROR_EXISTING_EMAIL;
|
|
|
|
|
|
|
|
case 429:
|
|
|
|
return CreateAccountResult.ERROR_TOO_MANY_REQUESTS;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return CreateAccountResult.ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-03 12:22:06 +00:00
|
|
|
/// Check out whether a given email address exists or not
|
|
|
|
///
|
|
|
|
/// Throws in case of failure
|
|
|
|
static Future<bool> existsMailAccount(String email) async =>
|
|
|
|
(await APIRequest.withoutLogin("account/exists_email")
|
|
|
|
.addString("email", email)
|
|
|
|
.execWithThrow())
|
|
|
|
.getObject()["exists"];
|
|
|
|
|
2021-02-18 17:58:47 +00:00
|
|
|
/// Get current user email address
|
|
|
|
static Future<String> getCurrentAccountEmailAddress() async =>
|
|
|
|
(await APIRequest.withLogin("account/mail")
|
|
|
|
.execWithThrowGetObject())["mail"];
|
|
|
|
|
2020-05-03 12:22:06 +00:00
|
|
|
/// Check out whether security questions have been set for an account or not
|
|
|
|
///
|
|
|
|
/// Throws in case of failure
|
|
|
|
static Future<bool> hasSecurityQuestions(String email) async =>
|
|
|
|
(await APIRequest.withoutLogin("account/has_security_questions")
|
2020-05-03 13:35:07 +00:00
|
|
|
.addString("email", email)
|
|
|
|
.execWithThrow())
|
2020-05-03 12:22:06 +00:00
|
|
|
.getObject()["defined"];
|
|
|
|
|
2020-05-03 13:35:07 +00:00
|
|
|
/// Get the security questions of the user
|
|
|
|
///
|
|
|
|
/// Throws in case of failure
|
|
|
|
static Future<List<String>> getSecurityQuestions(String email) async =>
|
|
|
|
((await APIRequest.withoutLogin("account/get_security_questions")
|
|
|
|
.addString("email", email)
|
|
|
|
.execWithThrow())
|
|
|
|
.getObject()["questions"])
|
|
|
|
.cast<String>();
|
|
|
|
|
2020-05-03 14:21:36 +00:00
|
|
|
/// Validate given security answers
|
|
|
|
///
|
|
|
|
/// Throws an [Exception] in case of failure
|
|
|
|
///
|
|
|
|
/// Returns a password reset token in case of success
|
|
|
|
static Future<String> checkAnswers(
|
|
|
|
String email, List<String> answers) async =>
|
|
|
|
(await APIRequest.withoutLogin("account/check_security_answers")
|
|
|
|
.addString("email", email)
|
|
|
|
.addString("answers",
|
|
|
|
answers.map((f) => Uri.encodeComponent(f)).join("&"))
|
|
|
|
.execWithThrow())
|
|
|
|
.getObject()["reset_token"];
|
|
|
|
|
2020-05-03 14:55:00 +00:00
|
|
|
/// Check a password reset token
|
|
|
|
///
|
|
|
|
/// Throws in case failure
|
2021-02-18 17:58:47 +00:00
|
|
|
static Future<ResCheckPasswordToken> validatePasswordResetToken(
|
|
|
|
String token) async {
|
|
|
|
final response =
|
|
|
|
await APIRequest.withoutLogin("account/check_password_reset_token")
|
|
|
|
.addString("token", token)
|
|
|
|
.execWithThrowGetObject();
|
|
|
|
|
|
|
|
return ResCheckPasswordToken(
|
|
|
|
firstName: response["first_name"],
|
|
|
|
lastName: response["last_name"],
|
|
|
|
email: response["mail"],
|
|
|
|
);
|
|
|
|
}
|
2020-05-03 14:55:00 +00:00
|
|
|
|
|
|
|
/// Change account password using password reset token
|
|
|
|
///
|
|
|
|
/// Throws an exception in case of failure
|
|
|
|
static Future<void> changeAccountPassword(
|
|
|
|
String token, String password) async =>
|
|
|
|
await APIRequest.withoutLogin("account/reset_user_passwd")
|
|
|
|
.addString("token", token)
|
|
|
|
.addString("password", password)
|
|
|
|
.execWithThrow();
|
|
|
|
|
2019-04-23 16:11:19 +00:00
|
|
|
/// Get current user ID from the server
|
|
|
|
Future<int> _downloadCurrentUserID() async {
|
2021-02-13 15:03:07 +00:00
|
|
|
final response = await APIRequest.withLogin("account/id").exec();
|
2019-04-23 16:11:19 +00:00
|
|
|
|
|
|
|
if (response.code != 200) return null;
|
|
|
|
|
|
|
|
return response.getObject()["userID"];
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the ID of the currently signed in user
|
|
|
|
Future<void> _loadCurrentUserID() async {
|
2021-02-19 16:32:38 +00:00
|
|
|
final preferences = await PreferencesHelper.getInstance();
|
|
|
|
_currentUserID = preferences.getInt(PreferencesKeyList.USER_ID);
|
2019-04-23 16:11:19 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 17:04:53 +00:00
|
|
|
/// Check if current user ID is loaded or not
|
|
|
|
static bool get isUserIDLoaded => _currentUserID > 0;
|
|
|
|
|
2019-04-23 16:11:19 +00:00
|
|
|
/// Get the ID of the currently signed in user
|
|
|
|
static int getCurrentUserID() {
|
|
|
|
if (_currentUserID == -1) throw "Current user ID has not been loaded yet!";
|
|
|
|
return _currentUserID;
|
|
|
|
}
|
2020-05-01 07:42:01 +00:00
|
|
|
|
|
|
|
/// Disconnect all the devices of the current user
|
|
|
|
///
|
|
|
|
/// Throws in case of failure
|
|
|
|
static Future<void> disconnectAllDevices() async {
|
|
|
|
await APIRequest(uri: "account/disconnect_all_devices", needLogin: true)
|
|
|
|
.execWithThrow();
|
|
|
|
}
|
2020-05-01 08:56:57 +00:00
|
|
|
|
|
|
|
/// Remove permanently a user account
|
|
|
|
///
|
|
|
|
/// Throws in case of failure
|
|
|
|
static Future<void> deleteAccount(String password) async {
|
|
|
|
await APIRequest(uri: "account/delete", needLogin: true)
|
|
|
|
.addString("password", password)
|
|
|
|
.execWithThrow();
|
|
|
|
}
|
2019-04-22 17:16:26 +00:00
|
|
|
}
|