selfprivacy.org.app/lib/logic/cubit/users/users_cubit.dart
NaiJi 4c99579f13 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
2022-05-02 14:56:46 +03:00

314 lines
10 KiB
Dart

import 'package:bloc/bloc.dart';
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/user.dart';
import '../../api_maps/server.dart';
export 'package:provider/provider.dart';
part 'users_state.dart';
class UsersCubit extends AppConfigDependendCubit<UsersState> {
UsersCubit(AppConfigCubit appConfigCubit)
: super(
appConfigCubit,
UsersState(
<User>[], User(login: 'root'), User(login: 'loading...')));
Box<User> box = Hive.box<User>(BNames.users);
Box configBox = Hive.box(BNames.appConfig);
final api = ServerApi();
Future<void> load() async {
if (appConfigCubit.state is AppConfigFinished) {
var loadedUsers = box.values.toList();
final primaryUser = configBox.get(BNames.rootUser,
defaultValue: User(login: 'loading...'));
List<String> rootKeys = [
...configBox.get(BNames.rootKeys, defaultValue: [])
];
if (loadedUsers.isNotEmpty) {
emit(UsersState(
loadedUsers, User(login: 'root', sshKeys: rootKeys), primaryUser));
}
final usersFromServer = await api.getUsersList();
if (usersFromServer.isSuccess) {
final updatedList =
mergeLocalAndServerUsers(loadedUsers, usersFromServer.data);
emit(UsersState(
updatedList, User(login: 'root', sshKeys: rootKeys), primaryUser));
}
final usersWithSshKeys = await loadSshKeys(state.users);
// Update the users it the box
box.clear();
box.addAll(usersWithSshKeys);
final rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
configBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
final primaryUserWithSshKeys =
(await loadSshKeys([state.primaryUser])).first;
configBox.put(BNames.rootUser, primaryUserWithSshKeys);
emit(UsersState(
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys));
}
}
List<User> mergeLocalAndServerUsers(
List<User> localUsers, List<String> serverUsers) {
// If local user not exists on server, add it with isFoundOnServer = false
// If server user not exists on local, add it
List<User> mergedUsers = [];
List<String> serverUsersCopy = List.from(serverUsers);
for (var localUser in localUsers) {
if (serverUsersCopy.contains(localUser.login)) {
mergedUsers.add(User(
login: localUser.login,
isFoundOnServer: true,
password: localUser.password,
sshKeys: localUser.sshKeys,
));
serverUsersCopy.remove(localUser.login);
} else {
mergedUsers.add(User(
login: localUser.login,
isFoundOnServer: false,
password: localUser.password,
note: localUser.note,
));
}
}
for (var serverUser in serverUsersCopy) {
mergedUsers.add(User(
login: serverUser,
isFoundOnServer: true,
));
}
return mergedUsers;
}
Future<List<User>> loadSshKeys(List<User> users) async {
List<User> updatedUsers = [];
for (var user in users) {
if (user.isFoundOnServer ||
user.login == 'root' ||
user.login == state.primaryUser.login) {
final sshKeys = await api.getUserSshKeys(user);
print('sshKeys for $user: ${sshKeys.data}');
if (sshKeys.isSuccess) {
updatedUsers.add(User(
login: user.login,
isFoundOnServer: true,
password: user.password,
sshKeys: sshKeys.data,
note: user.note,
));
} else {
updatedUsers.add(User(
login: user.login,
isFoundOnServer: true,
password: user.password,
note: user.note,
));
}
} else {
updatedUsers.add(User(
login: user.login,
isFoundOnServer: false,
password: user.password,
note: user.note,
));
}
}
return updatedUsers;
}
Future<void> refresh() async {
List<User> updatedUsers = List<User>.from(state.users);
final usersFromServer = await api.getUsersList();
if (usersFromServer.isSuccess) {
updatedUsers =
mergeLocalAndServerUsers(updatedUsers, usersFromServer.data);
}
final usersWithSshKeys = await loadSshKeys(updatedUsers);
box.clear();
box.addAll(usersWithSshKeys);
final rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
configBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
final primaryUserWithSshKeys =
(await loadSshKeys([state.primaryUser])).first;
configBox.put(BNames.rootUser, primaryUserWithSshKeys);
emit(UsersState(
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys));
return;
}
Future<void> createUser(User user) async {
// If user exists on server, do nothing
if (state.users.any((u) => u.login == user.login && u.isFoundOnServer)) {
return;
}
// If user is root or primary user, do nothing
if (user.login == 'root' || user.login == state.primaryUser.login) {
return;
}
// If API returned error, do nothing
final result = await api.createUser(user);
if (!result.isSuccess) {
return;
}
var loadedUsers = List<User>.from(state.users);
loadedUsers.add(result.data);
await box.clear();
await box.addAll(loadedUsers);
emit(state.copyWith(users: loadedUsers));
}
Future<void> deleteUser(User user) async {
// If user is primary or root, don't delete
if (user.login == state.primaryUser.login || user.login == 'root') {
return;
}
var loadedUsers = List<User>.from(state.users);
final result = await api.deleteUser(user);
if (result) {
loadedUsers.removeWhere((u) => u.login == user.login);
await box.clear();
await box.addAll(loadedUsers);
emit(state.copyWith(users: loadedUsers));
}
}
Future<void> addSshKey(User user, String publicKey) async {
// If adding root key, use api.addRootSshKey
// Otherwise, use api.addUserSshKey
if (user.login == 'root') {
final result = await api.addRootSshKey(publicKey);
if (result.isSuccess) {
// Add ssh key to the array of root keys
final rootKeys =
configBox.get(BNames.rootKeys, defaultValue: []) as List<String>;
rootKeys.add(publicKey);
configBox.put(BNames.rootKeys, rootKeys);
emit(state.copyWith(
rootUser: User(
login: state.rootUser.login,
isFoundOnServer: true,
password: state.rootUser.password,
sshKeys: rootKeys,
note: state.rootUser.note,
),
));
}
} else {
final result = await api.addUserSshKey(user, publicKey);
if (result.isSuccess) {
// If it is primary user, update primary user
if (user.login == state.primaryUser.login) {
List<String> primaryUserKeys =
List<String>.from(state.primaryUser.sshKeys);
primaryUserKeys.add(publicKey);
final updatedUser = User(
login: state.primaryUser.login,
isFoundOnServer: true,
password: state.primaryUser.password,
sshKeys: primaryUserKeys,
note: state.primaryUser.note,
);
configBox.put(BNames.rootUser, updatedUser);
emit(state.copyWith(
primaryUser: updatedUser,
));
} else {
// If it is not primary user, update user
List<String> userKeys = List<String>.from(user.sshKeys);
userKeys.add(publicKey);
final updatedUser = User(
login: user.login,
isFoundOnServer: true,
password: user.password,
sshKeys: userKeys,
note: user.note,
);
await box.putAt(box.values.toList().indexOf(user), updatedUser);
emit(state.copyWith(
users: box.values.toList(),
));
}
}
}
}
Future<void> deleteSshKey(User user, String publicKey) async {
// All keys are deleted via api.deleteUserSshKey
final result = await api.deleteUserSshKey(user, publicKey);
if (result.isSuccess) {
// If it is root user, delete key from root keys
// If it is primary user, update primary user
// If it is not primary user, update user
if (user.login == 'root') {
final rootKeys =
configBox.get(BNames.rootKeys, defaultValue: []) as List<String>;
rootKeys.remove(publicKey);
configBox.put(BNames.rootKeys, rootKeys);
emit(state.copyWith(
rootUser: User(
login: state.rootUser.login,
isFoundOnServer: true,
password: state.rootUser.password,
sshKeys: rootKeys,
note: state.rootUser.note,
),
));
return;
}
if (user.login == state.primaryUser.login) {
List<String> primaryUserKeys =
List<String>.from(state.primaryUser.sshKeys);
primaryUserKeys.remove(publicKey);
final updatedUser = User(
login: state.primaryUser.login,
isFoundOnServer: true,
password: state.primaryUser.password,
sshKeys: primaryUserKeys,
note: state.primaryUser.note,
);
configBox.put(BNames.rootUser, updatedUser);
emit(state.copyWith(
primaryUser: updatedUser,
));
return;
}
List<String> userKeys = List<String>.from(user.sshKeys);
userKeys.remove(publicKey);
final updatedUser = User(
login: user.login,
isFoundOnServer: true,
password: user.password,
sshKeys: userKeys,
note: user.note,
);
await box.putAt(box.values.toList().indexOf(user), updatedUser);
emit(state.copyWith(
users: box.values.toList(),
));
}
}
@override
void clear() async {
emit(UsersState(<User>[], User(login: 'root'), User(login: 'loading...')));
}
}