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 { UsersCubit(AppConfigCubit appConfigCubit) : super( appConfigCubit, UsersState( [], User(login: 'root'), User(login: 'loading...'))); Box box = Hive.box(BNames.users); Box configBox = Hive.box(BNames.appConfig); final api = ServerApi(); Future load() async { if (appConfigCubit.state is AppConfigFinished) { var loadedUsers = box.values.toList(); final primaryUser = configBox.get(BNames.rootUser, defaultValue: User(login: 'loading...')); List 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 mergeLocalAndServerUsers( List localUsers, List serverUsers) { // If local user not exists on server, add it with isFoundOnServer = false // If server user not exists on local, add it List mergedUsers = []; List 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> loadSshKeys(List users) async { List 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 refresh() async { List updatedUsers = List.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 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.from(state.users); loadedUsers.add(result.data); await box.clear(); await box.addAll(loadedUsers); emit(state.copyWith(users: loadedUsers)); } Future 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.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 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; 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 primaryUserKeys = List.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 userKeys = List.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 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; 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 primaryUserKeys = List.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 userKeys = List.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(login: 'root'), User(login: 'loading...'))); } }