From 91461f4a3e30a71ba2f905481d9872599dd1e72a Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Tue, 26 Dec 2023 15:55:56 +0100 Subject: [PATCH] Implement message registry and minor bug fixes. --- lib/jul | 2 +- lib/type | 2 +- .../context/AbstractBCOGraphQLContext.kt | 2 +- .../graphql/schema/RegistrySchemaModule.kt | 91 +++- .../subscriptions/SubscriptionModule.kt | 12 +- .../bco/app/preset/DeviceNotificationApp.kt | 149 +++++++ .../openbase/bco/app/preset/TemplateApp.kt | 67 +++ .../app/preset/DeviceNotificationAppTest.kt | 112 +++++ .../test/agent/AbstractBCOAppManagerTest.java | 91 ---- .../test/agent/AbstractBCOAppManagerTest.kt | 124 ++++++ .../openbase/app/test/agent/BCOAppTest.java | 6 + .../bco/app/util/launch/BCOLauncher.java | 8 +- .../bco/app/util/launch/BCOTestLauncher.java | 9 +- .../AbstractAuthorizedBaseUnitController.java | 15 +- .../control/layer/unit/InfluxDbProcessor.java | 8 +- .../unit/app/AppControllerFactoryImpl.java | 22 +- .../layer/unit/scene/SceneControllerImpl.java | 19 +- .../bco/dal/control/message/MessageManager.kt | 106 +++++ .../control/message/MessageManagerLauncher.kt | 32 ++ .../layer/service/ServiceStateProcessor.java | 26 +- .../openbase/bco/dal/lib/state/States.java | 18 +- .../bco/dal/remote/action/RemoteAction.java | 5 + .../dal/remote/detector/PresenceDetector.java | 4 +- .../dal/remote/layer/unit/CustomUnitPool.kt | 10 +- .../remote/layer/unit/CustomUnitPoolTest.java | 79 ---- .../remote/layer/unit/CustomUnitPoolTest.kt | 77 ++++ .../bco/dal/test/action/KotlinTest.kt | 4 - .../bco/device/openhab/jp/JPOpenHABURI.java | 9 +- .../lib/com/AbstractRegistryController.java | 6 +- .../core/MessageRegistryController.java | 79 ++-- .../message/remote/MessageRegistryRemote.java | 34 +- .../MessageRegistryRemoteAuthExtensions.kt | 39 ++ ...tionGroupClassGroupConsistencyHandler.java | 9 +- .../auth/AuthorizationWithTokenHelper.java | 347 --------------- .../lib/auth/AuthorizationWithTokenHelper.kt | 421 ++++++++++++++++++ .../bco/registry/mock/MockRegistry.java | 28 +- 36 files changed, 1424 insertions(+), 648 deletions(-) create mode 100644 module/app/preset/src/main/java/org/openbase/bco/app/preset/DeviceNotificationApp.kt create mode 100644 module/app/preset/src/main/java/org/openbase/bco/app/preset/TemplateApp.kt create mode 100644 module/app/preset/src/test/java/org/openbase/bco/app/preset/DeviceNotificationAppTest.kt delete mode 100644 module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.java create mode 100644 module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.kt create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManagerLauncher.kt delete mode 100644 module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.java create mode 100644 module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.kt delete mode 100644 module/dal/test/src/test/java/org/openbase/bco/dal/test/action/KotlinTest.kt create mode 100644 module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemoteAuthExtensions.kt delete mode 100644 module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.java create mode 100644 module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt diff --git a/lib/jul b/lib/jul index 00675ad064..7296390421 160000 --- a/lib/jul +++ b/lib/jul @@ -1 +1 @@ -Subproject commit 00675ad06484adf749ae3b4231820c0f6bdd2cc5 +Subproject commit 72963904216da0c780dc950d8a17499a46195900 diff --git a/lib/type b/lib/type index 99e866c784..05eba9bc57 160000 --- a/lib/type +++ b/lib/type @@ -1 +1 @@ -Subproject commit 99e866c78450e7ae410f2bc594a239b5b53ab970 +Subproject commit 05eba9bc579a8b7fcd006eda89f462d11a6a0fd1 diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt index 5dc614b01f..f43f611efd 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/context/AbstractBCOGraphQLContext.kt @@ -34,7 +34,7 @@ abstract class AbstractBCOGraphQLContext( abstract val languageCode: String? val auth: AuthTokenType.AuthToken? - get() = AuthTokenType.AuthToken.newBuilder().setAuthenticationToken(token).build() + get() = token?.let { AuthTokenType.AuthToken.newBuilder().setAuthenticationToken(it).build() } companion object { const val DATA_LOADER_UNITS = "units" diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt index a85d3d1034..53c5c4f24b 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/schema/RegistrySchemaModule.kt @@ -10,6 +10,9 @@ import org.openbase.bco.api.graphql.error.GenericError import org.openbase.bco.api.graphql.error.ServerError import org.openbase.bco.authentication.lib.SessionManager import org.openbase.bco.authentication.lib.iface.BCOSession +import org.openbase.bco.registry.message.remote.registerUserMessageAuthenticated +import org.openbase.bco.registry.message.remote.removeUserMessageAuthenticated +import org.openbase.bco.registry.message.remote.updateUserMessageAuthenticated import org.openbase.bco.registry.remote.Registries import org.openbase.bco.registry.remote.session.BCOSessionImpl import org.openbase.bco.registry.unit.remote.registerUnitConfigAuthenticated @@ -407,7 +410,7 @@ class RegistrySchemaModule : SchemaModule() { break } } - if (!entry.value.isEmpty()) { + if (entry.value.isNotEmpty()) { metaConfigBuilder.addEntry(entry) } @@ -425,6 +428,92 @@ class RegistrySchemaModule : SchemaModule() { throw GenericError(ex) } + @Mutation("updateUserMessage") + @Throws(BCOGraphQLError::class) + fun updateUserMessage( + @Arg("userMessage") userMessage: UserMessage, + env: DataFetchingEnvironment, + ): UserMessage = try { + val userMessageBuilder = Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ) + .getUserMessageById(userMessage.id) + .toBuilder() + userMessageBuilder.mergeFromWithoutRepeatedFields(userMessage) + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).updateUserMessageAuthenticated( + userMessageBuilder.build(), + env.context.auth + )[ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT] + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) + } catch (ex: ExecutionException) { + throw GenericError(ex) + } catch (ex: TimeoutException) { + throw GenericError(ex) + } + + @Mutation("removeUserMessage") + @Throws(BCOGraphQLError::class) + fun removeUserMessage( + @Arg("unitId") unitId: String?, + env: DataFetchingEnvironment, + ): UserMessage = try { + val userMessage = Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).getUserMessageById(unitId) + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).removeUserMessageAuthenticated( + userMessage, + env.context.auth + )[ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT] + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) + } catch (ex: ExecutionException) { + throw GenericError(ex) + } catch (ex: TimeoutException) { + throw GenericError(ex) + } + + @Mutation("registerUserMessage") + @Throws(BCOGraphQLError::class) + fun registerUserMessage( + @Arg("userMessage") userMessage: UserMessage?, + env: DataFetchingEnvironment, + ): UserMessage = try { + Registries.getMessageRegistry( + ServerError.BCO_TIMEOUT_SHORT, + ServerError.BCO_TIMEOUT_TIME_UNIT + ).registerUserMessageAuthenticated( + userMessage, + env.context.auth + )[ServerError.BCO_TIMEOUT_SHORT, ServerError.BCO_TIMEOUT_TIME_UNIT] + } catch (ex: RuntimeException) { + throw GenericError(ex) + } catch (ex: CouldNotPerformException) { + throw GenericError(ex) + } catch (ex: InterruptedException) { + throw GenericError(ex) + } catch (ex: ExecutionException) { + throw GenericError(ex) + } catch (ex: TimeoutException) { + throw GenericError(ex) + } + companion object { @Throws(BCOGraphQLError::class) fun getUnitConfigs( diff --git a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt index a877304693..26fadb2a03 100644 --- a/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt +++ b/module/api/graphql/src/main/java/org/openbase/bco/api/graphql/subscriptions/SubscriptionModule.kt @@ -8,6 +8,7 @@ import org.openbase.bco.api.graphql.error.GenericError import org.openbase.bco.api.graphql.error.ServerError import org.openbase.bco.api.graphql.schema.RegistrySchemaModule import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.lib.layer.unit.UnitRemote import org.openbase.bco.dal.remote.layer.unit.CustomUnitPool import org.openbase.bco.registry.remote.Registries import org.openbase.jul.exception.CouldNotPerformException @@ -20,7 +21,6 @@ import org.openbase.type.domotic.unit.UnitDataType import org.openbase.type.domotic.unit.UnitFilterType.UnitFilter import org.reactivestreams.Publisher import org.slf4j.LoggerFactory -import java.util.function.Consumer /*- * #%L @@ -51,15 +51,15 @@ import java.util.function.Consumer @Throws(BCOGraphQLError::class) fun subscribeUnits(unitFilter: UnitFilter): Publisher { return try { - val subscriptionUnitPool = CustomUnitPool() + val subscriptionUnitPool = CustomUnitPool>() subscriptionUnitPool.init(unitFilter) AbstractObserverMapper.createObservable( - Consumer { observer: Observer, Message> -> + { observer: Observer, Message> -> subscriptionUnitPool.addDataObserver( observer ) }, - Consumer { observer: Observer, Message> -> + { observer: Observer, Message> -> subscriptionUnitPool.removeDataObserver(observer) }, object : AbstractObserverMapper, Message, UnitDataType.UnitData>() { @@ -100,12 +100,12 @@ import java.util.function.Consumer ServerError.BCO_TIMEOUT_TIME_UNIT ) AbstractObserverMapper.createObservable( - Consumer { observer: Observer, UnitRegistryData> -> + { observer: Observer, UnitRegistryData> -> unitRegistry.addDataObserver( observer ) }, - Consumer { observer: Observer, UnitRegistryData> -> + { observer: Observer, UnitRegistryData> -> unitRegistry.removeDataObserver(observer) }, observer diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/DeviceNotificationApp.kt b/module/app/preset/src/main/java/org/openbase/bco/app/preset/DeviceNotificationApp.kt new file mode 100644 index 0000000000..7cdb269a5d --- /dev/null +++ b/module/app/preset/src/main/java/org/openbase/bco/app/preset/DeviceNotificationApp.kt @@ -0,0 +1,149 @@ +package org.openbase.bco.app.preset + +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.bco.dal.lib.layer.service.ServiceStateProcessor +import org.openbase.bco.dal.remote.layer.unit.BatteryRemote +import org.openbase.bco.dal.remote.layer.unit.CustomUnitPool +import org.openbase.bco.registry.message.remote.registerUserMessageAuthenticated +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.extension.type.processing.LabelProcessor +import org.openbase.jul.extension.type.processing.MultiLanguageTextProcessor +import org.openbase.jul.extension.type.processing.TimestampProcessor +import org.openbase.jul.schedule.GlobalScheduledExecutorService +import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription +import org.openbase.type.domotic.communication.UserMessageType.UserMessage +import org.openbase.type.domotic.service.ServiceStateDescriptionType.ServiceStateDescription +import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType +import org.openbase.type.domotic.state.ActivationStateType.ActivationState +import org.openbase.type.domotic.state.BatteryStateType.BatteryState +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.UnitFilterType.UnitFilter +import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType +import org.openbase.type.domotic.unit.dal.BatteryDataType.BatteryData +import org.openbase.type.language.MultiLanguageTextType.MultiLanguageText +import org.slf4j.LoggerFactory +import java.time.Duration +import java.util.* +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit + +/** + * An app that notifies users about devices with empty batteries or offline states. + */ +class DeviceNotificationApp : AbstractAppController() { + + private val batteryPool = CustomUnitPool() + private var task: ScheduledFuture<*>? = null + + init { + batteryPool.init( + UnitFilter.newBuilder().setProperties(UnitConfig.newBuilder().setUnitType(UnitType.BATTERY)).build() + ) + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun execute(activationState: ActivationState): ActionDescription = + activationState.responsibleAction.also { + batteryPool.activate() + task?.cancel(false) + task = GlobalScheduledExecutorService.scheduleWithFixedDelay( + ::checkDevices, + INITIAL_VALIDATION_DELAY.toMillis(), + VALIDATION_PERIOD.toMillis(), + TimeUnit.MILLISECONDS + ) + LOGGER.trace(getLabel() + " is running.") + } + + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun stop(activationState: ActivationState) = + super.stop(activationState).also { + batteryPool.deactivate() + task?.cancel(true) + LOGGER.trace(getLabel() + " has been stopped.") + } + + fun checkDevices() { + try { + logger.error("#@# check device states") + batteryPool.internalUnitList + .onEach { remote -> remote.waitForData() } + .filter { remote -> + when (remote.data.batteryState.value) { + BatteryState.State.LOW, BatteryState.State.CRITICAL, BatteryState.State.UNKNOWN -> true + else -> false + } + } + .map { remote -> + val messageBuilder: UserMessage.Builder = UserMessage.newBuilder() + val textBuilder: MultiLanguageText.Builder = MultiLanguageText.newBuilder() + + MultiLanguageTextProcessor.addMultiLanguageText( + textBuilder, + Locale.ENGLISH, + "Battery level of ${ + LabelProcessor.getBestMatch( + Locale.ENGLISH, + remote.config.label + ) + } is ${remote.data.batteryState.value}" + ) + + MultiLanguageTextProcessor.addMultiLanguageText( + textBuilder, + Locale.GERMAN, + "Batteriezustand von ${ + LabelProcessor.getBestMatch( + Locale.GERMAN, + remote.config.label + ) + } ist ${remote.data.batteryState.value}" + ) + + messageBuilder.id = UUID.randomUUID().toString() + messageBuilder.messageType = UserMessage.MessageType.WARNING + messageBuilder.timestamp = TimestampProcessor.getCurrentTimestamp() + messageBuilder.text = textBuilder.build() + messageBuilder.senderId = userConfig.id + messageBuilder.addCondition( + with(ServiceStateDescription.newBuilder()) { + setUnitId(remote.id) + setServiceType(ServiceType.BATTERY_STATE_SERVICE) + setServiceStateClassName(BatteryState::class.java.name) + setServiceState( + ServiceStateProcessor.serializeServiceState( + BatteryState.newBuilder().setValue(remote.data.batteryState.value).build(), + false, + ) + ) + }.also { ServiceStateProcessor.deserializeServiceState(it) } + ) + + // validate if message already exist + messageBuilder.build() to Registries.getMessageRegistry() + .getUserMessagesByText( + MultiLanguageTextProcessor.getBestMatch( + Locale.ENGLISH, messageBuilder.text + ), Locale.ENGLISH + ).isNotEmpty() + } + .filterNot { (_, exist) -> exist } + .forEach { (message, _) -> + Registries.getMessageRegistry() + .registerUserMessageAuthenticated(message, token) + .get(5, TimeUnit.SECONDS) + } + } catch (e: CouldNotPerformException) { + ExceptionPrinter.printHistory("Could not check device states!", e, logger) + } + } + + companion object { + private val LOGGER = LoggerFactory.getLogger(TemplateApp::class.java) + + private val VALIDATION_PERIOD: Duration = Duration.ofHours(24) + private val INITIAL_VALIDATION_DELAY: Duration = Duration.ofHours(1) + } +} diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/TemplateApp.kt b/module/app/preset/src/main/java/org/openbase/bco/app/preset/TemplateApp.kt new file mode 100644 index 0000000000..13f5e0dbdb --- /dev/null +++ b/module/app/preset/src/main/java/org/openbase/bco/app/preset/TemplateApp.kt @@ -0,0 +1,67 @@ +package org.openbase.bco.app.preset + +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription +import org.openbase.type.domotic.state.ActivationStateType +import org.openbase.type.domotic.state.ActivationStateType.ActivationState +import org.openbase.type.domotic.unit.UnitConfigType +import org.slf4j.LoggerFactory + +/* +* #%L +* BCO App Preset +* %% +* Copyright (C) 2018 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program. If not, see +* . +* #L% +*/ /** + * A class that can be used as template for new apps. + */ +class TemplateApp : AbstractAppController() { + + private var executing = false + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun applyConfigUpdate(config: UnitConfigType.UnitConfig): UnitConfigType.UnitConfig = + getManageWriteLockInterruptible(this).use { + super.applyConfigUpdate(config).also { + LOGGER.info(getLabel() + " config has been changed.") + } + } + + override fun shutdown() = super.shutdown().also { + LOGGER.info(getLabel() + " is shutting down.") + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun execute(activationState: ActivationStateType.ActivationState): ActionDescription = + activationState.responsibleAction.also { + executing = true + LOGGER.info(getLabel() + " is running.") + } + + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun stop(activationState: ActivationState) = + super.stop(activationState).also { + executing = false + LOGGER.info(getLabel() + " has been stopped.") + } + + companion object { + private val LOGGER = LoggerFactory.getLogger(TemplateApp::class.java) + } +} diff --git a/module/app/preset/src/test/java/org/openbase/bco/app/preset/DeviceNotificationAppTest.kt b/module/app/preset/src/test/java/org/openbase/bco/app/preset/DeviceNotificationAppTest.kt new file mode 100644 index 0000000000..700afc72ea --- /dev/null +++ b/module/app/preset/src/test/java/org/openbase/bco/app/preset/DeviceNotificationAppTest.kt @@ -0,0 +1,112 @@ +package org.openbase.bco.app.preset + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test +import org.openbase.app.test.agent.AbstractBCOAppManagerTest +import org.openbase.bco.dal.control.layer.unit.BatteryController +import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.lib.state.States +import org.openbase.bco.dal.remote.layer.unit.BatteryRemote +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.dal.remote.layer.unit.util.UnitStateAwaiter +import org.openbase.bco.registry.mock.MockRegistry +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.extension.type.processing.MultiLanguageTextProcessor +import org.openbase.type.domotic.communication.UserMessageType.UserMessage +import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType +import org.openbase.type.domotic.state.ActivationStateType.ActivationState +import org.openbase.type.domotic.state.BatteryStateType.BatteryState +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.UnitTemplateType +import org.openbase.type.domotic.unit.app.AppDataType.AppData +import java.util.* +import java.util.concurrent.TimeUnit + +class DeviceNotificationAppTest : AbstractBCOAppManagerTest() { + + private val APP_ALIAS = "Device_Notification_App_Test" + override fun getAppClass() = DeviceNotificationApp::class.java + + override fun getAppConfig(): UnitConfig.Builder = MockRegistry.generateAppConfig( + APP_ALIAS, + MockRegistry.ALIAS_LOCATION_ROOT_PARADISE + ) + + private fun getUserMessagesOfUnit(unit: Unit<*>): List = run { + Registries.getMessageRegistry().requestData().get(5, TimeUnit.SECONDS) + }.let { + Registries.getMessageRegistry().userMessages + }.filter { it.conditionList.any { condition -> condition.unitId == unit.id } } + + @Test + fun testLowBatteryNotification() { + println("testAbsenceEnergySavingAgent") + + val unitStateAwaiter = UnitStateAwaiter(appRemote) + unitStateAwaiter.waitForState { data: AppData -> + data.activationState.getValue() == ActivationState.State.ACTIVE + } + + val location = Units.getUnitByAlias(MockRegistry.ALIAS_LOCATION_STAIRWAY_TO_HEAVEN, true, Units.LOCATION) + val batteryRemote: BatteryRemote = + location.getUnits(UnitTemplateType.UnitTemplate.UnitType.BATTERY, true, Units.BATTERY).first() + + val batteryStateAwaiter = UnitStateAwaiter(batteryRemote) + val batteryController = + deviceManagerLauncher.launchable!!.unitControllerRegistry.get(batteryRemote.getId()) as BatteryController + + // verify ground truth + getUserMessagesOfUnit(batteryRemote) shouldBeEqual emptyList() + + appController!!.checkDevices() +// messageManagerLauncher.launchable!!.removeOutdatedMessages() + Thread.sleep(2000) + + // verify unknown battery state notification + getUserMessagesOfUnit(batteryRemote).size shouldBeEqual 1 + + batteryController.applyServiceState(States.Battery.OK, ServiceType.BATTERY_STATE_SERVICE) + batteryStateAwaiter.waitForState { it.batteryState.value == BatteryState.State.OK } + batteryRemote.requestData().get(5, TimeUnit.SECONDS) + + + appController!!.checkDevices() +// messageManagerLauncher.launchable!!.removeOutdatedMessages() + + Thread.sleep(2000) + + // verify everything is ok again + getUserMessagesOfUnit(batteryRemote) shouldBeEqual emptyList() + + batteryController.applyServiceState(States.Battery.CRITICAL, ServiceType.BATTERY_STATE_SERVICE) + batteryStateAwaiter.waitForState { it.batteryState.value == BatteryState.State.CRITICAL } + + appController!!.checkDevices() + messageManagerLauncher.launchable!!.removeOutdatedMessages() + + // verify critical battery state notification + getUserMessagesOfUnit(batteryRemote) + .also { it.size shouldBeEqual 1 } + .first().apply { + messageType shouldBeEqual UserMessage.MessageType.WARNING + MultiLanguageTextProcessor.getBestMatch( + Locale.GERMAN, + text + ) shouldBeEqual "Batteriezustand von F Motion Sensor Device Stairway ist CRITICAL" + MultiLanguageTextProcessor.getBestMatch( + Locale.ENGLISH, + text + ) shouldBeEqual "Battery level of F Motion Sensor Device Stairway is CRITICAL" + + } + + batteryController.applyServiceState(States.Battery.OK, ServiceType.BATTERY_STATE_SERVICE) + batteryStateAwaiter.waitForState { it.batteryState.value == BatteryState.State.OK } + + appController!!.checkDevices() + messageManagerLauncher.launchable!!.removeOutdatedMessages() + + // verify all messages are removed after the battery has been replaced. + getUserMessagesOfUnit(batteryRemote) shouldBeEqual emptyList() + } +} diff --git a/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.java b/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.java deleted file mode 100644 index 76ac4a44bc..0000000000 --- a/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.openbase.app.test.agent; - -/*- - * #%L - * BCO App Test Framework - * %% - * Copyright (C) 2018 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Timeout; -import org.openbase.bco.dal.remote.layer.unit.Units; -import org.openbase.bco.dal.remote.layer.unit.app.AppRemote; -import org.openbase.bco.dal.remote.layer.unit.util.UnitStateAwaiter; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.extension.type.processing.LabelProcessor; -import org.openbase.type.domotic.state.ActivationStateType; -import org.openbase.type.domotic.state.ConnectionStateType.ConnectionState; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType; -import org.openbase.type.domotic.unit.app.AppClassType.AppClass; -import org.slf4j.LoggerFactory; - -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -public abstract class AbstractBCOAppManagerTest extends BCOAppTest { - - private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(AbstractBCOAgentManagerTest.class); - - protected AppClass appClass = null; - protected UnitConfig appConfig = null; - protected AppRemote appRemote = null; - - @BeforeEach - @Timeout(30) - public void prepareAppManager() throws Exception { - try { - // setup and register app class - AppClass.Builder appClassBuilder = AppClass.newBuilder(); - LabelProcessor.addLabel(appClassBuilder.getLabelBuilder(), Locale.ENGLISH, getAppClass().getSimpleName().replace("App", "")); - this.appClass = Registries.getClassRegistry().registerAppClass(appClassBuilder.build()).get(5, TimeUnit.SECONDS); - - UnitConfig.Builder appConfigBuilder = getAppConfig(); - appConfigBuilder.getAppConfigBuilder().setAppClassId(this.appClass.getId()); - appConfigBuilder.setUnitType(UnitTemplateType.UnitTemplate.UnitType.APP); - // register app - this.appConfig = Registries.getUnitRegistry().registerUnitConfig(appConfigBuilder.build()).get(5, TimeUnit.SECONDS); - // retrieve remote and activate app - this.appRemote = Units.getUnit(this.appConfig, true, Units.APP); - if (!this.appConfig.getAppConfig().getAutostart()) { - // activate app if not in auto start - waitForExecution(this.appRemote.setActivationState(ActivationStateType.ActivationState.newBuilder().setValue(ActivationStateType.ActivationState.State.ACTIVE).build())); - } else { - // wait until active - new UnitStateAwaiter<>(this.appRemote).waitForState(data -> data.getActivationState().getValue() == ActivationStateType.ActivationState.State.ACTIVE); - } - } catch (Exception ex) { - throw ExceptionPrinter.printHistoryAndReturnThrowable(ex, LOGGER); - } - } - - @AfterEach - @Timeout(30) - public void removeAgent() throws Exception { - Registries.getUnitRegistry().removeUnitConfig(appConfig); - appRemote.waitForConnectionState(ConnectionState.State.DISCONNECTED); - } - - public abstract Class getAppClass(); - - public abstract UnitConfig.Builder getAppConfig() throws CouldNotPerformException; -} diff --git a/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.kt b/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.kt new file mode 100644 index 0000000000..7e2d96cf84 --- /dev/null +++ b/module/app/test/src/main/java/org/openbase/app/test/agent/AbstractBCOAppManagerTest.kt @@ -0,0 +1,124 @@ +package org.openbase.app.test.agent + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Timeout +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.dal.remote.layer.unit.app.AppRemote +import org.openbase.bco.dal.remote.layer.unit.util.UnitStateAwaiter +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.extension.type.processing.LabelProcessor.addLabel +import org.openbase.type.domotic.state.ActivationStateType +import org.openbase.type.domotic.state.ConnectionStateType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitTemplateType +import org.openbase.type.domotic.unit.app.AppClassType +import org.openbase.type.domotic.unit.app.AppDataType +import org.slf4j.LoggerFactory +import java.util.* +import java.util.concurrent.TimeUnit + +/*- +* #%L +* BCO App Test Framework +* %% +* Copyright (C) 2018 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program. If not, see +* . +* #L% +*/ +abstract class AbstractBCOAppManagerTest : BCOAppTest() { + protected var appClass: AppClassType.AppClass? = null + protected var appConfig: UnitConfigType.UnitConfig? = null + protected var appRemote: AppRemote? = null + protected var appController: APP_CLASS? = null + + @BeforeEach + @Timeout(30) + @Throws(Exception::class) + fun prepareAppManager() { + try { + // setup and register app class + val appClassBuilder = AppClassType.AppClass.newBuilder() + addLabel( + appClassBuilder.getLabelBuilder(), + Locale.ENGLISH, + getAppClass().getSimpleName().replace("App", "") + ) + val appClass = Registries.getClassRegistry().registerAppClass(appClassBuilder.build())[5, TimeUnit.SECONDS] + val appConfigBuilder = getAppConfig() + appConfigBuilder.getAppConfigBuilder().setAppClassId(appClass.getId()) + appConfigBuilder.setUnitType(UnitTemplateType.UnitTemplate.UnitType.APP) + // register app + val appConfig = Registries.getUnitRegistry() + .registerUnitConfig(appConfigBuilder.build())[5, TimeUnit.SECONDS] + + Registries.waitUntilReady() + + // retrieve remote and activate app + val appRemote = Units.getUnit(appConfig, true, Units.APP) + if (!appConfig.appConfig.autostart) { + // activate app if not in auto start + waitForExecution( + appRemote.setActivationState( + ActivationStateType.ActivationState.newBuilder().setValue( + ActivationStateType.ActivationState.State.ACTIVE + ).build() + ) + ) + } else { + // wait until active + UnitStateAwaiter(appRemote).waitForState { data: AppDataType.AppData -> data.activationState.getValue() == ActivationStateType.ActivationState.State.ACTIVE } + } + + this.appClass = appClass + this.appConfig = appConfig + this.appRemote = appRemote + this.appController = + appManagerLauncher.launchable!!.appControllerRegistry.get(appConfig?.getId()) as APP_CLASS + } catch (ex: Exception) { + throw ExceptionPrinter.printHistoryAndReturnThrowable(ex, LOGGER) + } + } + + @AfterEach + @Timeout(30) + @Throws(Exception::class) + fun removeAgent() { + if (appConfig != null) { + Registries.getUnitRegistry().removeUnitConfig(appConfig).get() + } + + if (appRemote != null) { + appRemote!!.waitForConnectionState(ConnectionStateType.ConnectionState.State.DISCONNECTED) + } + + if (appClass != null) { + Registries.getClassRegistry().removeAppClass(appClass).get() + } + } + + abstract fun getAppClass(): Class + + @Throws(CouldNotPerformException::class) + abstract fun getAppConfig(): UnitConfigType.UnitConfig.Builder + + companion object { + private val LOGGER = LoggerFactory.getLogger(AbstractBCOAgentManagerTest::class.java) + } +} diff --git a/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java b/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java index 4e76fc78ad..2fd9c73e49 100644 --- a/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java +++ b/module/app/test/src/main/java/org/openbase/app/test/agent/BCOAppTest.java @@ -30,6 +30,7 @@ import org.openbase.bco.dal.control.layer.unit.app.AppManagerLauncher; import org.openbase.bco.dal.control.layer.unit.device.DeviceManagerLauncher; import org.openbase.bco.dal.control.layer.unit.location.LocationManagerLauncher; +import org.openbase.bco.dal.control.message.MessageManagerLauncher; import org.openbase.bco.dal.lib.layer.unit.UnitController; import org.openbase.bco.dal.test.AbstractBCOTest; import org.openbase.bco.registry.remote.Registries; @@ -46,6 +47,7 @@ public class BCOAppTest extends AbstractBCOTest { protected static AppManagerLauncher appManagerLauncher; protected static DeviceManagerLauncher deviceManagerLauncher; protected static LocationManagerLauncher locationManagerLauncher; + protected static MessageManagerLauncher messageManagerLauncher; @BeforeAll @Timeout(30) @@ -67,6 +69,10 @@ public static void setupBcoApp() throws Throwable { appManagerLauncher = new AppManagerLauncher(); appManagerLauncher.launch().get(); + LOGGER.trace("Start message manager..."); + messageManagerLauncher = new MessageManagerLauncher(); + messageManagerLauncher.launch().get(); + LOGGER.trace("Finally wait for registry..."); Registries.waitForData(); diff --git a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java index 25d5495524..4a2d932a90 100644 --- a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java +++ b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOLauncher.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -32,6 +32,7 @@ import org.openbase.bco.dal.control.layer.unit.location.LocationManagerLauncher; import org.openbase.bco.dal.control.layer.unit.scene.SceneManagerLauncher; import org.openbase.bco.dal.control.layer.unit.user.UserManagerLauncher; +import org.openbase.bco.dal.control.message.MessageManagerLauncher; import org.openbase.bco.device.openhab.OpenHABDeviceManagerLauncher; import org.openbase.bco.device.openhab.registry.OpenHABConfigSynchronizerLauncher; import org.openbase.bco.device.openhab.sitemap.OpenHABSitemapSynchronizerLauncher; @@ -57,7 +58,7 @@ public static void main(final String[] args) { BCO.printLogo(); // create dynamic launcher container - ArrayList> launcher = new ArrayList<>(); + ArrayList>> launcher = new ArrayList<>(); /** * Configure Authenticator Launcher @@ -82,6 +83,7 @@ public static void main(final String[] args) { launcher.add(LocationManagerLauncher.class); launcher.add(SceneManagerLauncher.class); launcher.add(UserManagerLauncher.class); + launcher.add(MessageManagerLauncher.class); /** * API Launcher diff --git a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java index dbc69570fd..0e8e219702 100644 --- a/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java +++ b/module/app/util/src/main/java/org/openbase/bco/app/util/launch/BCOTestLauncher.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,22 +24,22 @@ import org.openbase.bco.api.graphql.BcoApiGraphQlLauncher; import org.openbase.bco.authentication.core.AuthenticatorLauncher; +import org.openbase.bco.authentication.lib.BCO; import org.openbase.bco.dal.control.layer.unit.agent.AgentManagerLauncher; import org.openbase.bco.dal.control.layer.unit.app.AppManagerLauncher; import org.openbase.bco.dal.control.layer.unit.device.DeviceManagerLauncher; import org.openbase.bco.dal.control.layer.unit.location.LocationManagerLauncher; import org.openbase.bco.dal.control.layer.unit.scene.SceneManagerLauncher; import org.openbase.bco.dal.control.layer.unit.user.UserManagerLauncher; +import org.openbase.bco.dal.control.message.MessageManagerLauncher; import org.openbase.bco.dal.lib.jp.JPProviderControlMode; import org.openbase.bco.registry.activity.core.ActivityRegistryLauncher; import org.openbase.bco.registry.clazz.core.ClassRegistryLauncher; -import org.openbase.bco.authentication.lib.BCO; import org.openbase.bco.registry.message.core.MessageRegistryLauncher; import org.openbase.bco.registry.template.core.TemplateRegistryLauncher; import org.openbase.bco.registry.unit.core.UnitRegistryLauncher; import org.openbase.jps.core.JPService; import org.openbase.jul.pattern.launch.AbstractLauncher; -import org.openbase.jul.pattern.launch.jp.JPPrintLauncher; /** * @author Divine Threepwood @@ -76,6 +76,7 @@ public static void main(final String[] args) { LocationManagerLauncher.class, SceneManagerLauncher.class, UserManagerLauncher.class, + MessageManagerLauncher.class, /* * API Launcher diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java index 8b63a196e5..fec9a639fb 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractAuthorizedBaseUnitController.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -63,7 +63,9 @@ public abstract class AbstractAuthorizedBaseUnitController observedTaskList; + protected UnitConfig userConfig = null; + + private final ArrayList observedTaskList; private final static ProtoBufJSonProcessor protoBufJSonProcessor = new ProtoBufJSonProcessor(); @@ -93,10 +95,10 @@ public UnitConfig applyConfigUpdate(final UnitConfig config) throws CouldNotPerf private AuthToken requestAuthToken(final UnitConfig unitConfig) throws CouldNotPerformException, InterruptedException { try { - final UnitConfig userUnitConfig = UnitUserCreationPlugin.findUser(unitConfig.getId(), Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitType.USER)); - final AuthenticationToken authenticationToken = AuthenticationToken.newBuilder().setUserId(userUnitConfig.getId()).build(); + userConfig = UnitUserCreationPlugin.findUser(unitConfig.getId(), Registries.getUnitRegistry(true).getUnitConfigsByUnitType(UnitType.USER)); + final AuthenticationToken authenticationToken = AuthenticationToken.newBuilder().setUserId(userConfig.getId()).build(); final SessionManager sessionManager = new SessionManager(); - sessionManager.loginUser(userUnitConfig.getId(), true); + sessionManager.loginUser(userConfig.getId(), true); final AuthenticatedValue authenticatedValue = sessionManager.initializeRequest(authenticationToken, null); return AuthToken.newBuilder().setAuthenticationToken(new AuthenticatedValueFuture<>( Registries.getUnitRegistry().requestAuthenticationTokenAuthenticated(authenticatedValue), @@ -149,6 +151,7 @@ protected AuthToken getToken() { * Additionally, the auth token of this controller is passed to the remote action and the action auto extension routine is enabled. * * @param futureAction used to identify the action to observe. + * * @return a ready to use action remote instance. */ protected RemoteAction observe(final Future futureAction) { diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java index 22432b8452..93f10865dd 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/InfluxDbProcessor.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -78,7 +78,7 @@ public class InfluxDbProcessor { public static final String INFLUXDB_BATCH_LIMIT = "INFLUXDB_BATCH_LIMIT"; public static final String INFLUXDB_BATCH_LIMIT_DEFAULT = "100"; public static final String INFLUXDB_URL = "INFLUXDB_URL"; - public static final String INFLUXDB_URL_DEFAULT = "http://localhost:8086"; + public static final String INFLUXDB_URL_DEFAULT = "http://influxdb:8086"; public static final String INFLUXDB_ORG = "INFLUXDB_ORG"; public static final String INFLUXDB_ORG_DEFAULT = "openbase"; public static final String INFLUXDB_TOKEN = "INFLUXDB_TOKEN"; @@ -458,7 +458,7 @@ public static Future queryAggregatedServiceState(final Q return FutureProcessor.completedFuture(newAggregatedServiceState); } catch (CouldNotPerformException ex) { - return FutureProcessor.canceledFuture(AggregatedServiceState.class,new CouldNotPerformException("Could not query aggregated service state", ex)); + return FutureProcessor.canceledFuture(AggregatedServiceState.class, new CouldNotPerformException("Could not query aggregated service state", ex)); } } } diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java index 355ec29991..9c96835574 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java @@ -10,35 +10,35 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . * #L% */ + import org.openbase.bco.dal.lib.layer.unit.app.App; import org.openbase.bco.dal.lib.layer.unit.app.AppController; import org.openbase.bco.dal.lib.layer.unit.app.AppControllerFactory; import org.openbase.bco.registry.remote.Registries; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.NotAvailableException; +import org.openbase.jul.extension.type.processing.LabelProcessor; import org.openbase.jul.processing.StringProcessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; import org.openbase.type.domotic.unit.app.AppClassType.AppClass; -import org.openbase.jul.extension.type.processing.LabelProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.util.Locale; /** - * * @author Tamino Huxohl */ public class AppControllerFactoryImpl implements AppControllerFactory { @@ -73,9 +73,11 @@ public AppController newInstance(final UnitConfig appUnitConfig) throws org.open try { // try to load preset app String className = PRESET_APP_PACKAGE_PREFIX - + "." + LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel()) + "App"; + + "." + StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel())) + "App"; app = (AppController) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance(); - } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | NoSuchMethodException | InvocationTargetException ex) { + } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | + IllegalAccessException | IllegalArgumentException | NoSuchMethodException | + InvocationTargetException ex) { // try to load custom app String className = CUSTOM_APP_PACKAGE_PREFIX + "." + StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel())).toLowerCase() @@ -84,7 +86,9 @@ public AppController newInstance(final UnitConfig appUnitConfig) throws org.open } logger.debug("Creating app of type [" + LabelProcessor.getBestMatch(appClass.getLabel()) + "]"); app.init(appUnitConfig); - } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InterruptedException | NoSuchMethodException | InvocationTargetException ex) { + } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | + IllegalAccessException | IllegalArgumentException | InterruptedException | NoSuchMethodException | + InvocationTargetException ex) { throw new org.openbase.jul.exception.InstantiationException(App.class, appUnitConfig.getId(), ex); } return app; diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java index 8062e2903a..817ddd693c 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/scene/SceneControllerImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -55,6 +55,7 @@ import org.openbase.jul.schedule.TimeoutSplitter; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; import org.openbase.type.domotic.action.ActionParameterType.ActionParameter; +import org.openbase.type.domotic.action.ActionPriorityType.ActionPriority.Priority; import org.openbase.type.domotic.action.ActionReferenceType.ActionReference; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; import org.openbase.type.domotic.service.ServiceStateDescriptionType.ServiceStateDescription; @@ -72,6 +73,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.time.Duration; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.CancellationException; @@ -195,7 +197,8 @@ public UnitConfig applyConfigUpdate(UnitConfig config) throws CouldNotPerformExc final ActionParameter actionParameterPrototype = ActionParameter.newBuilder() .setInterruptible(true) .setSchedulable(true) - .setExecutionTimePeriod(Long.MAX_VALUE).build(); + .setPriority(Priority.HIGH) + .setExecutionTimePeriod(Duration.ofMinutes(30).toMillis()).build(); requiredActionPool.initViaServiceStateDescription(config.getSceneConfig().getRequiredServiceStateDescriptionList(), actionParameterPrototype, () -> getActivationState().getValue() == ActivationState.State.ACTIVE); optionalActionPool.initViaServiceStateDescription(config.getSceneConfig().getOptionalServiceStateDescriptionList(), actionParameterPrototype, () -> getActivationState().getValue() == ActivationState.State.ACTIVE); return config; @@ -272,7 +275,7 @@ public synchronized Future setActivationState(final Activatio // shutdown all existing action observer to not let old observation interfere with new activations. - if(actionObserver != null) { + if (actionObserver != null) { actionObserver.shutdown(); } @@ -436,7 +439,7 @@ private RequiredActionObserver(final List requiredActionImpact, private void verifyAllStates() { try { - logger.trace(() -> "verify "+unitAndRequiredServiceStateMap.entrySet().size()+ " states of "+ getLabel("?")); + logger.trace(() -> "verify " + unitAndRequiredServiceStateMap.entrySet().size() + " states of " + getLabel("?")); for (Entry, RequiredServiceDescription> unitActionReferenceEntry : unitAndRequiredServiceStateMap.entrySet()) { try { // skip unit in case its offline, since then the verification is automatically @@ -457,7 +460,7 @@ private void verifyAllStates() { private void verifyState(final ServiceProvider unit, final Message serviceState) throws VerificationFailedException { // skip verification on destroyed required action observer! - if(destroy) { + if (destroy) { return; } @@ -466,13 +469,13 @@ private void verifyState(final ServiceProvider unit, final Me } // skip in case no service state was delivered - if(serviceState.toString().isBlank()) { + if (serviceState.toString().isBlank()) { return; } if (!Services.equalServiceStates(unitAndRequiredServiceStateMap.get(unit).getServiceState(), serviceState)) { logger.trace(() -> unitAndRequiredServiceStateMap.get(unit).getServiceState() + " is not equals " + serviceState.toString().substring(0, 20) + " and will cancel: " + SceneControllerImpl.this.getLabel("?")); - if(Actions.validateInitialAction(serviceState)) { + if (Actions.validateInitialAction(serviceState)) { throw new VerificationFailedException("State of " + unit + "not meet!"); } } diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt new file mode 100644 index 0000000000..91f90d6ac7 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt @@ -0,0 +1,106 @@ +package org.openbase.bco.dal.control.message + +import com.google.protobuf.Message +import org.openbase.bco.dal.lib.layer.service.Services +import org.openbase.bco.dal.lib.layer.unit.UnitRemote +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InitializationException +import org.openbase.jul.iface.Launchable +import org.openbase.jul.iface.VoidInitializable +import org.openbase.jul.pattern.Observer +import org.openbase.jul.pattern.provider.DataProvider +import org.openbase.jul.schedule.RecurrenceEventFilter +import org.openbase.type.domotic.registry.MessageRegistryDataType.MessageRegistryData +import org.slf4j.LoggerFactory +import java.time.Duration +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.write + +class MessageManager : Launchable, VoidInitializable { + + private var logger = LoggerFactory.getLogger(MessageManager::class.java) + + private val unitsOfConditionsLock = ReentrantReadWriteLock() + + private val maxUpdateInterval: Duration = Duration.ofSeconds(1) + + private var active = false + + init { + logger.error("#@# start message manager") + } + + private val removeOutdatedMessagesTask: RecurrenceEventFilter = + object : RecurrenceEventFilter(maxUpdateInterval.toMillis()) { + override fun relay() { + removeOutdatedMessages() + } + } + + fun removeOutdatedMessages() { + logger.error("#@# removeOutdatedMessages") + Registries.getMessageRegistry().userMessages + .filterNot { message -> + message.conditionList.any { condition -> + Units.getUnit(condition.unitId, true).let { unit -> + Services.equalServiceStates( + unit.getServiceState(condition.serviceType), + Services.deserializeServiceState(condition) + ) + } + } + } + .forEach { Registries.getMessageRegistry().removeUserMessage(it).get(5, TimeUnit.SECONDS) } + } + + private var unitsOfConditions: List>? = null + + private val conditionObserver: Observer, Message> = + Observer { _, _ -> removeOutdatedMessagesTask.trigger() } + + private val messageRegistryChangeObserver: Observer, MessageRegistryData> = + Observer { _, data -> + unitsOfConditionsLock.write { + unitsOfConditions?.forEach { it.removeDataObserver(conditionObserver) } + unitsOfConditions = data.userMessageList.let { userMessages -> + userMessages.flatMap { it.conditionList } + .map { it.unitId } + .distinct() + .mapNotNull { unitId -> + try { + Units.getUnit(unitId, false) + } catch (e: CouldNotPerformException) { + logger.warn("Could not resolve unit with id $unitId", e) + null + } + } + } + unitsOfConditions?.forEach { it.addDataObserver(conditionObserver) } + } + } + + @Throws(InitializationException::class, InterruptedException::class) + override fun init() { + // this overwrite is needed to overwrite the default implementation! + } + + override fun activate() { + logger.error("#@# activate message manager") + active = true + Registries.getMessageRegistry().addDataObserver(messageRegistryChangeObserver) + } + + override fun deactivate() { + Registries.getMessageRegistry().removeDataObserver(messageRegistryChangeObserver) + unitsOfConditionsLock.write { + unitsOfConditions?.forEach { it.removeDataObserver(conditionObserver) } + unitsOfConditions = null + } + active = false + } + + override fun isActive(): Boolean = active +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManagerLauncher.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManagerLauncher.kt new file mode 100644 index 0000000000..8f0cf54aa9 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManagerLauncher.kt @@ -0,0 +1,32 @@ +package org.openbase.bco.dal.control.message + +import org.openbase.bco.authentication.lib.BCO +import org.openbase.bco.authentication.lib.jp.JPBCODistributionDirectory +import org.openbase.bco.dal.lib.jp.JPBenchmarkMode +import org.openbase.bco.dal.lib.jp.JPHardwareSimulationMode +import org.openbase.bco.dal.lib.jp.JPProviderControlMode +import org.openbase.jps.core.JPService +import org.openbase.jul.pattern.launch.AbstractLauncher + +class MessageManagerLauncher : + AbstractLauncher(MessageManager::class.java, MessageManager::class.java) { + public override fun loadProperties() { + JPService.registerProperty(JPBCODistributionDirectory::class.java) + JPService.registerProperty(JPHardwareSimulationMode::class.java) + JPService.registerProperty(JPBenchmarkMode::class.java) + JPService.registerProperty(JPProviderControlMode::class.java) + } + + companion object { + @Throws(Throwable::class) + @JvmStatic + fun main(args: Array) { + BCO.printLogo() + main( + BCO::class.java, + MessageManager::class.java, args, + MessageManagerLauncher::class.java + ) + } + } +} diff --git a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java index 2c2a900730..53edebebef 100644 --- a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java +++ b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/layer/service/ServiceStateProcessor.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -34,6 +34,7 @@ import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.FatalImplementationErrorException; import org.openbase.jul.exception.NotAvailableException; +import org.openbase.jul.exception.VerificationFailedException; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.extension.protobuf.ProtoBufBuilderProcessor; import org.openbase.jul.extension.protobuf.processing.ProtoBufFieldProcessor; @@ -48,7 +49,10 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.function.Supplier; public class ServiceStateProcessor { @@ -181,7 +185,8 @@ public static void updateLatestValueOccurrence(final EnumValueDescriptor enumVal entryMessageBuilder.setField(valueDescriptor, timestamp); entryMessageBuilder.setField(keyDescriptor, enumValueDescriptor); entryMessage = entryMessageBuilder.build(); - } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | ClassCastException ex) { + } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | + ClassCastException ex) { throw new CouldNotPerformException("Could not build service state entry!", ex); } @@ -317,7 +322,7 @@ private static Set computeActionImpact(final ServiceStateDesc // handle termination: // make sure duplicated service states are only processed ones. - if(processedServiceStates.contains(serviceStateDescription)) { + if (processedServiceStates.contains(serviceStateDescription)) { return Collections.emptySet(); } else { processedServiceStates.add(serviceStateDescription); @@ -377,7 +382,7 @@ private static Set computeActionImpact(final ServiceStateDesc } // register location itself as impact in case it's affected by child units through aggregation - if(!locationChildUnits.isEmpty()) { + if (!locationChildUnits.isEmpty()) { actionImpactList.add(buildActionImpact(serviceStateDescription).setIntermediary(true).build()); } break; @@ -440,6 +445,15 @@ public static String serializeServiceState(final Message serviceState, final boo } public static Message deserializeServiceState(final ServiceStateDescriptionOrBuilder serviceStateDescriptionOrBuilder) throws CouldNotPerformException { + + if (!serviceStateDescriptionOrBuilder.hasServiceState() || serviceStateDescriptionOrBuilder.getServiceState().isEmpty()) { + throw new VerificationFailedException("ServiceStateDescription does not provide any service state: " + serviceStateDescriptionOrBuilder); + } + + if (!serviceStateDescriptionOrBuilder.hasServiceStateClassName() || serviceStateDescriptionOrBuilder.getServiceStateClassName().isEmpty()) { + throw new VerificationFailedException("ServiceStateDescription does not provide any service state class name: " + serviceStateDescriptionOrBuilder); + } + return deserializeServiceState(serviceStateDescriptionOrBuilder.getServiceState(), serviceStateDescriptionOrBuilder.getServiceStateClassName()); } diff --git a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java index a1ea493e92..ba0733bab1 100644 --- a/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java +++ b/module/dal/lib/src/main/java/org/openbase/bco/dal/lib/state/States.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -23,8 +23,7 @@ */ import org.openbase.type.domotic.state.ActivationStateType.ActivationState; -import org.openbase.type.domotic.state.ActivationStateType.ActivationState.State; -import org.openbase.type.domotic.state.BlindStateType; +import org.openbase.type.domotic.state.BatteryStateType.BatteryState; import org.openbase.type.domotic.state.BlindStateType.BlindState; import org.openbase.type.domotic.state.BrightnessStateType.BrightnessState; import org.openbase.type.domotic.state.ColorStateType.ColorState; @@ -33,7 +32,6 @@ import org.openbase.type.domotic.state.MotionStateType.MotionState; import org.openbase.type.domotic.state.PowerStateType.PowerState; import org.openbase.type.vision.ColorType; -import org.openbase.type.vision.ColorType.Color; import org.openbase.type.vision.ColorType.Color.Type; import org.openbase.type.vision.HSBColorType.HSBColor; @@ -125,4 +123,14 @@ public static class Contact { public static final ContactState OPEN = ContactState.newBuilder().setValue(ContactState.State.OPEN).build(); public static final ContactState CLOSED = ContactState.newBuilder().setValue(ContactState.State.CLOSED).build(); } + + /** + * Battery State Prototypes + */ + public static class Battery { + public static final BatteryState OK = BatteryState.newBuilder().setValue(BatteryState.State.OK).build(); + public static final BatteryState LOW = BatteryState.newBuilder().setValue(BatteryState.State.LOW).build(); + public static final BatteryState CRITICAL = BatteryState.newBuilder().setValue(BatteryState.State.CRITICAL).build(); + public static final BatteryState INSUFFICIENT = BatteryState.newBuilder().setValue(BatteryState.State.INSUFFICIENT).build(); + } } diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/action/RemoteAction.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/action/RemoteAction.java index 5bdb822d1b..0ea35e8516 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/action/RemoteAction.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/action/RemoteAction.java @@ -643,6 +643,11 @@ public boolean isRunning() { */ @Override public boolean isDone() { + + if (futureObservationTask != null && !futureObservationTask.isDone()) { + return false; + } + try { if (getActionDescription().getIntermediary()) { for (final RemoteAction impactedRemoteAction : impactedRemoteActions) { diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java index 51e86b5bec..9fc1aa24ba 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/detector/PresenceDetector.java @@ -74,8 +74,8 @@ public class PresenceDetector implements Manageable, DataProvider, LocationData> locationDataObserver; private Location location; private final ObservableImpl, PresenceState> presenceStateObservable; - private final CustomUnitPool buttonUnitPool; - private final CustomUnitPool connectionUnitPool; + private final CustomUnitPool buttonUnitPool; + private final CustomUnitPool connectionUnitPool; private boolean active; private boolean shutdownInitiated = false; diff --git a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt index 209264df21..2aaa885cce 100644 --- a/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt +++ b/module/dal/remote/src/main/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPool.kt @@ -45,12 +45,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock * #L% */ -class CustomUnitPool : Manageable>> { +class CustomUnitPool> : Manageable>> { private val UNIT_REMOTE_REGISTRY_LOCK = ReentrantReadWriteLock() private var unitRegistryDataObserver: Observer, UnitRegistryDataType.UnitRegistryData> - private var unitDataObserver: Observer, Message> + private var unitDataObserver: Observer, M> private var serviceStateObserver: Observer, out Message> - private var unitRemoteRegistry: RemoteControllerRegistry> + private var unitRemoteRegistry: RemoteControllerRegistry private var unitConfigDiff: ProtobufListDiff private var filterSet: MutableSet> private var unitDataObservable: ObservableImpl, Message> @@ -184,7 +184,7 @@ class CustomUnitPool : Manageable>> FatalImplementationErrorException("unid remote registered but pool was never activated!", this) } val unitRemote: UnitRemote = Units.getUnit(unitId, false) - unitRemoteRegistry.register(unitRemote) + unitRemoteRegistry.register(unitRemote as U) for (serviceType in unitRemote.availableServiceTypes) { unitRemote.addServiceStateObserver(serviceType, serviceStateObserver) } @@ -298,7 +298,7 @@ class CustomUnitPool : Manageable>> override fun isActive(): Boolean = active - val internalUnitList: List> + val internalUnitList: List get() = unitRemoteRegistry.entries companion object { diff --git a/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.java b/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.java deleted file mode 100644 index b90e840077..0000000000 --- a/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.openbase.bco.dal.remote.layer.unit; - -/*- - * #%L - * BCO DAL Test - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import static org.junit.jupiter.api.Assertions.*; -import com.google.protobuf.Message; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.openbase.bco.dal.lib.layer.unit.UnitRemote; -import org.openbase.bco.dal.test.AbstractBCODeviceManagerTest; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; - -import java.util.List; -import java.util.concurrent.TimeUnit; - - - -public class CustomUnitPoolTest extends AbstractBCODeviceManagerTest { - - /** - * Test of getBatteryLevel method, of class BatteryRemote. - * - * @throws java.lang.Exception - */ - @Test - @Timeout(10) - public void testUnitPool() throws Exception { - final CustomUnitPool customUnitPool = new CustomUnitPool(); - assertEquals(false, customUnitPool.isActive(), "pool is active while never activated"); - - customUnitPool.activate(); - - customUnitPool.init( - unitConfig -> unitConfig.hasId(), - unitConfig -> unitConfig.getUnitType() == UnitType.BUTTON); - - customUnitPool.activate(); - - for (UnitRemote unitRemote : customUnitPool.getInternalUnitList()) { - assertEquals(UnitType.BUTTON, unitRemote.getUnitType(), "pool contains actually filtered entry!"); - System.out.println("is button: "+ unitRemote.getLabel()); - } - - final List buttonUnitConfig = Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitType.BUTTON); - Registries.getUnitRegistry().updateUnitConfig(buttonUnitConfig.get(0).toBuilder().addAlias("MyButtonTestUnit").build()).get(5, TimeUnit.SECONDS); - - final List lightUnitConfig = Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitType.COLORABLE_LIGHT); - Registries.getUnitRegistry().updateUnitConfig(lightUnitConfig.get(0).toBuilder().addAlias("MyLightestUnit").build()).get(5, TimeUnit.SECONDS); - - for (UnitRemote unitRemote : customUnitPool.getInternalUnitList()) { - assertEquals(UnitType.BUTTON, unitRemote.getUnitType(), "pool contains actually filtered entry!"); - System.out.println("is button: "+ unitRemote.getLabel()); - } - - customUnitPool.shutdown(); - } -} diff --git a/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.kt b/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.kt new file mode 100644 index 0000000000..521d47975e --- /dev/null +++ b/module/dal/test/src/test/java/org/openbase/bco/dal/remote/layer/unit/CustomUnitPoolTest.kt @@ -0,0 +1,77 @@ +package org.openbase.bco.dal.remote.layer.unit + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Timeout +import org.openbase.bco.dal.test.AbstractBCODeviceManagerTest +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.pattern.Filter +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.UnitTemplateType +import java.util.concurrent.TimeUnit + +/*- +* #%L +* BCO DAL Test +* %% +* Copyright (C) 2014 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ +class CustomUnitPoolTest : AbstractBCODeviceManagerTest() { + /** + * Test of getBatteryLevel method, of class BatteryRemote. + * + * @throws java.lang.Exception + */ + @Test + @Timeout(10) + @Throws(Exception::class) + fun testUnitPool() { + val customUnitPool: CustomUnitPool<*, *> = CustomUnitPool() + Assertions.assertEquals(false, customUnitPool.isActive(), "pool is active while never activated") + customUnitPool.activate() + customUnitPool.init( + Filter { unitConfig: Any -> unitConfig.hasId() }, + Filter { unitConfig: Any -> unitConfig.getUnitType() === UnitTemplateType.UnitTemplate.UnitType.BUTTON }) + customUnitPool.activate() + for (unitRemote in customUnitPool.internalUnitList) { + Assertions.assertEquals( + UnitTemplateType.UnitTemplate.UnitType.BUTTON, + unitRemote.getUnitType(), + "pool contains actually filtered entry!" + ) + println("is button: " + unitRemote.getLabel()) + } + val buttonUnitConfig = + Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitTemplateType.UnitTemplate.UnitType.BUTTON) + Registries.getUnitRegistry() + .updateUnitConfig(buttonUnitConfig[0].toBuilder().addAlias("MyButtonTestUnit").build())[5, TimeUnit.SECONDS] + val lightUnitConfig = Registries.getUnitRegistry() + .getUnitConfigsByUnitType(UnitTemplateType.UnitTemplate.UnitType.COLORABLE_LIGHT) + Registries.getUnitRegistry() + .updateUnitConfig(lightUnitConfig[0].toBuilder().addAlias("MyLightestUnit").build())[5, TimeUnit.SECONDS] + for (unitRemote in customUnitPool.internalUnitList) { + Assertions.assertEquals( + UnitTemplateType.UnitTemplate.UnitType.BUTTON, + unitRemote.getUnitType(), + "pool contains actually filtered entry!" + ) + println("is button: " + unitRemote.getLabel()) + } + customUnitPool.shutdown() + } +} diff --git a/module/dal/test/src/test/java/org/openbase/bco/dal/test/action/KotlinTest.kt b/module/dal/test/src/test/java/org/openbase/bco/dal/test/action/KotlinTest.kt deleted file mode 100644 index 715a56e404..0000000000 --- a/module/dal/test/src/test/java/org/openbase/bco/dal/test/action/KotlinTest.kt +++ /dev/null @@ -1,4 +0,0 @@ -package org.openbase.bco.dal.test.action - -class KotlinTest { -} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java index 0b07dfc370..f579407fe0 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,7 +24,6 @@ import org.openbase.jps.core.AbstractJavaProperty; -import java.io.File; import java.net.URI; import java.util.List; @@ -36,7 +35,7 @@ public class JPOpenHABURI extends AbstractJavaProperty { private static final String[] ARGUMENT_IDENTIFIERS = {"URI"}; private static final String[] COMMAND_IDENTIFIERS = {"--openhab-url", "--openhab-uri", "--uri"}; - private static final String DEFAULT_URI = "http://localhost:8080"; + private static final String DEFAULT_URI = "http://openhab:8080"; public static final String SYSTEM_VARIABLE_OPENHAB_PORT = "OPENHAB_HTTP_PORT"; @@ -56,7 +55,7 @@ protected URI getPropertyDefaultValue() { // use system variable if defined String systemDefinedPort = System.getenv(SYSTEM_VARIABLE_OPENHAB_PORT); if (systemDefinedPort != null) { - return URI.create("http://localhost:"+systemDefinedPort); + return URI.create("http://localhost:" + systemDefinedPort); } return URI.create(DEFAULT_URI); diff --git a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java index ecb4f1efc5..5a804588d8 100644 --- a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java +++ b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/com/AbstractRegistryController.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -190,7 +190,7 @@ public void activate() throws InterruptedException, CouldNotPerformException { performInitialConsistencyCheck(); } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not activate "+this+" registry!", ex); + throw new CouldNotPerformException("Could not activate " + this + " registry!", ex); } } diff --git a/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java b/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java index da7a5366f5..d6c183e210 100644 --- a/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java +++ b/module/registry/message-registry/core/src/main/java/org/openbase/bco/registry/message/core/MessageRegistryController.java @@ -10,24 +10,21 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . * #L% */ -import com.google.protobuf.Descriptors.FieldDescriptor; import org.openbase.bco.authentication.lib.AuthenticatedServiceProcessor; -import org.openbase.bco.authentication.lib.AuthorizationHelper; import org.openbase.bco.authentication.lib.AuthorizationHelper.PermissionType; import org.openbase.bco.registry.lib.com.AbstractRegistryController; -import org.openbase.bco.registry.lib.jp.JPBCODatabaseDirectory; import org.openbase.bco.registry.message.lib.MessageRegistry; import org.openbase.bco.registry.message.lib.generator.UserMessageIdGenerator; import org.openbase.bco.registry.message.lib.jp.JPMessageRegistryScope; @@ -40,16 +37,13 @@ import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.pattern.ListFilter; import org.openbase.jul.schedule.GlobalCachedExecutorService; import org.openbase.jul.storage.registry.ProtoBufFileSynchronizedRegistry; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; -import org.openbase.type.domotic.authentication.UserClientPairType.UserClientPair; import org.openbase.type.domotic.communication.UserMessageType.UserMessage; import org.openbase.type.domotic.registry.MessageRegistryDataType.MessageRegistryData; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; @@ -131,10 +125,7 @@ public void notifyChange() throws CouldNotPerformException, InterruptedException @Override public Future registerUserMessage(final UserMessage userMessage) { - return GlobalCachedExecutorService.submit(() -> { - UserMessage result = userMessageRegistry.register(userMessage); - return result; - }); + return GlobalCachedExecutorService.submit(() -> userMessageRegistry.register(userMessage)); } @Override @@ -226,7 +217,7 @@ public Future removeUserMessage(final UserMessage userMessage) { public Future removeUserMessageAuthenticated(final AuthenticatedValue authenticatedValue) { return GlobalCachedExecutorService.submit(() -> AuthenticatedServiceProcessor.authenticatedAction(authenticatedValue, UserMessage.class, this, (userMessage, authenticationBaseData) -> { - // verify write permissions for the old user message + // verify write permissions for the user message to remove final UserMessage old = userMessageRegistry.getMessage(userMessage.getId()); AuthorizationWithTokenHelper.canDo(authenticationBaseData, old, PermissionType.WRITE, CachedUnitRegistryRemote.getRegistry()); return userMessageRegistry.remove(userMessage); @@ -252,35 +243,35 @@ public Boolean isUserMessageRegistryConsistent() { protected void registerRemoteRegistries() { } - @Override - protected MessageRegistryData filterDataForUser(final MessageRegistryData.Builder dataBuilder, final UserClientPair userClientPair) throws CouldNotPerformException { - // Create a filter which removes all user messages from a list without read permissions to its location by the user - final ListFilter readFilter = userMessage -> { - try { - boolean senderPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getSenderId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); - boolean receiverPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getRecipientId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); - return !(senderPermission || receiverPermission); - } catch (CouldNotPerformException e) { - // if id could not resolved, than we filter the element. - return true; - } - }; - // iterate over all fields of unit registry data - for (FieldDescriptor fieldDescriptor : dataBuilder.getAllFields().keySet()) { - // only filter repeated fields - if (!fieldDescriptor.isRepeated()) { - continue; - } - - // only filter fields of type UserMessage - if (!fieldDescriptor.getMessageType().getName().equals(UserMessage.getDescriptor().getName())) { - continue; - } - - // copy list, filter it and set as new list for the field - dataBuilder.setField(fieldDescriptor, readFilter.filter(new ArrayList<>((List) dataBuilder.getField(fieldDescriptor)))); - } - - return dataBuilder.build(); - } +// @Override +// protected MessageRegistryData filterDataForUser(final MessageRegistryData.Builder dataBuilder, final UserClientPair userClientPair) throws CouldNotPerformException { +// // Create a filter which removes all user messages from a list without read permissions to its location by the user +// final ListFilter readFilter = userMessage -> { +// try { +// boolean senderPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getSenderId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); +// boolean receiverPermission = AuthorizationHelper.canRead(CachedUnitRegistryRemote.getRegistry().getUnitConfigById(userMessage.getRecipientId()), userClientPair, CachedUnitRegistryRemote.getRegistry().getAuthorizationGroupUnitConfigRemoteRegistry(true).getEntryMap(), CachedUnitRegistryRemote.getRegistry().getLocationUnitConfigRemoteRegistry(true).getEntryMap()); +// return !(senderPermission || receiverPermission); +// } catch (CouldNotPerformException e) { +// // if id could not resolved, than we filter the element. +// return true; +// } +// }; +// // iterate over all fields of unit registry data +// for (FieldDescriptor fieldDescriptor : dataBuilder.getAllFields().keySet()) { +// // only filter repeated fields +// if (!fieldDescriptor.isRepeated()) { +// continue; +// } +// +// // only filter fields of type UserMessage +// if (!fieldDescriptor.getMessageType().getName().equals(UserMessage.getDescriptor().getName())) { +// continue; +// } +// +// // copy list, filter it and set as new list for the field +// dataBuilder.setField(fieldDescriptor, readFilter.filter(new ArrayList<>((List) dataBuilder.getField(fieldDescriptor)))); +// } +// +// return dataBuilder.build(); +// } } diff --git a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java index cf469f312f..05c3ed056b 100644 --- a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java +++ b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemote.java @@ -10,12 +10,12 @@ * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. - * + * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . @@ -36,13 +36,17 @@ import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.InvalidStateException; import org.openbase.jul.exception.NotAvailableException; +import org.openbase.jul.exception.printer.ExceptionPrinter; +import org.openbase.jul.extension.type.processing.MultiLanguageTextProcessor; import org.openbase.jul.extension.type.util.TransactionSynchronizationFuture; import org.openbase.jul.storage.registry.RegistryRemote; import org.openbase.type.domotic.authentication.AuthenticatedValueType.AuthenticatedValue; import org.openbase.type.domotic.communication.UserMessageType.UserMessage; import org.openbase.type.domotic.registry.MessageRegistryDataType.MessageRegistryData; +import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.concurrent.Future; /** @@ -88,6 +92,7 @@ public SynchronizedRemoteRegistry getU } return userMessageRemoteRegistry; } + /** * {@inheritDoc} * @@ -124,6 +129,27 @@ public UserMessage getUserMessageById(final String userMessageId) throws NotAvai } } + public List getUserMessagesByText(final String text, final Locale locale) { + try { + validateData(); + return userMessageRemoteRegistry + .getMessages().stream() + .filter(userMessage -> { + try { + return MultiLanguageTextProcessor + .getBestMatch(locale, userMessage.getText()) + .equals(text); + } catch (NotAvailableException ex) { + return false; + } + }) + .toList(); + } catch (CouldNotPerformException ex) { + ExceptionPrinter.printHistory(new NotAvailableException("UserMessages", ex), logger); + return Collections.emptyList(); + } + } + @Override public List getUserMessages() throws CouldNotPerformException { try { @@ -156,7 +182,7 @@ public Boolean containsUserMessageById(final String userMessageId) { @Override public Future updateUserMessage(final UserMessage userMessage) { - return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), authenticatedValue -> updateUserMessageAuthenticated(authenticatedValue)); + return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), this::updateUserMessageAuthenticated); } @Override @@ -166,7 +192,7 @@ public Future updateUserMessageAuthenticated(final Authentic @Override public Future removeUserMessage(final UserMessage userMessage) { - return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), authenticatedValue -> removeUserMessageAuthenticated(authenticatedValue)); + return AuthenticatedServiceProcessor.requestAuthenticatedAction(userMessage, UserMessage.class, SessionManager.getInstance(), this::removeUserMessageAuthenticated); } @Override diff --git a/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemoteAuthExtensions.kt b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemoteAuthExtensions.kt new file mode 100644 index 0000000000..0b1b8639cf --- /dev/null +++ b/module/registry/message-registry/remote/src/main/java/org/openbase/bco/registry/message/remote/MessageRegistryRemoteAuthExtensions.kt @@ -0,0 +1,39 @@ +package org.openbase.bco.registry.message.remote + +import org.openbase.bco.authentication.lib.SessionManager +import org.openbase.bco.authentication.lib.future.AuthenticatedValueFuture +import org.openbase.type.domotic.authentication.AuthTokenType +import org.openbase.type.domotic.authentication.AuthenticatedValueType +import org.openbase.type.domotic.communication.UserMessageType +import java.io.Serializable +import java.util.concurrent.Future + +fun MessageRegistryRemote.updateUserMessageAuthenticated( + userMessage: UserMessageType.UserMessage?, + auth: AuthTokenType.AuthToken?, +): Future = authRequest(userMessage, ::updateUserMessageAuthenticated, auth) + +fun MessageRegistryRemote.removeUserMessageAuthenticated( + userMessage: UserMessageType.UserMessage?, + auth: AuthTokenType.AuthToken?, +): Future = authRequest(userMessage, ::removeUserMessageAuthenticated, auth) + +fun MessageRegistryRemote.registerUserMessageAuthenticated( + userMessage: UserMessageType.UserMessage?, + auth: AuthTokenType.AuthToken?, +): Future = authRequest(userMessage, ::registerUserMessageAuthenticated, auth) + +inline fun MessageRegistryRemote.authRequest( + value: T?, + origin: (AuthenticatedValueType.AuthenticatedValue) -> Future, + auth: AuthTokenType.AuthToken?, +): Future = with(SessionManager.getInstance()) { + initializeRequest(value, auth).let { value -> + AuthenticatedValueFuture( + origin(value), + T::class.java, + value.ticketAuthenticatorWrapper, + this + ) + } +} diff --git a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java index 22400d9f0c..a72891e97f 100644 --- a/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java +++ b/module/registry/unit-registry/core/src/main/java/org/openbase/bco/registry/unit/core/consistency/authorizationgroup/AuthorizationGroupClassGroupConsistencyHandler.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -58,7 +58,8 @@ public class AuthorizationGroupClassGroupConsistencyHandler extends AbstractProt public AuthorizationGroupClassGroupConsistencyHandler( final ProtoBufRegistry userRegistry, final ProtoBufRegistry agentRegistry, - final ProtoBufRegistry appRegistry) { + final ProtoBufRegistry appRegistry + ) { this.userRegistry = userRegistry; typeRegistryMap = new HashMap<>(); typeRegistryMap.put(UnitType.AGENT, agentRegistry); @@ -129,7 +130,7 @@ public void processData(final String id, } } - // remove all users which should not longer be part of the group + // remove all users which should no longer be part of the group for (String memberId : authorizationGroupConfig.getMemberIdList()) { if (!userIdList.contains(memberId)) { modification = true; diff --git a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.java b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.java deleted file mode 100644 index 4a87d2f2db..0000000000 --- a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.java +++ /dev/null @@ -1,347 +0,0 @@ -package org.openbase.bco.registry.unit.lib.auth; - -/*- - * #%L - * BCO Registry Unit Library - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -import com.google.protobuf.ProtocolStringList; -import org.openbase.bco.authentication.lib.AuthPair; -import org.openbase.bco.authentication.lib.AuthenticationBaseData; -import org.openbase.bco.authentication.lib.AuthorizationHelper; -import org.openbase.bco.authentication.lib.AuthorizationHelper.PermissionType; -import org.openbase.bco.registry.lib.util.UnitConfigProcessor; -import org.openbase.bco.registry.template.lib.TemplateRegistry; -import org.openbase.bco.registry.template.remote.CachedTemplateRegistryRemote; -import org.openbase.bco.registry.unit.lib.UnitRegistry; -import org.openbase.jul.exception.*; -import org.openbase.jul.exception.MultiException.ExceptionStack; -import org.openbase.type.domotic.authentication.AuthorizationTokenType.AuthorizationToken; -import org.openbase.type.domotic.authentication.AuthorizationTokenType.AuthorizationToken.PermissionRule; -import org.openbase.type.domotic.authentication.PermissionType.Permission; -import org.openbase.type.domotic.authentication.UserClientPairType.UserClientPair; -import org.openbase.type.domotic.communication.UserMessageType.UserMessage; -import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Tamino Huxohl - */ -public class AuthorizationWithTokenHelper { - - private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationWithTokenHelper.class); - - /** - * Verify an authorization token. This is done in two steps. - * By checking if all entered ids match either a UnitConfig, ServiceTemplate or UnitTemplate. - * If the id matches a unit config it is checked if the user defined in the token has at least as many permissions - * for the unit as the token would grant. - * - * @param authorizationToken the authorization token that is checked - * @param unitRegistry registry used to resolve authorization groups and locations to check permissions - * - * @throws CouldNotPerformException thrown if the token is invalid - */ - public static void verifyAuthorizationToken(final AuthorizationToken authorizationToken, final UnitRegistry unitRegistry) throws CouldNotPerformException { - try { - for (final PermissionRule permissionRule : authorizationToken.getPermissionRuleList()) { - // make sure the unit with the given id exists - final UnitConfig unitConfig; - try { - unitConfig = unitRegistry.getUnitConfigById(permissionRule.getUnitId()); - - // make sure the unit template with the given id exists - if (permissionRule.hasUnitTemplateId()) { - CachedTemplateRegistryRemote.getRegistry().getUnitTemplateById(permissionRule.getUnitTemplateId()); - } - - // make sure the service template with the given id exists - if (permissionRule.hasServiceTemplateId()) { - CachedTemplateRegistryRemote.getRegistry().getServiceTemplateById(permissionRule.getServiceTemplateId()); - } - } catch (CouldNotPerformException ex) { - throw new RejectedException("Invalid unit id, service template id or unit template id", ex); - } - - // a filter reduces permissions so it can be granted anyways - if (permissionRule.getFilter()) { - continue; - } - - // evaluate the permissions the given user has for the unit defined in the token - final Permission permission = AuthorizationHelper.getPermission(unitConfig, authorizationToken.getUserId(), unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap()); - - // reject the token if the user tries to give more permissions than he has - if (!AuthorizationHelper.isSubPermission(permission, permissionRule.getPermission())) { - throw new RejectedException("User[" + authorizationToken.getUserId() + "] has not enough permissions to create an authorizationToken with permissions[" + permissionRule.getPermission() + "] for unit[" + UnitConfigProcessor.getDefaultAlias(unitConfig, "?") + "]"); - } - } - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could verify access token", ex); - } - } - - /** - * Perform a permission check by validating that the actor is either the receiver or the sender of the message. - * - * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization - * @param userMessage the user message for which permissions are checked - * @param permissionType the permission type which is checked - * @param unitRegistry unit registry used to resolve ids - * - * @return a string representing the authorized user, this is either just the username of the authenticated user - * or the username of the authenticated user followed by the username of the issuer of the authorization token - * - * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails - */ - public static AuthPair canDo( - final AuthenticationBaseData authenticationBaseData, - final UserMessage userMessage, - final PermissionType permissionType, - final UnitRegistry unitRegistry) throws CouldNotPerformException { - - try { - // validate sender - return canDo(authenticationBaseData, unitRegistry.getUnitConfigById(userMessage.getSenderId()), permissionType, unitRegistry, null, null); - } catch (CouldNotPerformException ex) { - ExceptionStack exceptionStack = null; - exceptionStack = MultiException.push(AuthorizationWithTokenHelper.class, ex, exceptionStack); - - // validate receiver if sender validation failed. - try { - return canDo(authenticationBaseData, unitRegistry.getUnitConfigById(userMessage.getRecipientId()), permissionType, unitRegistry, null, null); - } catch (CouldNotPerformException exx) { - exceptionStack = MultiException.push(AuthorizationWithTokenHelper.class, exx, exceptionStack); - MultiException.checkAndThrow(() -> "Permission denied!", exceptionStack); - } - } - throw new FatalImplementationErrorException("ExceptionStack empty in error case.", AuthorizationWithTokenHelper.class); - } - - /** - * Perform a permission check according to {@link #canDo(AuthenticationBaseData, UnitConfig, PermissionType, UnitRegistry, UnitType, ServiceType)} - * by ignoring unit type and service type permissions. - * - * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization - * @param unitConfig the unit config for which permissions are checked - * @param permissionType the permission type which is checked - * @param unitRegistry unit registry used to resolve ids - * - * @return a string representing the authorized user, this is either just the username of the authenticated user - * or the username of the authenticated user followed by the username of the issuer of the authorization token - * - * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails - */ - public static AuthPair canDo( - final AuthenticationBaseData authenticationBaseData, - final UnitConfig unitConfig, - final PermissionType permissionType, - final UnitRegistry unitRegistry) throws CouldNotPerformException { - return canDo(authenticationBaseData, unitConfig, permissionType, unitRegistry, null, null); - } - - - /** - * Perform a permission check for authentication data including tokens. - * - * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization - * @param unitConfig the unit config for which permissions are checked - * @param permissionType the permission type which is checked - * @param unitRegistry unit registry used to resolve ids - * @param unitType the unit type for which is checked if the authorization tokens gives permissions for it, if it is null - * it will be ignored - * @param serviceType the service type for which is checked if the authorization tokens gives permissions for it, if it is null - * it will be ignored - * - * @return a string representing the authorized user, this is either just the username of the authenticated user - * or the username of the authenticated user followed by the username of the issuer of the authorization token - * - * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails - */ - public static AuthPair canDo( - final AuthenticationBaseData authenticationBaseData, - final UnitConfig unitConfig, - final PermissionType permissionType, - final UnitRegistry unitRegistry, - final UnitType unitType, - final ServiceType serviceType) throws CouldNotPerformException { - try { - // resolve the responsible user - UserClientPair userClientPair; - if (authenticationBaseData == null) { - // authentication data is not given so use null to check for other permissions - userClientPair = UserClientPair.getDefaultInstance(); - } else { - if (authenticationBaseData.getAuthenticationToken() != null) { - // authentication token is set so use it as the responsible user - userClientPair = UserClientPair.newBuilder().setUserId(authenticationBaseData.getAuthenticationToken().getUserId()).build(); - } else { - // use the user that is authenticated for the request - userClientPair = authenticationBaseData.getUserClientPair(); - } - } - - // check if authenticated user has needed permissions - if (AuthorizationHelper.canDo(unitConfig, userClientPair.getUserId(), unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap(), permissionType)) { - return new AuthPair(userClientPair, userClientPair.getUserId()); - } - if (AuthorizationHelper.canDo(unitConfig, userClientPair.getClientId(), unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap(), permissionType)) { - return new AuthPair(userClientPair, userClientPair.getClientId()); - } - - try { - // test if user is part of the admin group - final ProtocolStringList memberIdList = unitRegistry.getUnitConfigByAlias(UnitRegistry.ADMIN_GROUP_ALIAS).getAuthorizationGroupConfig().getMemberIdList(); - if (memberIdList.contains(userClientPair.getUserId())) { - return new AuthPair(userClientPair, userClientPair.getUserId()); - } - if (memberIdList.contains(userClientPair.getClientId())) { - return new AuthPair(userClientPair, userClientPair.getClientId()); - } - } catch (NotAvailableException ex) { - // continue with the checks, admin group is not available - } - - // authenticated user does not have permissions so check if the authorization token grants them - if (authenticationBaseData != null && authenticationBaseData.getAuthorizationToken() != null) { - final AuthorizationToken authorizationToken = authenticationBaseData.getAuthorizationToken(); - // verify that the authorization token is valid - verifyAuthorizationToken(authorizationToken, unitRegistry); - - // verify if the token grants the necessary permissions - return authorizedByToken(authorizationToken, userClientPair, unitConfig, permissionType, unitRegistry, unitType, serviceType); - } - String userRepresentation = userClientPair.getUserId(); - if (!userRepresentation.isEmpty()) { - userRepresentation += "@"; - } - userRepresentation += userClientPair.getClientId(); - if (userRepresentation.isEmpty()) { - userRepresentation = "Other"; - } - throw new PermissionDeniedException("User[" + userRepresentation + "] " + permissionType.name().toLowerCase() + " permission denied!"); - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not verify permissions for unit[" + UnitConfigProcessor.getDefaultAlias(unitConfig, "?") + "]", ex); - } - } - - private static AuthPair authorizedByToken( - final AuthorizationToken authorizationToken, - final UserClientPair userClientPair, - final UnitConfig unitConfig, - final PermissionType permissionType, - final UnitRegistry unitRegistry, - final UnitType unitType, - final ServiceType serviceType) throws CouldNotPerformException { - // verify if the token grants the necessary permissions - final TemplateRegistry templateRegistry = CachedTemplateRegistryRemote.getRegistry(); - final Set grantingPermissionSet = new HashSet<>(); - final Set filteringPermissionSet = new HashSet<>(); - for (final PermissionRule permissionRule : authorizationToken.getPermissionRuleList()) { - if (permissionRule.getFilter()) { - filteringPermissionSet.add(permissionRule); - } else { - grantingPermissionSet.add(permissionRule); - } - } - - boolean granted = false; - for (final PermissionRule permissionRule : grantingPermissionSet) { - if (permitted(unitConfig, permissionRule, unitRegistry, templateRegistry, serviceType, unitType, permissionType)) { - granted = true; - break; - } - } - - if (!granted) { - throw new PermissionDeniedException("Authorization token does not grant the necessary permissions"); - } - - for (final PermissionRule permissionRule : filteringPermissionSet) { - if (permitted(unitConfig, permissionRule, unitRegistry, templateRegistry, serviceType, unitType, permissionType)) { - throw new PermissionDeniedException("Authorization token does not grant the necessary permissions"); - } - } - - // build the auth pair - return new AuthPair(userClientPair, authorizationToken.getUserId()); - } - - private static boolean permitted(final UnitConfig unitConfig, - final PermissionRule permissionRule, - final UnitRegistry unitRegistry, - final TemplateRegistry templateRegistry, - final ServiceType serviceType, - final UnitType unitType, - final PermissionType permissionType) throws CouldNotPerformException { - // the permission would not grant these permissions anyway so return false - // this is done first so that rejections are handled fast - if (!AuthorizationHelper.permitted(permissionRule.getPermission(), permissionType)) { - return false; - } - // test if the unit id in the permission rule matches the unit config - if (!permissionRule.getUnitId().equals(unitConfig.getId())) { - // it does not so test if the unit id belongs to a location - final UnitConfig locationToCheck = unitRegistry.getUnitConfigById(permissionRule.getUnitId()); - // if it is not a location then the permission rule do not permit what is asked - if (locationToCheck.getUnitType() != UnitType.LOCATION) { - return false; - } - // if the location does not contain the given unit config the permission rule does not permit it - if (!containsUnit(unitConfig, locationToCheck, unitRegistry)) { - return false; - } - } - // if the given service type is defined and the rule contains a service type which does not match return false - if (serviceType != null - && serviceType != ServiceType.UNKNOWN - && permissionRule.hasServiceTemplateId() - && templateRegistry.getServiceTemplateById(permissionRule.getServiceTemplateId()).getServiceType() != serviceType) { - return false; - } - // if the given unit type is defined and the rule contains a unit type which does not match return false - return unitType == null - || unitType == UnitType.UNKNOWN - || !permissionRule.hasUnitTemplateId() - || templateRegistry.getUnitTemplateById(permissionRule.getUnitTemplateId()).getUnitType() == unitType; - } - - private static boolean containsUnit(final UnitConfig unitConfig, final UnitConfig locationToCheck, final UnitRegistry unitRegistry) throws CouldNotPerformException { - UnitConfig location = unitRegistry.getUnitConfigById(unitConfig.getPlacementConfig().getLocationId()); - if (location.getId().equals(locationToCheck.getId())) { - return true; - } - - while (!location.getLocationConfig().getRoot()) { - location = unitRegistry.getUnitConfigById(location.getPlacementConfig().getLocationId()); - if (location.getId().equals(locationToCheck.getId())) { - return true; - } - } - - return false; - } -} diff --git a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt new file mode 100644 index 0000000000..6cb5cc903e --- /dev/null +++ b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt @@ -0,0 +1,421 @@ +package org.openbase.bco.registry.unit.lib.auth + +import org.openbase.bco.authentication.lib.AuthPair +import org.openbase.bco.authentication.lib.AuthenticationBaseData +import org.openbase.bco.authentication.lib.AuthorizationHelper +import org.openbase.bco.registry.lib.util.UnitConfigProcessor +import org.openbase.bco.registry.template.lib.TemplateRegistry +import org.openbase.bco.registry.template.remote.CachedTemplateRegistryRemote +import org.openbase.bco.registry.unit.lib.UnitRegistry +import org.openbase.jul.exception.* +import org.openbase.jul.exception.MultiException.ExceptionStack +import org.openbase.type.domotic.authentication.AuthorizationTokenType +import org.openbase.type.domotic.authentication.UserClientPairType +import org.openbase.type.domotic.communication.UserMessageType +import org.openbase.type.domotic.service.ServiceTemplateType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitTemplateType +import org.slf4j.LoggerFactory +import java.util.* + +/*- +* #%L +* BCO Registry Unit Library +* %% +* Copyright (C) 2014 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Lesser Public License for more details. +* +* You should have received a copy of the GNU General Lesser Public +* License along with this program. If not, see +* . +* #L% +*/ /** + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +object AuthorizationWithTokenHelper { + private val LOGGER = LoggerFactory.getLogger(AuthorizationWithTokenHelper::class.java) + + /** + * Verify an authorization token. This is done in two steps. + * By checking if all entered ids match either a UnitConfig, ServiceTemplate or UnitTemplate. + * If the id matches a unit config it is checked if the user defined in the token has at least as many permissions + * for the unit as the token would grant. + * + * @param authorizationToken the authorization token that is checked + * @param unitRegistry registry used to resolve authorization groups and locations to check permissions + * + * @throws CouldNotPerformException thrown if the token is invalid + */ + @JvmStatic + @Throws(CouldNotPerformException::class) + fun verifyAuthorizationToken( + authorizationToken: AuthorizationTokenType.AuthorizationToken, + unitRegistry: UnitRegistry, + ) { + try { + for (permissionRule in authorizationToken.permissionRuleList) { + // make sure the unit with the given id exists + val unitConfig: UnitConfigType.UnitConfig + try { + unitConfig = unitRegistry.getUnitConfigById(permissionRule.getUnitId()) + + // make sure the unit template with the given id exists + if (permissionRule.hasUnitTemplateId()) { + CachedTemplateRegistryRemote.getRegistry() + .getUnitTemplateById(permissionRule.getUnitTemplateId()) + } + + // make sure the service template with the given id exists + if (permissionRule.hasServiceTemplateId()) { + CachedTemplateRegistryRemote.getRegistry() + .getServiceTemplateById(permissionRule.getServiceTemplateId()) + } + } catch (ex: CouldNotPerformException) { + throw RejectedException("Invalid unit id, service template id or unit template id", ex) + } + + // a filter reduces permissions so it can be granted anyways + if (permissionRule.filter) { + continue + } + + // evaluate the permissions the given user has for the unit defined in the token + val permission = AuthorizationHelper.getPermission( + unitConfig, + authorizationToken.getUserId(), + unitRegistry.getAuthorizationGroupMap(), + unitRegistry.getLocationMap() + ) + + // reject the token if the user tries to give more permissions than he has + if (!AuthorizationHelper.isSubPermission(permission, permissionRule.permission)) { + throw RejectedException( + "User[" + authorizationToken.getUserId() + "] has not enough permissions to create an authorizationToken with permissions[" + permissionRule.permission + "] for unit[" + UnitConfigProcessor.getDefaultAlias( + unitConfig, + "?" + ) + "]" + ) + } + } + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could verify access token", ex) + } + } + + /** + * Perform a permission check by validating that the actor is either the receiver or the sender of the message. + * + * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization + * @param userMessage the user message for which permissions are checked + * @param permissionType the permission type which is checked + * @param unitRegistry unit registry used to resolve ids + * + * @return a string representing the authorized user, this is either just the username of the authenticated user + * or the username of the authenticated user followed by the username of the issuer of the authorization token + * + * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails + */ + @JvmStatic + @Throws(CouldNotPerformException::class) + fun canDo( + authenticationBaseData: AuthenticationBaseData?, + userMessage: UserMessageType.UserMessage, + permissionType: AuthorizationHelper.PermissionType, + unitRegistry: UnitRegistry, + ): AuthPair { + try { + // validate sender + canDo( + authenticationBaseData, + unitRegistry.getUnitConfigById(userMessage.getSenderId()), + permissionType, + unitRegistry, + null, + null + ) + } catch (ex: CouldNotPerformException) { + var exceptionStack: ExceptionStack? = null + exceptionStack = MultiException.push(AuthorizationWithTokenHelper::class.java, ex, exceptionStack) + + // validate receiver if sender validation failed. + try { + canDo( + authenticationBaseData, + unitRegistry.getUnitConfigById(userMessage.getRecipientId()), + permissionType, + unitRegistry, + null, + null + ) + } catch (exx: CouldNotPerformException) { + exceptionStack = MultiException.push(AuthorizationWithTokenHelper::class.java, exx, exceptionStack) + + userMessage.conditionList.forEach { condition -> + try { + return canDo( + authenticationBaseData, + unitRegistry.getUnitConfigById(condition.unitId), + permissionType, + unitRegistry, + null, + null + ) + } catch (exxx: CouldNotPerformException) { + exceptionStack = + MultiException.push(AuthorizationWithTokenHelper::class.java, exx, exceptionStack) + } + } + + MultiException.checkAndThrow({ "Permission denied!" }, exceptionStack) + } + } + throw FatalImplementationErrorException( + "ExceptionStack empty in error case.", + AuthorizationWithTokenHelper::class.java + ) + } + + /** + * Perform a permission check for authentication data including tokens. + * + * @param authenticationBaseData the authentication data including who is authenticated and tokens used for authorization + * @param unitConfig the unit config for which permissions are checked + * @param permissionType the permission type which is checked + * @param unitRegistry unit registry used to resolve ids + * @param unitType the unit type for which is checked if the authorization tokens gives permissions for it, if it is null + * it will be ignored + * @param serviceType the service type for which is checked if the authorization tokens gives permissions for it, if it is null + * it will be ignored + * + * @return a string representing the authorized user, this is either just the username of the authenticated user + * or the username of the authenticated user followed by the username of the issuer of the authorization token + * + * @throws CouldNotPerformException thrown if the user does not have permissions or if the check fails + */ + @JvmStatic + @JvmOverloads + @Throws(CouldNotPerformException::class) + fun canDo( + authenticationBaseData: AuthenticationBaseData?, + unitConfig: UnitConfigType.UnitConfig, + permissionType: AuthorizationHelper.PermissionType, + unitRegistry: UnitRegistry, + unitType: UnitTemplateType.UnitTemplate.UnitType? = null, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType? = null, + ): AuthPair { + try { + // resolve the responsible user + val userClientPair: UserClientPairType.UserClientPair + userClientPair = if (authenticationBaseData == null) { + // authentication data is not given so use null to check for other permissions + UserClientPairType.UserClientPair.getDefaultInstance() + } else { + if (authenticationBaseData.authenticationToken != null) { + // authentication token is set so use it as the responsible user + UserClientPairType.UserClientPair.newBuilder() + .setUserId(authenticationBaseData.authenticationToken.getUserId()).build() + } else { + // use the user that is authenticated for the request + authenticationBaseData.userClientPair + } + } + + // check if authenticated user has needed permissions + if (AuthorizationHelper.canDo( + unitConfig, + userClientPair.getUserId(), + unitRegistry.getAuthorizationGroupMap(), + unitRegistry.getLocationMap(), + permissionType + ) + ) { + return AuthPair(userClientPair, userClientPair.getUserId()) + } + if (AuthorizationHelper.canDo( + unitConfig, + userClientPair.getClientId(), + unitRegistry.getAuthorizationGroupMap(), + unitRegistry.getLocationMap(), + permissionType + ) + ) { + return AuthPair(userClientPair, userClientPair.getClientId()) + } + try { + // test if user is part of the admin group + val memberIdList = + unitRegistry.getUnitConfigByAlias(UnitRegistry.ADMIN_GROUP_ALIAS).authorizationGroupConfig.memberIdList + if (memberIdList.contains(userClientPair.getUserId())) { + return AuthPair(userClientPair, userClientPair.getUserId()) + } + if (memberIdList.contains(userClientPair.getClientId())) { + return AuthPair(userClientPair, userClientPair.getClientId()) + } + } catch (ex: NotAvailableException) { + // continue with the checks, admin group is not available + } + + // authenticated user does not have permissions so check if the authorization token grants them + if (authenticationBaseData != null && authenticationBaseData.authorizationToken != null) { + val authorizationToken = authenticationBaseData.authorizationToken + // verify that the authorization token is valid + verifyAuthorizationToken(authorizationToken, unitRegistry) + + // verify if the token grants the necessary permissions + return authorizedByToken( + authorizationToken, + userClientPair, + unitConfig, + permissionType, + unitRegistry, + unitType, + serviceType + ) + } + var userRepresentation = userClientPair.getUserId() + if (!userRepresentation.isEmpty()) { + userRepresentation += "@" + } + userRepresentation += userClientPair.getClientId() + if (userRepresentation.isEmpty()) { + userRepresentation = "Other" + } + throw PermissionDeniedException("User[" + userRepresentation + "] " + permissionType.name.lowercase(Locale.getDefault()) + " permission denied!") + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException( + "Could not verify permissions for unit[" + UnitConfigProcessor.getDefaultAlias( + unitConfig, + "?" + ) + "]", ex + ) + } + } + + @Throws(CouldNotPerformException::class) + private fun authorizedByToken( + authorizationToken: AuthorizationTokenType.AuthorizationToken, + userClientPair: UserClientPairType.UserClientPair, + unitConfig: UnitConfigType.UnitConfig, + permissionType: AuthorizationHelper.PermissionType, + unitRegistry: UnitRegistry, + unitType: UnitTemplateType.UnitTemplate.UnitType?, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType?, + ): AuthPair { + // verify if the token grants the necessary permissions + val templateRegistry: TemplateRegistry = CachedTemplateRegistryRemote.getRegistry() + val grantingPermissionSet: MutableSet = HashSet() + val filteringPermissionSet: MutableSet = HashSet() + for (permissionRule in authorizationToken.permissionRuleList) { + if (permissionRule.filter) { + filteringPermissionSet.add(permissionRule) + } else { + grantingPermissionSet.add(permissionRule) + } + } + var granted = false + for (permissionRule in grantingPermissionSet) { + if (permitted( + unitConfig, + permissionRule, + unitRegistry, + templateRegistry, + serviceType, + unitType, + permissionType + ) + ) { + granted = true + break + } + } + if (!granted) { + throw PermissionDeniedException("Authorization token does not grant the necessary permissions") + } + for (permissionRule in filteringPermissionSet) { + if (permitted( + unitConfig, + permissionRule, + unitRegistry, + templateRegistry, + serviceType, + unitType, + permissionType + ) + ) { + throw PermissionDeniedException("Authorization token does not grant the necessary permissions") + } + } + + // build the auth pair + return AuthPair(userClientPair, authorizationToken.getUserId()) + } + + @Throws(CouldNotPerformException::class) + private fun permitted( + unitConfig: UnitConfigType.UnitConfig, + permissionRule: AuthorizationTokenType.AuthorizationToken.PermissionRule, + unitRegistry: UnitRegistry, + templateRegistry: TemplateRegistry, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType?, + unitType: UnitTemplateType.UnitTemplate.UnitType?, + permissionType: AuthorizationHelper.PermissionType, + ): Boolean { + // the permission would not grant these permissions anyway so return false + // this is done first so that rejections are handled fast + if (!AuthorizationHelper.permitted(permissionRule.permission, permissionType)) { + return false + } + // test if the unit id in the permission rule matches the unit config + if (permissionRule.getUnitId() != unitConfig.getId()) { + // it does not so test if the unit id belongs to a location + val locationToCheck = unitRegistry.getUnitConfigById(permissionRule.getUnitId()) + // if it is not a location then the permission rule do not permit what is asked + if (locationToCheck.getUnitType() != UnitTemplateType.UnitTemplate.UnitType.LOCATION) { + return false + } + // if the location does not contain the given unit config the permission rule does not permit it + if (!containsUnit(unitConfig, locationToCheck, unitRegistry)) { + return false + } + } + // if the given service type is defined and the rule contains a service type which does not match return false + return if (serviceType != null && serviceType != ServiceTemplateType.ServiceTemplate.ServiceType.UNKNOWN && permissionRule.hasServiceTemplateId() && templateRegistry.getServiceTemplateById( + permissionRule.getServiceTemplateId() + ) + .getServiceType() != serviceType + ) { + false + } else unitType == null || unitType == UnitTemplateType.UnitTemplate.UnitType.UNKNOWN || !permissionRule.hasUnitTemplateId() || templateRegistry.getUnitTemplateById( + permissionRule.getUnitTemplateId() + ).getUnitType() == unitType + // if the given unit type is defined and the rule contains a unit type which does not match return false + } + + @Throws(CouldNotPerformException::class) + private fun containsUnit( + unitConfig: UnitConfigType.UnitConfig, + locationToCheck: UnitConfigType.UnitConfig, + unitRegistry: UnitRegistry, + ): Boolean { + var location = unitRegistry.getUnitConfigById(unitConfig.placementConfig.getLocationId()) + if (location.getId() == locationToCheck.getId()) { + return true + } + while (!location.locationConfig.root) { + location = unitRegistry.getUnitConfigById(location.placementConfig.getLocationId()) + if (location.getId() == locationToCheck.getId()) { + return true + } + } + return false + } +} diff --git a/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java b/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java index 4f007b80b3..69a94d2486 100644 --- a/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java +++ b/module/registry/util/src/main/java/org/openbase/bco/registry/mock/MockRegistry.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -31,12 +31,12 @@ import org.openbase.bco.registry.activity.core.ActivityRegistryLauncher; import org.openbase.bco.registry.clazz.core.ClassRegistryLauncher; import org.openbase.bco.registry.lib.jp.JPBCODatabaseDirectory; +import org.openbase.bco.registry.message.core.MessageRegistryLauncher; import org.openbase.bco.registry.remote.Registries; import org.openbase.bco.registry.template.core.TemplateRegistryLauncher; import org.openbase.bco.registry.unit.core.UnitRegistryLauncher; import org.openbase.jps.core.JPService; import org.openbase.jps.exception.JPServiceException; -import org.openbase.jps.preset.JPTestMode; import org.openbase.jps.preset.JPTmpDirectory; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.FatalImplementationErrorException; @@ -45,7 +45,6 @@ import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.exception.printer.LogLevel; import org.openbase.jul.extension.protobuf.IdentifiableMessage; -import org.openbase.jul.extension.protobuf.ProtoBufBuilderProcessor; import org.openbase.jul.extension.protobuf.container.ProtoBufMessageMap; import org.openbase.jul.extension.type.processing.LabelProcessor; import org.openbase.jul.processing.StringProcessor; @@ -174,6 +173,7 @@ public class MockRegistry { public static final String USER_NAME = "uSeRnAmE"; public static final String USER_FIRST_NAME = "Max"; public static final String USER_LAST_NAME = "Mustermann"; + public static final Map APP_CLASS_LABEL_ID_MAP = new HashMap<>(); public static final Map AGENT_CLASS_LABEL_ID_MAP = new HashMap<>(); public static final AxisAlignedBoundingBox3DFloat DEFAULT_BOUNDING_BOX = AxisAlignedBoundingBox3DFloat.newBuilder() .setHeight(10) @@ -191,6 +191,7 @@ public class MockRegistry { private static ClassRegistryLauncher classRegistryLauncher; private static TemplateRegistryLauncher templateRegistryLauncher; private static UnitRegistryLauncher unitRegistryLauncher; + private static MessageRegistryLauncher messageRegistryLauncher; protected MockRegistry() throws InstantiationException { try { @@ -263,6 +264,15 @@ protected MockRegistry() throws InstantiationException { } return null; })); + registryStartupTasks.add(GlobalCachedExecutorService.submit(() -> { + try { + messageRegistryLauncher = new MessageRegistryLauncher(); + messageRegistryLauncher.launch().get(); + } catch (CouldNotPerformException ex) { + throw ExceptionPrinter.printHistoryAndReturnThrowable(ex, LOGGER, LogLevel.ERROR); + } + return null; + })); LOGGER.debug("Starting all registries: unit, class, template, activity..."); for (Future task : registryStartupTasks) { while (true) { @@ -283,7 +293,7 @@ protected MockRegistry() throws InstantiationException { Registries.waitForData(); - if(loadTestData) { + if (loadTestData) { registryStartupTasks.add(GlobalCachedExecutorService.submit(() -> { LOGGER.debug("Update serviceTemplates..."); for (MockServiceTemplate mockServiceTemplate : MockServiceTemplate.values()) { @@ -473,6 +483,14 @@ public static UnitConfig.Builder generateAgentConfig(final String agentClassLabe return agentUnitConfig; } + public static UnitConfig.Builder generateAppConfig(final String alias, final String locationAlias) throws CouldNotPerformException { + final UnitConfig.Builder appUnitConfig = UnitConfig.newBuilder().setUnitType(UnitType.APP); + appUnitConfig.getPlacementConfigBuilder().setLocationId(Registries.getUnitRegistry().getUnitConfigByAlias(locationAlias).getId()); + LabelProcessor.addLabel(appUnitConfig.getLabelBuilder(), Locale.ENGLISH, alias); + appUnitConfig.addAlias(alias); + return appUnitConfig; + } + protected void shutdown() { if (unitRegistryLauncher != null) { unitRegistryLauncher.shutdown();