import 'dart:async'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart'; import 'package:selfprivacy/logic/models/hive/server.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart'; import 'package:selfprivacy/logic/providers/backups_providers/backups_provider_factory.dart'; import 'package:selfprivacy/logic/providers/dns_providers/dns_provider_factory.dart'; import 'package:selfprivacy/logic/providers/provider_settings.dart'; import 'package:selfprivacy/logic/providers/providers_controller.dart'; import 'package:selfprivacy/logic/providers/server_providers/server_provider_factory.dart'; part 'tokens_event.dart'; part 'tokens_state.dart'; class TokensBloc extends Bloc { TokensBloc() : super(const TokensInitial()) { on( validateTokens, transformer: droppable(), ); on( addServerProviderCredential, ); on( connectServerToProviderToken, ); add(const RevalidateTokens()); _resourcesModelSubscription = getIt().statusStream.listen((final _) { add(const RevalidateTokens()); }); } Future validateTokens( final RevalidateTokens event, final Emitter emit, ) async { emit(const TokensInitial()); final List serverProviderCredentials = getIt().serverProviderCredentials; final List dnsProviderCredentials = getIt().dnsProviderCredentials; final List backupsCredentials = getIt().backupsCredentials; final List> validatedServerProviderCredentials = []; final List> validatedDnsProviderCredentials = []; final List> validatedBackupsCredentials = []; for (final credential in serverProviderCredentials) { final TokenStatus status = await _validateServerProviderToken(credential); validatedServerProviderCredentials .add(TokenStatusWrapper(data: credential, status: status)); } for (final credential in dnsProviderCredentials) { final TokenStatus status = await _validateDnsProviderToken(credential); validatedDnsProviderCredentials .add(TokenStatusWrapper(data: credential, status: status)); } for (final credential in backupsCredentials) { final TokenStatus status = await _validateBackupsToken(credential); validatedBackupsCredentials .add(TokenStatusWrapper(data: credential, status: status)); } emit( TokensChecked( serverProviderCredentials: validatedServerProviderCredentials, dnsProviderCredentials: validatedDnsProviderCredentials, backupsCredentials: validatedBackupsCredentials, ), ); } Future _validateServerProviderToken( final ServerProviderCredential credential, ) async { final ServerProviderSettings settings = ServerProviderSettings( provider: credential.provider, token: credential.token, isAuthorized: true, ); final serverProvider = ServerProviderFactory.createServerProviderInterface(settings); // First, we check if the token works at all final basicInitCheckResult = await serverProvider.tryInitApiByToken(credential.token); if (!basicInitCheckResult.data) { return TokenStatus.invalid; } // Now, if there are associated servers, check if we have access to them if (credential.associatedServerIds.isNotEmpty) { final servers = (await serverProvider.getServers()).data; for (final serverId in credential.associatedServerIds) { if (!servers.any((final server) => server.id == serverId)) { return TokenStatus.noAccess; } } } return TokenStatus.valid; } Future _validateDnsProviderToken( final DnsProviderCredential credential, ) async { final DnsProviderSettings settings = DnsProviderSettings( provider: credential.provider, token: credential.token, isAuthorized: true, ); final dnsProvider = DnsProviderFactory.createDnsProviderInterface(settings); final basicInitCheckResult = await dnsProvider.tryInitApiByToken(credential.token); if (!basicInitCheckResult.data) { return TokenStatus.invalid; } if (credential.associatedDomainNames.isNotEmpty) { final domains = (await dnsProvider.domainList()).data; for (final domainName in credential.associatedDomainNames) { if (!domains.any((final domain) => domain.domainName == domainName)) { return TokenStatus.noAccess; } } } return TokenStatus.valid; } Future _validateBackupsToken( final BackupsCredential credential, ) async { final BackupsProviderSettings settings = BackupsProviderSettings( provider: credential.provider, token: credential.applicationKey, tokenId: credential.keyId, isAuthorized: true, ); final backupsProvider = BackupsProviderFactory.createBackupsProviderInterface(settings); final basicInitCheckResult = await backupsProvider.tryInitApiByToken( encodedBackblazeKey( credential.keyId, credential.applicationKey, ), ); if (!basicInitCheckResult.data) { return TokenStatus.invalid; } return TokenStatus.valid; } Future addServerProviderCredential( final AddServerProviderToken event, final Emitter emit, ) async { await getIt() .addServerProviderToken(event.serverProviderCredential); final ServerProviderSettings settings = ServerProviderSettings( provider: event.serverProviderCredential.provider, token: event.serverProviderCredential.token, isAuthorized: true, ); ProvidersController.initServerProvider(settings); } Future connectServerToProviderToken( final ServerSelectedForProviderToken event, final Emitter emit, ) async { await getIt().associateServerWithToken( event.providerServer.id, event.serverProviderCredential.token, ); final Server newServerData = Server( domain: event.server.domain, hostingDetails: ServerHostingDetails( ip4: event.providerServer.ip, id: event.providerServer.id, createTime: event.providerServer.created, volume: ServerProviderVolume( id: 0, name: 'recovered_volume', sizeByte: 0, serverId: event.providerServer.id, linuxDevice: '', ), apiToken: event.server.hostingDetails.apiToken, provider: event.serverProviderCredential.provider, serverLocation: event.server.hostingDetails.serverLocation, serverType: event.server.hostingDetails.serverType, ), ); await getIt().updateServerByDomain(newServerData); } @override Future close() { _resourcesModelSubscription.cancel(); return super.close(); } late StreamSubscription _resourcesModelSubscription; }