/* * Copyright 2014-2016 CyberVision, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaaproject.kaa.server.operations.service.user; import org.kaaproject.kaa.common.dto.EndpointProfileDto; import org.kaaproject.kaa.common.dto.EventClassFamilyVersionStateDto; import org.kaaproject.kaa.common.dto.user.UserVerifierDto; import org.kaaproject.kaa.common.hash.EndpointObjectHash; import org.kaaproject.kaa.server.common.Base64Util; import org.kaaproject.kaa.server.common.dao.EndpointService; import org.kaaproject.kaa.server.common.dao.UserVerifierService; import org.kaaproject.kaa.server.common.dao.exception.DatabaseProcessingException; import org.kaaproject.kaa.server.operations.service.cache.AppSeqNumber; import org.kaaproject.kaa.server.operations.service.cache.CacheService; import org.kaaproject.kaa.server.operations.service.cache.EventClassFqnKey; import org.kaaproject.kaa.server.operations.service.event.EventClassFqnVersion; import org.kaaproject.kaa.server.operations.service.event.RouteTableKey; import org.kaaproject.kaa.server.sync.EndpointAttachRequest; import org.kaaproject.kaa.server.sync.EndpointAttachResponse; import org.kaaproject.kaa.server.sync.EndpointDetachRequest; import org.kaaproject.kaa.server.sync.EndpointDetachResponse; import org.kaaproject.kaa.server.sync.EventListenersRequest; import org.kaaproject.kaa.server.sync.EventListenersResponse; import org.kaaproject.kaa.server.sync.SyncStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * The Class DefauleEndpointUserService is a default implementation of * {@link EndpointUserService EndpointUserService}. * * @author ashvayka */ public class DefaultEndpointUserService implements EndpointUserService { /** * The LOG constant. */ private static final Logger LOG = LoggerFactory.getLogger(DefaultEndpointUserService.class); /** * The application service. */ @Autowired private EndpointService endpointService; @Autowired private UserVerifierService userVerifierService; /** * The application service. */ @Autowired private CacheService cacheService; @Override public UserVerifierDto findUserVerifier(String appId, String verifierToken) { return userVerifierService.findUserVerifiersByAppIdAndVerifierToken(appId, verifierToken); } @Override public List<UserVerifierDto> findUserVerifiers(String appId) { return userVerifierService.findUserVerifiersByAppId(appId); } @Override public EndpointProfileDto attachEndpointToUser(EndpointProfileDto profile, String appToken, String userExternalId) { String tenantId = cacheService.getTenantIdByAppToken(appToken); return endpointService.attachEndpointToUser(userExternalId, tenantId, profile); } @Override public EndpointAttachResponse attachEndpoint(EndpointProfileDto profile, EndpointAttachRequest endpointAttachRequest) { EndpointAttachResponse response = new EndpointAttachResponse(); response.setRequestId(endpointAttachRequest.getRequestId()); response.setResult(SyncStatus.FAILURE); String endpointUserId = profile.getEndpointUserId(); if (isNotEmpty(endpointUserId)) { try { EndpointProfileDto attachedEndpoint = endpointService.attachEndpointToUser( endpointUserId, endpointAttachRequest.getEndpointAccessToken()); response.setResult(SyncStatus.SUCCESS); response.setEndpointKeyHash(Base64Util.encode(attachedEndpoint.getEndpointKeyHash())); } catch (DatabaseProcessingException ex) { LOG.warn("[{}] failed to attach endpoint with access token {} and " + "user {}, exception catched: {}", Base64Util.encode(profile.getEndpointKeyHash()), endpointAttachRequest.getEndpointAccessToken(), profile.getEndpointUserId(), ex); } } else { LOG.warn("[{}] received attach endpoint request, but there is no user to attach.", Base64Util.encode(profile.getEndpointKeyHash())); } return response; } @Override public EndpointDetachResponse detachEndpoint(EndpointProfileDto profile, EndpointDetachRequest endpointDetachRequest) { EndpointDetachResponse response = new EndpointDetachResponse(); response.setRequestId(endpointDetachRequest.getRequestId()); response.setResult(SyncStatus.FAILURE); if (isValid(endpointDetachRequest) && isNotEmpty(profile.getEndpointUserId())) { try { byte[] endpointKeyHash = Base64Util.decode(endpointDetachRequest.getEndpointKeyHash()); if (Arrays.equals(profile.getEndpointKeyHash(), endpointKeyHash)) { endpointService.detachEndpointFromUser(profile); response.setResult(SyncStatus.SUCCESS); } else { EndpointProfileDto detachEndpoint = endpointService.findEndpointProfileByKeyHash( endpointKeyHash); if (detachEndpoint != null) { if (detachEndpoint.getEndpointUserId() != null && detachEndpoint.getEndpointUserId().equals(profile.getEndpointUserId())) { endpointService.detachEndpointFromUser(detachEndpoint); response.setResult(SyncStatus.SUCCESS); } else { LOG.warn("[{}] received detach endpoint request, " + "but requested {} and current {} user mismatch.", Base64Util.encode(profile.getEndpointKeyHash()), profile.getEndpointUserId(), detachEndpoint.getEndpointUserId()); } } else { LOG.warn("[{}] received detach endpoint request, for not existing endpoint.", Base64Util.encode(profile.getEndpointKeyHash())); } } } catch (DatabaseProcessingException ex) { LOG.warn("[{}] failed to detach endpoint {}, exception catched: ", profile, ex); } } else { LOG.warn("[{}] detach endpoint request {} or profile {} is not valid", Base64Util.encode(profile.getEndpointKeyHash()), endpointDetachRequest, profile); } return response; } protected boolean isValid(EndpointDetachRequest endpointDetachRequest) { return endpointDetachRequest.getEndpointKeyHash() != null && !endpointDetachRequest.getEndpointKeyHash().isEmpty(); } protected boolean isNotEmpty(String userId) { return userId != null && !userId.isEmpty(); } @Override public EventListenersResponse findListeners(EndpointProfileDto profile, String appToken, EventListenersRequest request) { if (profile.getEndpointUserId() == null || profile.getEndpointUserId().isEmpty()) { LOG.info("Can't find listeners for unassigned endpoint!"); return new EventListenersResponse(request.getRequestId(), null, SyncStatus.FAILURE); } List<EndpointProfileDto> endpointProfiles = endpointService.findEndpointProfilesByUserId( profile.getEndpointUserId()); if (endpointProfiles.size() <= 1) { LOG.info("There is only one endpoint(current) assigned to this user!"); List<String> emptyList = Collections.emptyList(); return new EventListenersResponse(request.getRequestId(), emptyList, SyncStatus.SUCCESS); } String tenantId = cacheService.getTenantIdByAppToken(appToken); Set<EndpointObjectHash> eventClassIntersectionSet = null; for (String eventClassFqn : request.getEventClassFqns()) { Set<EndpointObjectHash> eventClassSet = new HashSet<>(); LOG.debug("Lookup event class family id using tenant [{}] and event class fqn {}", tenantId, eventClassFqn); String ecfId = cacheService.getEventClassFamilyIdByEventClassFqn( new EventClassFqnKey(tenantId, eventClassFqn)); int version = 0; for (EventClassFamilyVersionStateDto ecfVersionDto : profile.getEcfVersionStates()) { if (ecfVersionDto.getEcfId().equals(ecfId)) { version = ecfVersionDto.getVersion(); break; } } if (version > 0) { LOG.debug("Load recepient keys using tenant [{}] event class {} and version {}", tenantId, eventClassFqn, version); Set<RouteTableKey> recipientKeys = cacheService.getRouteKeys( new EventClassFqnVersion(tenantId, eventClassFqn, version)); for (EndpointProfileDto endpointProfile : endpointProfiles) { if (endpointProfile.getId().equals(profile.getId())) { continue; } for (RouteTableKey routeTableKey : recipientKeys) { AppSeqNumber endpointProfileSeqNumber = cacheService.getAppSeqNumber( routeTableKey.getAppToken()); if (!endpointProfile.getApplicationId().equals(endpointProfileSeqNumber.getAppId())) { continue; } for (EventClassFamilyVersionStateDto ecfVersionDto : profile.getEcfVersionStates()) { if (ecfVersionDto.getEcfId().equals(routeTableKey.getEcfVersion().getEcfId()) && ecfVersionDto.getVersion() == routeTableKey.getEcfVersion().getVersion()) { eventClassSet.add( EndpointObjectHash.fromBytes(endpointProfile.getEndpointKeyHash())); } } } } } else { LOG.warn("Lookup event class family version using tenant [{}] and event class fqn {} " + "FAILED!", tenantId, eventClassFqn); } if (eventClassIntersectionSet == null) { eventClassIntersectionSet = eventClassSet; } else { eventClassIntersectionSet.retainAll(eventClassSet); } } List<String> result = new ArrayList<>(); for (EndpointObjectHash eoHash : eventClassIntersectionSet) { result.add(Base64Util.encode(eoHash.getData())); } return new EventListenersResponse(request.getRequestId(), result, SyncStatus.SUCCESS); } }