mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-27 11:16:45 +00:00
Fix username validation and exception handling
1. Refactor string validation classes 2. Rename string validation assets for length 3. Improve exception handling when server is not able to create requested server
This commit is contained in:
parent
265cc15ea5
commit
4c99579f13
|
@ -320,12 +320,13 @@
|
||||||
"delete_ssh_key": "Delete SSH key for {}"
|
"delete_ssh_key": "Delete SSH key for {}"
|
||||||
},
|
},
|
||||||
"validations": {
|
"validations": {
|
||||||
"required": "Required",
|
"required": "Required.",
|
||||||
"invalid_format": "Invalid format",
|
"invalid_format": "Invalid format.",
|
||||||
"root_name": "User name cannot be 'root'",
|
"root_name": "User name cannot be 'root'.",
|
||||||
"key_format": "Invalid key format",
|
"key_format": "Invalid key format.",
|
||||||
"length": "Length is [] should be {}",
|
"length_not_equal": "Length is []. Should be {}.",
|
||||||
"user_already_exist": "Already exists",
|
"length_longer": "Length is []. Should be shorter than or equal to {}.",
|
||||||
"key_already_exists": "This key already exists"
|
"user_already_exist": "This user already exists.",
|
||||||
|
"key_already_exists": "This key already exists."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,9 +323,10 @@
|
||||||
"validations": {
|
"validations": {
|
||||||
"required": "Обязательное поле.",
|
"required": "Обязательное поле.",
|
||||||
"invalid_format": "Неверный формат.",
|
"invalid_format": "Неверный формат.",
|
||||||
"root_name": "Имя пользователя не может быть'root'.",
|
"root_name": "Имя пользователя не может быть 'root'.",
|
||||||
"key_format": "Неверный формат.",
|
"key_format": "Неверный формат.",
|
||||||
"length": "Длина строки [] должна быть {}.",
|
"length_not_equal": "Длина строки []. Должно быть равно {}.",
|
||||||
|
"length_longer": "Длина строки []. Должно быть меньше либо равно {}.",
|
||||||
"user_already_exist": "Имя уже используется.",
|
"user_already_exist": "Имя уже используется.",
|
||||||
"key_already_exists": "Этот ключ уже добавлен."
|
"key_already_exists": "Этот ключ уже добавлен."
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ class ApiResponse<D> {
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
final D data;
|
final D data;
|
||||||
|
|
||||||
get isSuccess => statusCode >= 200 && statusCode < 300;
|
bool get isSuccess => statusCode >= 200 && statusCode < 300;
|
||||||
|
|
||||||
ApiResponse({
|
ApiResponse({
|
||||||
required this.statusCode,
|
required this.statusCode,
|
||||||
|
@ -65,27 +65,48 @@ class ServerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<User>> createUser(User user) async {
|
Future<ApiResponse<User>> createUser(User user) async {
|
||||||
Response response;
|
|
||||||
|
|
||||||
var client = await getClient();
|
var client = await getClient();
|
||||||
// POST request with JSON body containing username and password
|
// POST request with JSON body containing username and password
|
||||||
|
|
||||||
response = await client.post(
|
var makeErrorApiReponse = (int status) {
|
||||||
'/users',
|
|
||||||
data: {
|
|
||||||
'username': user.login,
|
|
||||||
'password': user.password,
|
|
||||||
},
|
|
||||||
options: Options(
|
|
||||||
contentType: 'application/json',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
close(client);
|
|
||||||
|
|
||||||
if (response.statusCode == HttpStatus.created) {
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
statusCode: response.statusCode ?? HttpStatus.internalServerError,
|
statusCode: status,
|
||||||
|
data: User(
|
||||||
|
login: user.login,
|
||||||
|
password: user.password,
|
||||||
|
isFoundOnServer: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
late Response<dynamic> response;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await client.post(
|
||||||
|
'/users',
|
||||||
|
data: {
|
||||||
|
'username': user.login,
|
||||||
|
'password': user.password,
|
||||||
|
},
|
||||||
|
options: Options(
|
||||||
|
contentType: 'application/json',
|
||||||
|
receiveDataWhenStatusError: true,
|
||||||
|
followRedirects: false,
|
||||||
|
validateStatus: (status) {
|
||||||
|
return (status != null) &&
|
||||||
|
(status < HttpStatus.internalServerError);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return makeErrorApiReponse(HttpStatus.internalServerError);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((response.statusCode != null) &&
|
||||||
|
(response.statusCode == HttpStatus.created)) {
|
||||||
|
return ApiResponse(
|
||||||
|
statusCode: response.statusCode!,
|
||||||
data: User(
|
data: User(
|
||||||
login: user.login,
|
login: user.login,
|
||||||
password: user.password,
|
password: user.password,
|
||||||
|
@ -93,18 +114,11 @@ class ServerApi extends ApiMap {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return ApiResponse(
|
print(response.statusCode.toString() +
|
||||||
statusCode: response.statusCode ?? HttpStatus.internalServerError,
|
": " +
|
||||||
data: User(
|
(response.statusMessage ?? ""));
|
||||||
login: user.login,
|
return makeErrorApiReponse(
|
||||||
password: user.password,
|
response.statusCode ?? HttpStatus.internalServerError);
|
||||||
isFoundOnServer: false,
|
|
||||||
note: response.data['message'] ?? null,
|
|
||||||
),
|
|
||||||
errorMessage: response.data?.containsKey('error') ?? false
|
|
||||||
? response.data['error']
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,6 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
//ValidationModel<String>(
|
|
||||||
//(s) => regExp.hasMatch(s), 'invalid key format'),
|
|
||||||
//LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64')
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -22,9 +19,6 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('required'),
|
RequiredStringValidation('required'),
|
||||||
//ValidationModel<String>(
|
|
||||||
//(s) => regExp.hasMatch(s), 'invalid key format'),
|
|
||||||
//LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64')
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,7 @@ import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
import '../validations/validations.dart';
|
|
||||||
|
|
||||||
class CloudFlareFormCubit extends FormCubit {
|
class CloudFlareFormCubit extends FormCubit {
|
||||||
CloudFlareFormCubit(this.initializingCubit) {
|
CloudFlareFormCubit(this.initializingCubit) {
|
||||||
|
@ -16,12 +15,7 @@ class CloudFlareFormCubit extends FormCubit {
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
||||||
LengthStringValidationWithLengthShowing(
|
LengthStringNotEqualValidation(40)
|
||||||
40,
|
|
||||||
'validations.length'.tr(
|
|
||||||
args: ["40"],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,7 @@ import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
import '../validations/validations.dart';
|
|
||||||
|
|
||||||
class HetznerFormCubit extends FormCubit {
|
class HetznerFormCubit extends FormCubit {
|
||||||
HetznerFormCubit(this.initializingCubit) {
|
HetznerFormCubit(this.initializingCubit) {
|
||||||
|
@ -16,8 +15,7 @@ class HetznerFormCubit extends FormCubit {
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
||||||
LengthStringValidationWithLengthShowing(
|
LengthStringNotEqualValidation(64)
|
||||||
64, 'validations.length'.tr(args: ["64"]))
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/job.dart';
|
import 'package:selfprivacy/logic/models/job.dart';
|
||||||
import 'package:selfprivacy/logic/models/user.dart';
|
import 'package:selfprivacy/logic/models/user.dart';
|
||||||
|
@ -15,8 +16,10 @@ class UserFormCubit extends FormCubit {
|
||||||
}) {
|
}) {
|
||||||
var isEdit = user != null;
|
var isEdit = user != null;
|
||||||
|
|
||||||
var userRegExp = RegExp(r"\W");
|
var userAllowedRegExp = RegExp(r"^[a-z_][a-z0-9_]+$");
|
||||||
var passwordRegExp = RegExp(r"[\n\r\s]+");
|
const userMaxLength = 31;
|
||||||
|
|
||||||
|
var passwordForbiddenRegExp = RegExp(r"[\n\r\s]+");
|
||||||
|
|
||||||
login = FieldCubit(
|
login = FieldCubit(
|
||||||
initalValue: isEdit ? user!.login : '',
|
initalValue: isEdit ? user!.login : '',
|
||||||
|
@ -28,8 +31,9 @@ class UserFormCubit extends FormCubit {
|
||||||
'validations.user_already_exist'.tr(),
|
'validations.user_already_exist'.tr(),
|
||||||
),
|
),
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
LengthStringLongerValidation(userMaxLength),
|
||||||
(s) => userRegExp.hasMatch(s), 'validations.invalid_format'.tr()),
|
ValidationModel<String>((s) => !userAllowedRegExp.hasMatch(s),
|
||||||
|
'validations.invalid_format'.tr()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -38,7 +42,7 @@ class UserFormCubit extends FormCubit {
|
||||||
isEdit ? (user?.password ?? '') : StringGenerators.userPassword(),
|
isEdit ? (user?.password ?? '') : StringGenerators.userPassword(),
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>((s) => passwordRegExp.hasMatch(s),
|
ValidationModel<String>((s) => passwordForbiddenRegExp.hasMatch(s),
|
||||||
'validations.invalid_format'.tr()),
|
'validations.invalid_format'.tr()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,13 +1,28 @@
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class LengthStringValidationWithLengthShowing extends ValidationModel<String> {
|
abstract class LengthStringValidation extends ValidationModel<String> {
|
||||||
LengthStringValidationWithLengthShowing(int length, String errorText)
|
LengthStringValidation(bool Function(String) predicate, String errorMessage)
|
||||||
: super((n) => n.length != length, errorText);
|
: super(predicate, errorMessage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? check(String val) {
|
String? check(String value) {
|
||||||
var length = val.length;
|
var length = value.length;
|
||||||
var errorMassage = this.errorMassage.replaceAll("[]", length.toString());
|
var errorMessage = this.errorMassage.replaceAll("[]", length.toString());
|
||||||
return test(val) ? errorMassage : null;
|
return test(value) ? errorMessage : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String must be equal to [length]
|
||||||
|
class LengthStringNotEqualValidation extends LengthStringValidation {
|
||||||
|
LengthStringNotEqualValidation(int length)
|
||||||
|
: super((n) => n.length != length,
|
||||||
|
'validations.length_not_equal'.tr(args: [length.toString()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// String must be shorter than or equal to [length]
|
||||||
|
class LengthStringLongerValidation extends LengthStringValidation {
|
||||||
|
LengthStringLongerValidation(int length)
|
||||||
|
: super((n) => n.length > length,
|
||||||
|
'validations.length_longer'.tr(args: [length.toString()]));
|
||||||
|
}
|
||||||
|
|
|
@ -160,7 +160,12 @@ class UsersCubit extends AppConfigDependendCubit<UsersState> {
|
||||||
if (user.login == 'root' || user.login == state.primaryUser.login) {
|
if (user.login == 'root' || user.login == state.primaryUser.login) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If API returned error, do nothing
|
||||||
final result = await api.createUser(user);
|
final result = await api.createUser(user);
|
||||||
|
if (!result.isSuccess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var loadedUsers = List<User>.from(state.users);
|
var loadedUsers = List<User>.from(state.users);
|
||||||
loadedUsers.add(result.data);
|
loadedUsers.add(result.data);
|
||||||
await box.clear();
|
await box.clear();
|
||||||
|
|
Loading…
Reference in a new issue