Java tutorial
/* * Copyright 2017 Atypon Systems, 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 com.atypon.wayf.facade.impl; import com.atypon.wayf.dao.DeviceDao; import com.atypon.wayf.data.ServiceException; import com.atypon.wayf.data.authentication.AuthenticatedEntity; import com.atypon.wayf.data.device.Device; import com.atypon.wayf.data.device.DeviceQuery; import com.atypon.wayf.data.device.DeviceStatus; import com.atypon.wayf.data.device.access.DeviceAccess; import com.atypon.wayf.data.device.access.DeviceAccessQuery; import com.atypon.wayf.data.device.access.DeviceAccessType; import com.atypon.wayf.data.identity.IdentityProviderUsage; import com.atypon.wayf.data.publisher.Publisher; import com.atypon.wayf.facade.*; import com.atypon.wayf.reactivex.FacadePolicies; import com.atypon.wayf.request.RequestContextAccessor; import com.google.common.collect.Lists; import com.google.common.hash.Hashing; import com.google.inject.Inject; import com.google.inject.Singleton; import io.reactivex.Completable; import io.reactivex.Observable; import io.reactivex.Single; import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.UUID; import static com.atypon.wayf.reactivex.FacadePolicies.singleOrException; @Singleton public class DeviceFacadeImpl implements DeviceFacade { private static final Logger LOG = LoggerFactory.getLogger(DeviceFacadeImpl.class); @Inject private DeviceDao deviceDao; @Inject private DeviceAccessFacade deviceAccessFacade; @Inject private PublisherFacade publisherFacade; @Inject private IdentityProviderUsageFacade identityProviderUsageFacade; @Inject private CryptFacade cryptFacade; public DeviceFacadeImpl() { } @Override public Single<Device> create(Device device) { LOG.debug("Creating device [{}]", device); device.setStatus(DeviceStatus.ACTIVE); // Generate a global ID String globalId = UUID.randomUUID().toString(); String hashedGlobalId = hashGlobalId(globalId); device.setGlobalId(hashedGlobalId); // Create the device return deviceDao.create(device).map(device1 -> { device1.setGlobalId(globalId); return device1; }); } @Override public Single<Device> read(DeviceQuery query) { LOG.debug("Reading device with query [{}]", query); return singleOrException(deviceDao.read(query), HttpStatus.SC_NOT_FOUND, "Invalid Global ID") .flatMap((device) -> populate(device, query).toSingle(() -> device)); } @Override public Observable<Device> filter(DeviceQuery query) { LOG.debug("Filtering for devices that match query [{}]", query); return deviceDao.filter(query); } @Override public Single<Device> relateLocalIdToDevice(Publisher publisher, String localId) { return resolveFromRequest(publisher, localId) // Create a DeviceAccess object to store the resolved Device and Publisher. This may be // a little clunky but the alternative would be to create a new type to wrap the two .map((device) -> new DeviceAccess.Builder().device(device).publisher(publisher) .type(DeviceAccessType.CREATE_IDP).build()) .flatMap((deviceAccess) -> // Now that all the required data is available, point the local ID at the device deviceAccessFacade.create(deviceAccess).flatMap( deviceAccess1 -> deviceDao.updateDevicePublisherLocalIdXref(deviceAccess.getDevice().getId(), deviceAccess.getPublisher().getId(), localId).map((numAffectedRows) -> { if (numAffectedRows != 1) { throw new ServiceException(HttpStatus.SC_NOT_FOUND, "Could not find local ID"); } return deviceAccess.getDevice(); }))); } @Override public Completable registerLocalId(String localId) { return deviceDao.registerLocalId(AuthenticatedEntity .authenticatedAsPublisher(RequestContextAccessor.get().getAuthenticated()).getId(), localId); } private Single<Device> resolveFromRequest(Publisher publisher, String localId) { String deviceGlobalId = RequestContextAccessor.get().getDeviceId(); String hashedDeviceId = hashGlobalId(deviceGlobalId); LOG.debug("Found global ID from client [{}]", deviceGlobalId); if (deviceGlobalId != null) { return read(new DeviceQuery().setGlobalId(hashedDeviceId)).flatMap(device -> { device.setGlobalId(deviceGlobalId); return Single.just(device); }); } // If the localId has already been used, return the device associated with it. Otherwise, create a new Device return deviceDao.readByPublisherLocalId(publisher.getId(), localId) .concatWith(create(new Device()).toMaybe()).firstOrError(); } @Override public Single<Device> readByLocalId(String localId) { LOG.debug("Reading device with local ID [{}]", localId); Publisher publisher = AuthenticatedEntity .authenticatedAsPublisher(RequestContextAccessor.get().getAuthenticated()); LOG.debug("Reading device with local ID [{}] and publisher ID [{}]", localId, publisher.getId()); return singleOrException(deviceDao.readByPublisherLocalId(publisher.getId(), localId), HttpStatus.SC_NOT_FOUND, "Invalid local ID"); } private Completable populate(Device device, DeviceQuery query) { return Completable.mergeArray(inflateActivity(device, query), inflateHistory(device, query)); } private Completable inflateActivity(Device device, DeviceQuery query) { // Return as complete if authenticatedBy is not a requested field if (query.getInflationPolicy() == null || !query.getInflationPolicy().hasChildField(DeviceQuery.ACTIVITY)) { return Completable.complete(); } return deviceAccessFacade .filter(new DeviceAccessQuery().setDeviceIds(Lists.newArrayList(device.getId())) .setInflationPolicy(query.getInflationPolicy().getChildPolicy(DeviceQuery.ACTIVITY))) .toList() .flatMapCompletable((activity) -> Completable.fromAction(() -> device.setActivity(activity))); } private Completable inflateHistory(Device device, DeviceQuery query) { // Return as complete if authenticatedBy is not a requested field if (query.getInflationPolicy() == null || !query.getInflationPolicy().hasChildField(DeviceQuery.HISTORY)) { return Completable.complete(); } return Completable.fromAction(() -> { List<IdentityProviderUsage> history = identityProviderUsageFacade.buildRecentHistory(device); device.setHistory(history); }); } @Override public String encryptLocalId(Long publisherId, String localId) { return cryptFacade.encrypt(publisherFacade.getPublishersSalt(publisherId), localId); } @Override public Completable deleteDevice(Long deviceId) { return deviceDao.delete(deviceId).compose(FacadePolicies::applyCompletable) .andThen(deviceAccessFacade.delete(deviceId)).compose(FacadePolicies::applyCompletable); } @Override public String hashGlobalId(String globalId) { return globalId == null ? null : Hashing.sha256().hashString(globalId, StandardCharsets.UTF_8).toString(); } }