package im.rosetta.executors; import im.rosetta.Failures; import im.rosetta.client.ClientManager; import im.rosetta.client.tags.ECIAuthentificate; import im.rosetta.client.tags.ECIDevice; import im.rosetta.database.entity.Device; import im.rosetta.database.entity.User; import im.rosetta.database.repository.BufferRepository; import im.rosetta.database.repository.DeviceRepository; import im.rosetta.database.repository.UserRepository; import im.rosetta.event.EventManager; import im.rosetta.event.events.handshake.HandshakeCompletedEvent; import im.rosetta.event.events.handshake.HandshakeDeviceConfirmEvent; import im.rosetta.event.events.handshake.HandshakeFailedEvent; import im.rosetta.packet.Packet0Handshake; import im.rosetta.packet.Packet9DeviceNew; import im.rosetta.packet.runtime.HandshakeStage; import im.rosetta.service.services.BufferService; import im.rosetta.service.services.DeviceService; import io.orprotocol.ProtocolException; import io.orprotocol.client.Client; import io.orprotocol.lock.Lock; import io.orprotocol.packet.PacketExecutor; import io.orprotocol.packet.PacketManager; public class Executor0Handshake extends PacketExecutor { private final UserRepository userRepository = new UserRepository(); private final DeviceRepository deviceRepository = new DeviceRepository(); private final DeviceService deviceService = new DeviceService(deviceRepository); private final EventManager eventManager; private final ClientManager clientManager; private final BufferRepository bufferRepository = new BufferRepository(); private final BufferService bufferService; public Executor0Handshake(EventManager eventManager, ClientManager clientManager, PacketManager packetManager) { this.eventManager = eventManager; this.clientManager = clientManager; this.bufferService = new BufferService(bufferRepository, packetManager); } @Override @Lock(lockFor = "publicKey") public void onPacketReceived(Packet0Handshake handshake, Client client) throws ProtocolException { String publicKey = handshake.getPublicKey(); String privateKey = handshake.getPrivateKey(); String deviceId = handshake.getDeviceId(); String deviceName = handshake.getDeviceName(); String deviceOs = handshake.getDeviceOs(); int protocolVersion = handshake.getProtocolVersion(); /** * Получаем информацию об аутентификации клиента * используя возможности ECI тэгов. */ ECIAuthentificate authentificate = client.getTag(ECIAuthentificate.class); if(authentificate != null && authentificate.hasAuthorized()) { /** * Клиент уже авторизован, повторный хэндшейк не допускается */ return; } /** * Проверяем корректность версии протокола */ if(protocolVersion != 1) { client.disconnect(Failures.UNSUPPORTED_PROTOCOL); return; } /** * Создаем минимальную информацию об устройстве клиента */ ECIDevice device = new ECIDevice(deviceId, deviceName, deviceOs); client.addTag(ECIDevice.class, device); /** * Проверяем есть ли такой пользователь */ User user = userRepository.findByField("publicKey", publicKey); if(user == null) { /** * Пользователь не найден, создаем нового */ user = new User(); user.setPrivateKey(privateKey); user.setPublicKey(publicKey); user.setUsername(""); user.setTitle(publicKey.substring(0, 7)); /** * Новый пользователь не верифицирован */ user.setVerified(0); userRepository.save(user); /** * Это первое устройство пользователя, сохраняем его * как верифицированное */ Device newDevice = new Device(); newDevice.setDeviceId(deviceId); newDevice.setDeviceName(deviceName); newDevice.setDeviceOs(deviceOs); newDevice.setPublicKey(publicKey); newDevice.setSyncTime(System.currentTimeMillis()); deviceRepository.save(newDevice); /** * Ставим метку аутентификации на клиента */ ECIAuthentificate eciTag = new ECIAuthentificate (publicKey, privateKey, HandshakeStage.COMPLETED); client.addTag(ECIAuthentificate.class, eciTag); /** * Вызываем событие завершения хэндшейка */ boolean cancelled = this.eventManager.callEvent( new HandshakeCompletedEvent(publicKey, privateKey, device, eciTag, client) ); if(cancelled) { /** * Событие было отменено, не даем завершить хэндшейк */ client.disconnect(Failures.DATA_MISSMATCH); return; } /** * Отправляем клиенту подтверждение успешного хэндшейка */ handshake.setHandshakeStage(HandshakeStage.COMPLETED); handshake.setHeartbeatInterval(this.settings.heartbeatInterval); client.send(handshake); return; } /** * Пользователь найден, проверяем приватный ключ */ if(!user.getPrivateKey().equals(privateKey)){ /** * Приватный ключ не совпадает, отключаем клиента */ eventManager.callEvent(new HandshakeFailedEvent(publicKey, privateKey, device, authentificate, client)); client.disconnect(Failures.AUTHENTIFICATION_ERROR); return; } long userDevicesCount = deviceRepository.countUserDevices(user); /** * Проверяем верифицировано ли устройство */ if(userDevicesCount > 0 && !deviceService.isDeviceVerifiedByUser(deviceId, user)) { /** * Устройство не верифицировано, нужно отправить клиента * на подтверждение устройства */ handshake.setHandshakeStage(HandshakeStage.NEED_DEVICE_VERIFICATION); handshake.setHeartbeatInterval(this.settings.heartbeatInterval); /** * Ставим метку аутентификации на клиента */ ECIAuthentificate eciTag = new ECIAuthentificate (publicKey, privateKey, HandshakeStage.NEED_DEVICE_VERIFICATION); client.addTag(ECIAuthentificate.class, eciTag); /** * Вызываем событие подтверждения устройства */ this.eventManager.callEvent( new HandshakeDeviceConfirmEvent(publicKey, privateKey, device, authentificate, client) ); /** * Отправляем клиенту информацию о необходимости * подтверждения устройства */ client.send(handshake); /** * Уведомляем все авторизованные устройства пользователя о том, что нужно подтвердить новое устройство */ Packet9DeviceNew newDevicePacket = new Packet9DeviceNew(); newDevicePacket.setDeviceId(deviceId); newDevicePacket.setDeviceName(deviceName); newDevicePacket.setDeviceOs(deviceOs); newDevicePacket.setIpAddress(client.getIpAddress()); clientManager.sendPacketToAuthorizedPK(publicKey, newDevicePacket); /** * Сбрасываем клиенту все старые подтверждения устройств, чтобы исключить спам запросами */ this.bufferService.deletePacketsFromBuffer(publicKey, newDevicePacket, 0); /** * Кладем пакет в очередь на все устройства пользователя, * чтобы если в момент отправки этого пакета какое-то устройство было не онлайн, * то когда оно зайдет в сеть, то получит этот пакет и сможет отреагировать на него, * показав пользователю уведомление о том, что нужно подтвердить новое устройство */ this.bufferService.pushPacketToBuffer("server", publicKey, newDevicePacket); return; } /** * Ставим метку аутентификации на клиента */ ECIAuthentificate eciTag = new ECIAuthentificate (publicKey, privateKey, HandshakeStage.COMPLETED); client.addTag(ECIAuthentificate.class, eciTag); /** * Вызываем событие завершения хэндшейка */ boolean cancelled = this.eventManager.callEvent( new HandshakeCompletedEvent(publicKey, privateKey, device, eciTag, client) ); if(cancelled) { /** * Событие было отменено, не даем завершить хэндшейк */ client.disconnect(Failures.DATA_MISSMATCH); return; } /** * Отправляем клиенту подтверждение успешного хэндшейка */ handshake.setHandshakeStage(HandshakeStage.COMPLETED); handshake.setHeartbeatInterval(this.settings.heartbeatInterval); client.send(handshake); } }