Skip to content

Commit

Permalink
Использование другого API для аутентификации (#368)
Browse files Browse the repository at this point in the history
Co-authored-by: Mikhail Kriseev <mish.kryseev@gmail.com>
  • Loading branch information
Namxobick and KriseevM authored Sep 9, 2024
1 parent e46a0c0 commit 506781c
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 77 deletions.
6 changes: 6 additions & 0 deletions lib/core/constants/api_url_strings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ class ApiPaths {
/// Доменное имя
static const String host = 'portal.unn.ru';

/// Второе доменное имя
static const String mobileHost = 'portal-m.unn.ru';

/// Для авторизация
static const String auth = 'auth/';

/// Для авторизации с получением куки (через второе доменное имя)
static const String authWithCookie = 'api/getcookie.php';

/// Для обработки AJAX-запросов
static const String ajax = 'bitrix/services/main/ajax.php';

Expand Down
1 change: 1 addition & 0 deletions lib/core/constants/session_identifier_strings.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class SessionIdentifierStrings {
static const String sessionIdCookieKey = 'PHPSESSID';
static const String sessid = 'sessid';
static const String csrf = 'csrf';
static const String newCsrf = 'X-Bitrix-New-Csrf';
static const String csrfToken = 'X-Bitrix-Csrf-Token';
}
1 change: 1 addition & 0 deletions lib/core/misc/custom_errors/auth_error_messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ extension AuthRequestResultErrorMessages on AuthRequestResult {
AuthRequestResult.success => '',
AuthRequestResult.wrongCredentials => 'Логин или пароль неверны',
AuthRequestResult.noInternet => 'Не получилось подключиться к серверу',
AuthRequestResult.unknown => 'Неизвестная ошибка'
};
}
128 changes: 53 additions & 75 deletions lib/core/services/implementations/authorisation_service_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,123 +3,101 @@ import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:unn_mobile/core/constants/api_url_strings.dart';
import 'package:unn_mobile/core/constants/session_identifier_strings.dart';
import 'package:unn_mobile/core/misc/custom_errors/auth_errors.dart';
import 'package:unn_mobile/core/misc/http_helper.dart';
import 'package:unn_mobile/core/models/online_status_data.dart';
import 'package:unn_mobile/core/services/interfaces/authorisation_service.dart';
import 'package:unn_mobile/core/services/interfaces/logger_service.dart';

class AuthorizationServiceImpl implements AuthorizationService {
final OnlineStatusData _onlineStatus;
final LoggerService _loggerService;
final String _userLogin = 'USER_LOGIN';
final String _userPasswortd = 'USER_PASSWORD';
final String _bxPortatlUnnGuestId = 'BX_PORTAL_UNN_GUEST_ID';

String? _sessionId;
String? _csrf;
String? _guestId;
bool _isAuthorised = false;

AuthorizationServiceImpl(this._onlineStatus);
@override
String? get csrf => _csrf;

@override
bool get isAuthorised => _isAuthorised;

@override
String? get sessionId => _sessionId;

@override
String? get guestId => _guestId;

AuthorizationServiceImpl(this._onlineStatus, this._loggerService);

@override
Future<AuthRequestResult> auth(String login, String password) async {
_isAuthorised = false;

if (await _isOffline()) {
_onlineStatus.isOnline = false;
return AuthRequestResult.noInternet;
}

HttpClientResponse authResponse;
HttpClientResponse csrfResponse;
final requestSender = HttpRequestSender(
host: ApiPaths.mobileHost,
path: ApiPaths.authWithCookie,
);

HttpClientResponse response;
try {
authResponse = await _sendAuthRequest(login, password);
response = await requestSender.postForm(
{
_userLogin: login,
_userPasswortd: password,
},
timeoutSeconds: 15,
);
} on TimeoutException {
_onlineStatus.isOnline = false;
return AuthRequestResult.noInternet;
} on Exception catch (_) {
rethrow;
}

if (authResponse.statusCode != 302) {
if (response.statusCode == 401) {
return AuthRequestResult.wrongCredentials;
}

final sessionCookie = authResponse.cookies
.where(
(cookie) =>
cookie.name == SessionIdentifierStrings.sessionIdCookieKey,
)
.firstOrNull;

if (sessionCookie == null) {
throw SessionCookieException(
message: 'sessionCookie is null',
privateInformation: {'user_login': login},
);
if (response.statusCode != 200) {
return AuthRequestResult.unknown;
}

String responseString;
try {
csrfResponse = await _sendCsrfRequest(sessionCookie.value);
} on TimeoutException {
return AuthRequestResult.noInternet;
} on Exception catch (_) {
rethrow;
responseString = await HttpRequestSender.responseToStringBody(response);
} catch (error, stackTrace) {
_loggerService.logError(error, stackTrace);
return AuthRequestResult.unknown;
}

final csrfValue = csrfResponse.headers.value(
SessionIdentifierStrings.newCsrf,
_sessionId = _extractValue(
responseString,
SessionIdentifierStrings.sessionIdCookieKey,
);

if (csrfValue == null) {
throw CsrfValueException(
message: 'csrfValue is null',
privateInformation: {'user_login': login},
);
}

// bind properties
_sessionId = sessionCookie.value;
_csrf = csrfValue;
_guestId = _extractValue(responseString, _bxPortatlUnnGuestId);
_csrf = _extractValue(responseString, SessionIdentifierStrings.csrf);
_isAuthorised = true;

// success result
_onlineStatus.isOnline = true;
_onlineStatus.timeOfLastOnline = DateTime.now();

return AuthRequestResult.success;
}

@override
String? get csrf => _csrf;

@override
bool get isAuthorised => _isAuthorised;

@override
String? get sessionId => _sessionId;

Future<HttpClientResponse> _sendAuthRequest(
String login,
String password,
) async {
final requestSender = HttpRequestSender(
path: ApiPaths.auth,
queryParams: {'login': 'yes'},
);

return await requestSender.postForm(
{
'AUTH_FORM': 'Y',
'TYPE': 'AUTH',
'backurl': '/',
'USER_LOGIN': login,
'USER_PASSWORD': password,
},
timeoutSeconds: 15,
);
}

Future<HttpClientResponse> _sendCsrfRequest(String session) async {
final requestSender = HttpRequestSender(
path: ApiPaths.ajax,
queryParams: {AjaxActionStrings.actionKey: AjaxActionStrings.getNextPage},
cookies: {SessionIdentifierStrings.sessionIdCookieKey: session},
);

return await requestSender.get(timeoutSeconds: 15);
String _extractValue(String input, String key) {
final RegExp regExp = RegExp('$key=([^;]+)');
final Match? match = regExp.firstMatch(input);
return match?.group(1) ?? '';
}

Future<bool> _isOffline() async {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'dart:io';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:unn_mobile/core/constants/api_url_strings.dart';
import 'package:unn_mobile/core/constants/session_identifier_strings.dart';
import 'package:unn_mobile/core/misc/custom_errors/auth_errors.dart';
import 'package:unn_mobile/core/misc/http_helper.dart';
import 'package:unn_mobile/core/models/online_status_data.dart';
import 'package:unn_mobile/core/services/interfaces/authorisation_service.dart';

class LegacyAuthorizationServiceImpl implements AuthorizationService {
final OnlineStatusData _onlineStatus;
String? _sessionId;
String? _csrf;
bool _isAuthorised = false;

LegacyAuthorizationServiceImpl(this._onlineStatus);

@override
Future<AuthRequestResult> auth(String login, String password) async {
if (await _isOffline()) {
return AuthRequestResult.noInternet;
}

HttpClientResponse authResponse;
HttpClientResponse csrfResponse;

try {
authResponse = await _sendAuthRequest(login, password);
} on TimeoutException {
return AuthRequestResult.noInternet;
} on Exception catch (_) {
rethrow;
}

if (authResponse.statusCode != 302) {
return AuthRequestResult.wrongCredentials;
}

final sessionCookie = authResponse.cookies
.where(
(cookie) =>
cookie.name == SessionIdentifierStrings.sessionIdCookieKey,
)
.firstOrNull;

if (sessionCookie == null) {
throw SessionCookieException(
message: 'sessionCookie is null',
privateInformation: {'user_login': login},
);
}

try {
csrfResponse = await _sendCsrfRequest(sessionCookie.value);
} on TimeoutException {
return AuthRequestResult.noInternet;
} on Exception catch (_) {
rethrow;
}

final csrfValue = csrfResponse.headers.value(
SessionIdentifierStrings.newCsrf,
);

if (csrfValue == null) {
throw CsrfValueException(
message: 'csrfValue is null',
privateInformation: {'user_login': login},
);
}

// bind properties
_sessionId = sessionCookie.value;
_csrf = csrfValue;
_isAuthorised = true;

// success result
_onlineStatus.isOnline = true;
_onlineStatus.timeOfLastOnline = DateTime.now();

return AuthRequestResult.success;
}

@override
String? get csrf => _csrf;

@override
bool get isAuthorised => _isAuthorised;

@override
String? get sessionId => _sessionId;

Future<HttpClientResponse> _sendAuthRequest(
String login,
String password,
) async {
final requestSender = HttpRequestSender(
path: ApiPaths.auth,
queryParams: {'login': 'yes'},
);

return await requestSender.postForm(
{
'AUTH_FORM': 'Y',
'TYPE': 'AUTH',
'backurl': '/',
'USER_LOGIN': login,
'USER_PASSWORD': password,
},
timeoutSeconds: 15,
);
}

Future<HttpClientResponse> _sendCsrfRequest(String session) async {
final requestSender = HttpRequestSender(
path: ApiPaths.ajax,
queryParams: {AjaxActionStrings.actionKey: AjaxActionStrings.getNextPage},
cookies: {SessionIdentifierStrings.sessionIdCookieKey: session},
);

return await requestSender.get(timeoutSeconds: 15);
}

Future<bool> _isOffline() async {
return await Connectivity().checkConnectivity() == ConnectivityResult.none;
}

@override
String? get guestId => throw UnimplementedError();
}
2 changes: 2 additions & 0 deletions lib/core/services/interfaces/authorisation_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ enum AuthRequestResult {
success,
noInternet,
wrongCredentials,
unknown,
}

abstract interface class AuthorizationService {
Expand All @@ -23,4 +24,5 @@ abstract interface class AuthorizationService {
bool get isAuthorised;
String? get csrf;
String? get sessionId;
String? get guestId;
}
1 change: 1 addition & 0 deletions lib/core/viewmodels/loading_page_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class LoadingPageViewModel extends BaseViewModel {
AuthRequestResult.success => _TypeScreen.mainScreen,
AuthRequestResult.noInternet => _TypeScreen.mainScreen,
AuthRequestResult.wrongCredentials => _TypeScreen.authScreen,
AuthRequestResult.unknown => _TypeScreen.authScreen,
};

if (typeScreen == _TypeScreen.mainScreen) {
Expand Down
11 changes: 10 additions & 1 deletion lib/load_services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:unn_mobile/core/models/online_status_data.dart';
import 'package:unn_mobile/core/services/implementations/auth_data_provider_impl.dart';
import 'package:unn_mobile/core/services/implementations/authorisation_refresh_service_impl.dart';
import 'package:unn_mobile/core/services/implementations/authorisation_service_impl.dart';
// import 'package:unn_mobile/core/services/implementations/legacy/authorisation_service_impl.dart';
import 'package:unn_mobile/core/services/implementations/export_schedule_service_impl.dart';
import 'package:unn_mobile/core/services/implementations/feed_stream_updater_service_impl.dart';
import 'package:unn_mobile/core/services/implementations/firebase_logger.dart';
Expand Down Expand Up @@ -72,8 +73,16 @@ void registerDependencies() {
injector.registerSingleton<ExportScheduleService>(
() => ExportScheduleServiceImpl(),
);
/* legacy
injector.registerSingleton<AuthorizationService>(
() => LegacyAuthorizationServiceImpl(get<OnlineStatusData>()),
);
*/
injector.registerSingleton<AuthorizationService>(
() => AuthorizationServiceImpl(get<OnlineStatusData>()),
() => AuthorizationServiceImpl(
get<OnlineStatusData>(),
get<LoggerService>(),
),
);
injector.registerSingleton<AuthDataProvider>(
() => AuthDataProviderImpl(
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: unn_mobile
description: A mobile application for UNN Portal website
publish_to: 'none'

version: 0.2.2+209
version: 0.2.2+210

environment:
sdk: '>=3.1.2 <4.0.0'
Expand Down

0 comments on commit 506781c

Please sign in to comment.