diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 9320af9d..db8230bd 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -628,7 +628,7 @@ class ServerApi extends ApiMap { .replaceAll('"', ''); } - Future> getRecoveryTokenStatus() async { + Future> getRecoveryTokenStatus() async { Response response; var client = await getClient(); @@ -639,7 +639,7 @@ class ServerApi extends ApiMap { return ApiResponse( errorMessage: e.message, statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: RecoveryTokenStatus(exists: false, valid: false)); + data: RecoveryKeyStatus(exists: false, valid: false)); } finally { close(client); } @@ -654,17 +654,23 @@ class ServerApi extends ApiMap { } Future> generateRecoveryToken( - DateTime expiration, int uses) async { + DateTime? expiration, + int? uses, + ) async { Response response; var client = await getClient(); + var data = {}; + if (expiration != null) { + data['expiration'] = expiration.toIso8601String(); + } + if (uses != null) { + data['uses'] = uses; + } try { response = await client.post( '/auth/recovery_token', - data: { - 'expiration': expiration.toIso8601String(), - 'uses': uses, - }, + data: data, ); } on DioError catch (e) { print(e.message); diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart new file mode 100644 index 00000000..377bd010 --- /dev/null +++ b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart @@ -0,0 +1,72 @@ +import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; + +part 'recovery_key_state.dart'; + +class RecoveryKeyCubit + extends ServerInstallationDependendCubit { + RecoveryKeyCubit(ServerInstallationCubit serverInstallationCubit) + : super(serverInstallationCubit, RecoveryKeyState.initial()); + + final api = ServerApi(); + + @override + void load() async { + if (serverInstallationCubit.state is ServerInstallationFinished) { + final status = await _getRecoveryKeyStatus(); + if (status == null) { + emit(state.copyWith(loadingStatus: LoadingStatus.error)); + } else { + emit(state.copyWith(status: status, loadingStatus: LoadingStatus.good)); + } + } else { + emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized)); + } + } + + Future _getRecoveryKeyStatus() async { + final ApiResponse response = + await api.getRecoveryTokenStatus(); + if (response.isSuccess) { + return response.data; + } else { + return null; + } + } + + Future refresh() async { + emit(state.copyWith(loadingStatus: LoadingStatus.refreshing)); + final status = await _getRecoveryKeyStatus(); + if (status == null) { + emit(state.copyWith(loadingStatus: LoadingStatus.error)); + } else { + emit(state.copyWith(status: status, loadingStatus: LoadingStatus.good)); + } + } + + Future generateRecoveryKey({ + DateTime? expirationDate, + int? numberOfUses, + }) async { + final ApiResponse response = + await api.generateRecoveryToken(expirationDate, numberOfUses); + if (response.isSuccess) { + refresh(); + return response.data; + } else { + throw GenerationError(response.errorMessage ?? 'Unknown error'); + } + } + + @override + void clear() { + emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized)); + } +} + +class GenerationError extends Error { + final String message; + + GenerationError(this.message); +} diff --git a/lib/logic/cubit/recovery_key/recovery_key_state.dart b/lib/logic/cubit/recovery_key/recovery_key_state.dart new file mode 100644 index 00000000..60e86ecf --- /dev/null +++ b/lib/logic/cubit/recovery_key/recovery_key_state.dart @@ -0,0 +1,37 @@ +part of 'recovery_key_cubit.dart'; + +enum LoadingStatus { + uninitialized, + refreshing, + good, + error, +} + + +class RecoveryKeyState extends ServerInstallationDependendState { + const RecoveryKeyState(this._status, this.loadingStatus); + + RecoveryKeyState.initial() + : this(RecoveryKeyStatus(exists: false, valid: false), LoadingStatus.refreshing); + + final RecoveryKeyStatus _status; + final LoadingStatus loadingStatus; + + bool get exists => _status.exists; + bool get isValid => _status.valid; + DateTime? get generatedAt => _status.date; + DateTime? get expiresAt => _status.date; + int? get usesLeft => _status.usesLeft; + @override + List get props => [_status, loadingStatus]; + + RecoveryKeyState copyWith({ + RecoveryKeyStatus? status, + LoadingStatus? loadingStatus, + }) { + return RecoveryKeyState( + status ?? this._status, + loadingStatus ?? this.loadingStatus, + ); + } +} diff --git a/lib/logic/models/json/recovery_token_status.dart b/lib/logic/models/json/recovery_token_status.dart index 9c94b41a..8904f84f 100644 --- a/lib/logic/models/json/recovery_token_status.dart +++ b/lib/logic/models/json/recovery_token_status.dart @@ -1,23 +1,34 @@ +import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; part 'recovery_token_status.g.dart'; @JsonSerializable() -class RecoveryTokenStatus { - RecoveryTokenStatus({ +class RecoveryKeyStatus extends Equatable { + RecoveryKeyStatus({ required this.exists, required this.valid, this.date, this.expiration, - this.uses_left, + this.usesLeft, }); final bool exists; final DateTime? date; final DateTime? expiration; - final int? uses_left; + @JsonKey(name: 'uses_left') + final int? usesLeft; final bool valid; - factory RecoveryTokenStatus.fromJson(Map json) => - _$RecoveryTokenStatusFromJson(json); + factory RecoveryKeyStatus.fromJson(Map json) => + _$RecoveryKeyStatusFromJson(json); + + @override + List get props => [ + exists, + date, + expiration, + usesLeft, + valid, + ]; } diff --git a/lib/logic/models/json/recovery_token_status.g.dart b/lib/logic/models/json/recovery_token_status.g.dart index 3f213364..cef9abbd 100644 --- a/lib/logic/models/json/recovery_token_status.g.dart +++ b/lib/logic/models/json/recovery_token_status.g.dart @@ -6,8 +6,8 @@ part of 'recovery_token_status.dart'; // JsonSerializableGenerator // ************************************************************************** -RecoveryTokenStatus _$RecoveryTokenStatusFromJson(Map json) => - RecoveryTokenStatus( +RecoveryKeyStatus _$RecoveryKeyStatusFromJson(Map json) => + RecoveryKeyStatus( exists: json['exists'] as bool, valid: json['valid'] as bool, date: @@ -15,5 +15,5 @@ RecoveryTokenStatus _$RecoveryTokenStatusFromJson(Map json) => expiration: json['expiration'] == null ? null : DateTime.parse(json['expiration'] as String), - uses_left: json['uses_left'] as int?, + usesLeft: json['uses_left'] as int?, );