mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-24 17:56:45 +00:00
NaiJi
4c99579f13
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
314 lines
10 KiB
Dart
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...')));
|
|
}
|
|
}
|