diff --git a/bom/opensmarthouse-core/pom.xml b/bom/opensmarthouse-core/pom.xml
index 9244035a3..55dfa785a 100755
--- a/bom/opensmarthouse-core/pom.xml
+++ b/bom/opensmarthouse-core/pom.xml
@@ -117,6 +117,12 @@
${project.version}compile
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth
+ ${project.version}
+ compile
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.auth.jaas
diff --git a/bom/opensmarthouse/pom.xml b/bom/opensmarthouse/pom.xml
index 8d270d639..54ca87cca 100755
--- a/bom/opensmarthouse/pom.xml
+++ b/bom/opensmarthouse/pom.xml
@@ -162,6 +162,11 @@
org.opensmarthouse.core.id${project.version}
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.auth
+ ${project.version}
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.persistence
@@ -217,11 +222,51 @@
org.opensmarthouse.core.auth${project.version}
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.apitoken
+ ${project.version}
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.apitoken.provider
+ ${project.version}
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.cookie
+ ${project.version}
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.core
+ ${project.version}
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.auth.jaas${project.version}
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.jwt
+ ${project.version}
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.jwt.provider
+ ${project.version}
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local
+ ${project.version}
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local.provider
+ ${project.version}
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.auth.oauth2client
@@ -232,6 +277,11 @@
org.opensmarthouse.core.auth.oauth2client.core${project.version}
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.password
+ ${project.version}
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.io.net
@@ -287,11 +337,26 @@
org.opensmarthouse.core.io.http.auth${project.version}
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.auth.apitoken
+ ${project.version}
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.io.http.auth.basic${project.version}
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.auth.jwt
+ ${project.version}
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.facade
+ ${project.version}
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.io.monitor
@@ -307,6 +372,11 @@
org.opensmarthouse.core.io.rest.auth${project.version}
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.rest.auth.local
+ ${project.version}
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.io.rest.binding
diff --git a/bundles/org.opensmarthouse.core.auth.apitoken.provider/NOTICE b/bundles/org.opensmarthouse.core.auth.apitoken.provider/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.apitoken.provider/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.auth.apitoken.provider/pom.xml b/bundles/org.opensmarthouse.core.auth.apitoken.provider/pom.xml
new file mode 100755
index 000000000..e557fa637
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.apitoken.provider/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.apitoken.provider
+
+ OpenSmartHouse Core | Bundles | Apitoken Authentication Provider
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.apitoken
+
+
+ org.osgi
+ osgi.cmpn
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+ org.mockito
+ mockito-junit-jupiter
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth.apitoken.provider/src/main/java/org/openhab/core/auth/apitoken/provider/internal/ApitokenAuthenticationProvider.java b/bundles/org.opensmarthouse.core.auth.apitoken.provider/src/main/java/org/openhab/core/auth/apitoken/provider/internal/ApitokenAuthenticationProvider.java
new file mode 100644
index 000000000..fdd5ed22f
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.apitoken.provider/src/main/java/org/openhab/core/auth/apitoken/provider/internal/ApitokenAuthenticationProvider.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth.apitoken.provider.internal;
+
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.AuthenticationProvider;
+import org.openhab.core.auth.AuthenticationResult;
+import org.openhab.core.auth.Credentials;
+import org.openhab.core.auth.local.GenericUser;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserApiToken;
+import org.openhab.core.auth.local.UserRegistry;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.openhab.core.auth.apitoken.UserApiTokenCredentials;
+
+/**
+ * Authentication provider which is able to correlate received authentication token.
+ *
+ * @author Yannick Schaus - initial contribution
+ * @author Łukasz Dywicki - Extracted from io.rest.auth module.
+ */
+@Component
+public class ApitokenAuthenticationProvider implements AuthenticationProvider {
+
+ private final UserRegistry userRegistry;
+
+ @Activate
+ public ApitokenAuthenticationProvider(@Reference UserRegistry userRegistry) {
+ this.userRegistry = userRegistry;
+ }
+
+ @Override
+ public AuthenticationResult authenticate(Credentials credentials) throws AuthenticationException {
+ if (!(credentials instanceof UserApiTokenCredentials)) {
+ throw new IllegalArgumentException("Invalid credential type");
+ }
+
+ UserApiTokenCredentials apiTokenCreds = (UserApiTokenCredentials) credentials;
+ String[] apiTokenParts = apiTokenCreds.getApiToken().split("\\.");
+ if (apiTokenParts.length != 3 || !UserRegistry.APITOKEN_PREFIX.equals(apiTokenParts[0])) {
+ throw new AuthenticationException("Invalid API token format");
+ }
+ for (User user : userRegistry.getAll()) {
+ ManagedUser managedUser = (ManagedUser) user;
+ for (UserApiToken userApiToken : managedUser.getApiTokens()) {
+ // only check if the name in the token matches
+ if (!userApiToken.getName().equals(apiTokenParts[1])) {
+ continue;
+ }
+ String[] existingTokenHashAndSalt = userApiToken.getApiToken().split(":");
+ String incomingTokenHash = UserRegistry.hash(apiTokenCreds.getApiToken(), existingTokenHashAndSalt[1],
+ UserRegistry.APITOKEN_ITERATIONS).get();
+
+ if (incomingTokenHash.equals(existingTokenHashAndSalt[0])) {
+ return new AuthenticationResult(new GenericUser(managedUser.getName()), credentials.getScheme(),
+ new Authentication(managedUser.getName(), managedUser.getRoles().stream().toArray(String[]::new), userApiToken.getScope())
+ );
+ }
+ }
+ }
+
+ throw new AuthenticationException("Unknown API token");
+ }
+
+ @Override
+ public boolean supports(Class extends Credentials> type) {
+ return UserApiTokenCredentials.class.isAssignableFrom(type);
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth.apitoken.provider/src/test/java/org/openhab/core/auth/apitoken/provider/internal/ApitokenAuthenticationProviderTest.java b/bundles/org.opensmarthouse.core.auth.apitoken.provider/src/test/java/org/openhab/core/auth/apitoken/provider/internal/ApitokenAuthenticationProviderTest.java
new file mode 100644
index 000000000..26d756b55
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.apitoken.provider/src/test/java/org/openhab/core/auth/apitoken/provider/internal/ApitokenAuthenticationProviderTest.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth.apitoken.provider.internal;
+
+import static org.mockito.Mockito.*;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.UserApiToken;
+import org.openhab.core.auth.local.UserRegistry;
+import org.openhab.core.auth.apitoken.UserApiTokenCredentials;
+
+@ExtendWith(MockitoExtension.class)
+class ApitokenAuthenticationProviderTest {
+
+ public static final String TOKEN_X = "X";
+ public static final String TOKEN_Y = "Y";
+ public static final String TOKEN_Z = "Z";
+ private static final String TOKEN_SALT = "asdf";
+
+
+ @Mock
+ private UserRegistry registry;
+
+ private ApitokenAuthenticationProvider provider;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ provider = new ApitokenAuthenticationProvider(registry);
+
+ List users = Arrays.asList(
+ createUser("foo", "x", TOKEN_SALT, TOKEN_X),
+ createUser("bar", "y", TOKEN_SALT, TOKEN_Y),
+ createUser("baz", "z", TOKEN_SALT, TOKEN_Z)
+ );
+ when(registry.getAll()).thenAnswer((inv) -> users);
+ }
+
+ @Test
+ public void testApiTokens() throws Exception {
+ String token1 = UserRegistry.APITOKEN_PREFIX + ".x." + TOKEN_X;
+ String token2 = UserRegistry.APITOKEN_PREFIX + ".y." + TOKEN_Y;
+ String token3 = UserRegistry.APITOKEN_PREFIX + ".z." + TOKEN_Z;
+
+ provider.authenticate(new UserApiTokenCredentials(token1));
+ provider.authenticate(new UserApiTokenCredentials(token2));
+ provider.authenticate(new UserApiTokenCredentials(token3));
+ }
+
+ @Test
+ public void testInvalidTokens() throws Exception {
+ String token1 = UserRegistry.APITOKEN_PREFIX + ".x.x";
+ String token2 = UserRegistry.APITOKEN_PREFIX + ".Y.y";
+
+ Assertions.assertThrows(AuthenticationException.class, () -> provider.authenticate(new UserApiTokenCredentials(token1)));
+ Assertions.assertThrows(AuthenticationException.class, () -> provider.authenticate(new UserApiTokenCredentials(token2)));
+ }
+
+ private ManagedUser createUser(String name, String tokenId, String tokenSalt, String tokenValue) {
+ ManagedUser user = mock(ManagedUser.class, name);
+
+ String token = UserRegistry.APITOKEN_PREFIX + "." + tokenId + "." + tokenValue;
+ String tokenHash = UserRegistry.hash(token, tokenSalt, UserRegistry.APITOKEN_ITERATIONS).get();
+ System.out.println(token);
+
+ UserApiToken userApiToken = new UserApiToken(tokenId, tokenHash + ":" + tokenSalt, "");
+ when(user.getApiTokens()).thenReturn(Collections.singletonList(userApiToken));
+ return user;
+ }
+
+}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.auth.apitoken/NOTICE b/bundles/org.opensmarthouse.core.auth.apitoken/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.apitoken/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.auth.apitoken/pom.xml b/bundles/org.opensmarthouse.core.auth.apitoken/pom.xml
new file mode 100755
index 000000000..fbde0b1c5
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.apitoken/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.apitoken
+
+ OpenSmartHouse Core | Bundles | Apitoken Authentication
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.core
+
+
+ org.osgi
+ osgi.cmpn
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserApiTokenCredentials.java b/bundles/org.opensmarthouse.core.auth.apitoken/src/main/java/org/openhab/core/auth/apitoken/UserApiTokenCredentials.java
similarity index 85%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserApiTokenCredentials.java
rename to bundles/org.opensmarthouse.core.auth.apitoken/src/main/java/org/openhab/core/auth/apitoken/UserApiTokenCredentials.java
index 7c893e67e..a548aced4 100644
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserApiTokenCredentials.java
+++ b/bundles/org.opensmarthouse.core.auth.apitoken/src/main/java/org/openhab/core/auth/apitoken/UserApiTokenCredentials.java
@@ -10,7 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.apitoken;
+
+import org.openhab.core.auth.Credentials;
/**
* Credentials which represent a user API token.
@@ -38,4 +40,10 @@ public UserApiTokenCredentials(String userApiToken) {
public String getApiToken() {
return userApiToken;
}
+
+ @Override
+ public String getScheme() {
+ return "apitoken";
+ }
+
}
diff --git a/bundles/org.opensmarthouse.core.auth.cookie/pom.xml b/bundles/org.opensmarthouse.core.auth.cookie/pom.xml
new file mode 100755
index 000000000..5e93d4f76
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.cookie/pom.xml
@@ -0,0 +1,23 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.cookie
+
+ OpenSmartHouse Core | Bundles | Cookie Credentials
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth.cookie/src/main/java/org/openhab/core/auth/cookie/CookieCredentials.java b/bundles/org.opensmarthouse.core.auth.cookie/src/main/java/org/openhab/core/auth/cookie/CookieCredentials.java
new file mode 100644
index 000000000..cfffe7d59
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.cookie/src/main/java/org/openhab/core/auth/cookie/CookieCredentials.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth.cookie;
+
+import org.openhab.core.auth.Credentials;
+
+/**
+ * Credentials which represent key/value pair coming from HTTP cookie.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+public class CookieCredentials implements Credentials {
+
+ private final String name;
+ private final String value;
+
+ public CookieCredentials(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String getScheme() {
+ return "cookie";
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/AuthenticationManagerImpl.java b/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/AuthenticationManagerImpl.java
index a8845f322..7a4d838d8 100755
--- a/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/AuthenticationManagerImpl.java
+++ b/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/AuthenticationManagerImpl.java
@@ -19,6 +19,7 @@
import org.openhab.core.auth.AuthenticationException;
import org.openhab.core.auth.AuthenticationManager;
import org.openhab.core.auth.AuthenticationProvider;
+import org.openhab.core.auth.AuthenticationResult;
import org.openhab.core.auth.Credentials;
import org.openhab.core.auth.UnsupportedCredentialsException;
import org.osgi.service.component.annotations.Component;
@@ -41,15 +42,15 @@ public class AuthenticationManagerImpl implements AuthenticationManager {
private final List providers = new CopyOnWriteArrayList<>();
@Override
- public Authentication authenticate(Credentials credentials) throws AuthenticationException {
+ public AuthenticationResult authenticate(Credentials credentials) throws AuthenticationException {
boolean unmatched = true;
for (AuthenticationProvider provider : providers) {
if (provider.supports(credentials.getClass())) {
unmatched = false;
try {
- Authentication authentication = provider.authenticate(credentials);
- if (authentication != null) {
- return authentication;
+ AuthenticationResult authenticationResult = provider.authenticate(credentials);
+ if (authenticationResult != null) {
+ return authenticationResult;
}
} catch (AuthenticationException e) {
logger.info("Failed to authenticate credentials {} with provider {}", credentials.getClass(),
diff --git a/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/AuthorizationManagerImpl.java b/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/AuthorizationManagerImpl.java
new file mode 100644
index 000000000..68a9fe9b8
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/AuthorizationManagerImpl.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.internal.auth;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthorizationManager;
+import org.openhab.core.auth.Permission;
+import org.openhab.core.auth.PermissionEvaluator;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+public class AuthorizationManagerImpl implements AuthorizationManager {
+
+ private final Logger logger = LoggerFactory.getLogger(AuthorizationManagerImpl.class);
+ private List> evaluators = new CopyOnWriteArrayList<>();
+
+ @Override
+ public boolean hasPermission(Permission permission, T object, Authentication authentication) {
+ if (authentication == null) {
+ return false;
+ }
+
+ for (PermissionEvaluator
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.password
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local
+ org.osgiosgi.cmpn
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.config
+
diff --git a/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/JaasAuthenticationProvider.java b/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/JaasAuthenticationProvider.java
index 9799cd8c8..aa737c9aa 100755
--- a/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/JaasAuthenticationProvider.java
+++ b/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/JaasAuthenticationProvider.java
@@ -32,11 +32,15 @@
import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthenticationException;
import org.openhab.core.auth.AuthenticationProvider;
+import org.openhab.core.auth.AuthenticationResult;
import org.openhab.core.auth.Credentials;
-import org.openhab.core.auth.GenericUser;
-import org.openhab.core.auth.UsernamePasswordCredentials;
+import org.openhab.core.auth.local.GenericUser;
+import org.openhab.core.auth.password.UsernamePasswordCredentials;
+import org.openhab.core.config.core.ConfigurableService;
+import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
@@ -50,14 +54,17 @@
* @author Yannick Schaus - provides a configuration with the ManagedUserLoginModule as a sufficient login module
*/
@NonNullByDefault
-@Component(configurationPid = "org.openhab.jaas")
+@Component(configurationPid = "org.openhab.jaas", property = Constants.SERVICE_PID + "=org.openhab.jaas", configurationPolicy = ConfigurationPolicy.REQUIRE)
+@ConfigurableService(category = "auth", label = "JAAS Authentication", description_uri = JaasAuthenticationProvider.CONFIG_URI)
public class JaasAuthenticationProvider implements AuthenticationProvider {
+
private static final String DEFAULT_REALM = "openhab";
+ static final String CONFIG_URI = "auth:jaas";
private @Nullable String realmName;
@Override
- public Authentication authenticate(final Credentials credentials) throws AuthenticationException {
+ public AuthenticationResult authenticate(final Credentials credentials) throws AuthenticationException {
if (realmName == null) { // configuration is not yet ready or set
realmName = DEFAULT_REALM;
}
@@ -93,7 +100,7 @@ public void handle(@NonNullByDefault({}) Callback[] callbacks)
}, new ManagedUserLoginConfiguration());
loginContext.login();
- return getAuthentication(name, loginContext.getSubject());
+ return getAuthentication(name, userCredentials.getScheme(), loginContext.getSubject());
} catch (LoginException e) {
throw new AuthenticationException(e.getMessage());
} finally {
@@ -101,8 +108,10 @@ public void handle(@NonNullByDefault({}) Callback[] callbacks)
}
}
- private Authentication getAuthentication(String name, Subject subject) {
- return new Authentication(name, getRoles(subject.getPrincipals()));
+ private AuthenticationResult getAuthentication(String name, String scheme, Subject subject) {
+ return new AuthenticationResult(subject.getPrincipals().iterator().next(), scheme,
+ new Authentication(name, getRoles(subject.getPrincipals()))
+ );
}
private String[] getRoles(Set principals) {
diff --git a/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/ManagedUserLoginModule.java b/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/ManagedUserLoginModule.java
index 31ff32224..1c4e7def7 100755
--- a/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/ManagedUserLoginModule.java
+++ b/bundles/org.opensmarthouse.core.auth.jaas/src/main/java/org/openhab/core/auth/jaas/internal/ManagedUserLoginModule.java
@@ -16,12 +16,13 @@
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.openhab.core.auth.AuthenticationException;
import org.openhab.core.auth.Credentials;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.UserRegistry;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
@@ -37,8 +38,6 @@ public class ManagedUserLoginModule implements LoginModule {
private final Logger logger = LoggerFactory.getLogger(ManagedUserLoginModule.class);
- private UserRegistry userRegistry;
-
private Subject subject;
@Override
@@ -49,23 +48,25 @@ public void initialize(Subject subject, CallbackHandler callbackHandler, Map serviceReference = null;
try {
// try to get the UserRegistry instance
- BundleContext bundleContext = FrameworkUtil.getBundle(UserRegistry.class).getBundleContext();
- ServiceReference serviceReference = bundleContext.getServiceReference(UserRegistry.class);
-
- userRegistry = bundleContext.getService(serviceReference);
- } catch (Exception e) {
- logger.error("Cannot initialize the ManagedLoginModule", e);
- throw new LoginException("Authorization failed");
- }
+ serviceReference = bundleContext.getServiceReference(UserRegistry.class);
- try {
+ UserRegistry userRegistry = bundleContext.getService(serviceReference);
Credentials credentials = (Credentials) this.subject.getPrivateCredentials().iterator().next();
userRegistry.authenticate(credentials);
return true;
} catch (AuthenticationException e) {
- throw new LoginException(e.getMessage());
+ throw new FailedLoginException(e.getMessage());
+ } catch (Exception e) {
+ logger.error("Cannot initialize the ManagedLoginModule", e);
+ throw new LoginException("Authorization failed");
+ } finally {
+ if (serviceReference != null) {
+ bundleContext.ungetService(serviceReference);
+ }
}
}
diff --git a/bundles/org.opensmarthouse.core.auth.jaas/src/main/resources/OH-INF/config/config.xml b/bundles/org.opensmarthouse.core.auth.jaas/src/main/resources/OH-INF/config/config.xml
new file mode 100755
index 000000000..e58bba83b
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jaas/src/main/resources/OH-INF/config/config.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Name of underlying JAAS realm used to verify user credentials.
+ This parameter points to actual configuration which is used to authenticate user.
+
+ openhab
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth.jwt.provider/NOTICE b/bundles/org.opensmarthouse.core.auth.jwt.provider/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt.provider/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.auth.jwt.provider/pom.xml b/bundles/org.opensmarthouse.core.auth.jwt.provider/pom.xml
new file mode 100755
index 000000000..88a6fda77
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt.provider/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.jwt.provider
+
+ OpenSmartHouse Core | Bundles | JWT Authentication Provider
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.jwt
+
+
+ org.bitbucket.b_c
+ jose4j
+
+
+ org.osgi
+ org.osgi.service.component.annotations
+ 1.4.0
+ provided
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.config
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/java/org/openhab/core/auth/jwt/provider/internal/ClaimSetPrincipal.java b/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/java/org/openhab/core/auth/jwt/provider/internal/ClaimSetPrincipal.java
new file mode 100644
index 000000000..99680ec76
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/java/org/openhab/core/auth/jwt/provider/internal/ClaimSetPrincipal.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth.jwt.provider.internal;
+
+import java.security.Principal;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.jwt.MalformedClaimException;
+
+public class ClaimSetPrincipal implements Principal {
+
+ private final String name;
+
+ public ClaimSetPrincipal(JwtClaims claimsSet) throws MalformedClaimException {
+ this(claimsSet.getStringClaimValue("preferred_username"));
+ }
+
+ public ClaimSetPrincipal(String username) {
+ this.name = username;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/java/org/openhab/core/auth/jwt/provider/internal/JwtAuthenticationProvider.java b/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/java/org/openhab/core/auth/jwt/provider/internal/JwtAuthenticationProvider.java
new file mode 100755
index 000000000..fc63e459c
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/java/org/openhab/core/auth/jwt/provider/internal/JwtAuthenticationProvider.java
@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth.jwt.provider.internal;
+
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
+import org.jose4j.jwk.HttpsJwks;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.jwt.MalformedClaimException;
+import org.jose4j.jwt.consumer.InvalidJwtException;
+import org.jose4j.jwt.consumer.JwtConsumer;
+import org.jose4j.jwt.consumer.JwtConsumerBuilder;
+import org.jose4j.jwt.consumer.JwtContext;
+import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.AuthenticationProvider;
+import org.openhab.core.auth.AuthenticationResult;
+import org.openhab.core.auth.Credentials;
+import org.openhab.core.auth.jwt.JWTCredentials;
+import org.openhab.core.config.core.ConfigurableService;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+
+/**
+ * Implementation of authentication provider which is backed by JWT realm.
+ *
+ * Real authentication logic is embedded in login modules implemented by 3rd party, this code is just for bridging it to
+ * smarthome platform.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "org.openhab.jwt", property = Constants.SERVICE_PID + "=org.openhab.jwt", configurationPolicy = ConfigurationPolicy.REQUIRE)
+@ConfigurableService(category = "auth", label = "JWT Authentication", description_uri = JwtAuthenticationProvider.CONFIG_URI)
+public class JwtAuthenticationProvider implements AuthenticationProvider {
+
+ static final String CONFIG_URI = "auth:jwt";
+ private @Nullable JwtConsumer jwtProcessor;
+
+ @Override
+ public AuthenticationResult authenticate(final Credentials credentials) throws AuthenticationException {
+ if (jwtProcessor == null) { // configuration is not yet ready or set
+ throw new AuthenticationException("Authenticator is not set up");
+ }
+
+ if (!(credentials instanceof JWTCredentials)) {
+ throw new AuthenticationException("Unsupported credentials passed to provider.");
+ }
+
+ JWTCredentials userCredentials = (JWTCredentials) credentials;
+ final String token = userCredentials.getToken();
+
+ try {
+ JwtContext jwtContext = jwtProcessor.process(token);
+ JwtClaims claims = jwtContext.getJwtClaims();
+ return new AuthenticationResult(new ClaimSetPrincipal(claims), credentials.getScheme(),
+ new Authentication(claims.getSubject(), extractRoles(claims))
+ );
+ } catch (InvalidJwtException e) {
+ throw new AuthenticationException("Error while processing token", e);
+ } catch (MalformedClaimException e) {
+ throw new AuthenticationException("Authentication failed", e);
+ }
+ }
+
+ private String[] extractRoles(JwtClaims claims) {
+ Object roles = claims.getClaimValue("roles");
+ if (roles instanceof List) {
+ List rolesClaim = (List) roles;
+ return rolesClaim.toArray(new String[rolesClaim.size()]);
+ }
+ return new String[0];
+ }
+
+ @Activate
+ protected void activate(Map properties) throws ConfigurationException {
+ modified(properties);
+ }
+
+ @Deactivate
+ protected void deactivate(Map properties) {
+ }
+
+ @Modified
+ protected void modified(Map properties) throws ConfigurationException {
+ if (properties == null) {
+ jwtProcessor = null;
+ return;
+ }
+
+ Object jwkSetUrlVal = properties.get("jwkSetUrl");
+ Object signatureAlgorithmVal = properties.get("signatureAlgorithm");
+
+ if (signatureAlgorithmVal != null) {
+ String jwkUrl = null;
+ String signatureAlgorithm = null;
+
+ if (jwkSetUrlVal instanceof String && !((String) jwkSetUrlVal).trim().isEmpty()) {
+ jwkUrl = (String) jwkSetUrlVal;
+ } else {
+ throw new ConfigurationException("jwkSetUrl", "Invalid or empty value for property");
+ }
+ if (signatureAlgorithmVal instanceof String && !((String) signatureAlgorithmVal).trim().isEmpty()) {
+ signatureAlgorithm = (String) signatureAlgorithmVal;
+ } else {
+ throw new ConfigurationException("signatureAlgorithmVal", "Invalid or empty value for property");
+ }
+
+ HttpsJwks httpsJkws = new HttpsJwks(jwkUrl);
+ HttpsJwksVerificationKeyResolver keyResolver = new HttpsJwksVerificationKeyResolver(httpsJkws);
+ jwtProcessor = new JwtConsumerBuilder().setRequireExpirationTime().setAllowedClockSkewInSeconds(30)
+ .setJwsAlgorithmConstraints(ConstraintType.WHITELIST, signatureAlgorithm)
+ .setVerificationKeyResolver(keyResolver)
+ .build();
+ } else {
+ // value could be unset, we should reset its value
+ jwtProcessor = null;
+ }
+ }
+
+ @Override
+ public boolean supports(Class extends Credentials> type) {
+ return JWTCredentials.class.isAssignableFrom(type);
+ }
+}
diff --git a/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/resources/OH-INF/config/config.xml b/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/resources/OH-INF/config/config.xml
new file mode 100755
index 000000000..8a3955d8c
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt.provider/src/main/resources/OH-INF/config/config.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+ A location of Json Web Key set which should be used to validate incoming JWT tokens.
+
+
+
+
+
+ Algorithm used to validate signature.
+
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth.jwt/NOTICE b/bundles/org.opensmarthouse.core.auth.jwt/NOTICE
new file mode 100755
index 000000000..bed08ed3f
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.auth.jwt/pom.xml b/bundles/org.opensmarthouse.core.auth.jwt/pom.xml
new file mode 100755
index 000000000..12a52df9a
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt/pom.xml
@@ -0,0 +1,23 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.jwt
+
+ OpenSmartHouse Core | Bundles | JWT Credentials
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth.jwt/src/main/java/org/openhab/core/auth/jwt/JWTCredentials.java b/bundles/org.opensmarthouse.core.auth.jwt/src/main/java/org/openhab/core/auth/jwt/JWTCredentials.java
new file mode 100644
index 000000000..8bd010311
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.jwt/src/main/java/org/openhab/core/auth/jwt/JWTCredentials.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth.jwt;
+
+import org.openhab.core.auth.Credentials;
+
+public class JWTCredentials implements Credentials {
+
+ private final String token;
+
+ public JWTCredentials(String token) {
+ this.token = token;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public String getScheme() {
+ return "Bearer";
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth.local.provider/NOTICE b/bundles/org.opensmarthouse.core.auth.local.provider/NOTICE
new file mode 100755
index 000000000..bed08ed3f
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.local.provider/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.auth.local.provider/pom.xml b/bundles/org.opensmarthouse.core.auth.local.provider/pom.xml
new file mode 100755
index 000000000..8cbaac1c1
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.local.provider/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.local.provider
+
+ OpenSmartHouse Core | Bundles | Local Authentication Provider
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.password
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.apitoken
+
+
+ org.osgi
+ osgi.cmpn
+
+
+
+ org.mockito
+ mockito-junit-jupiter
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/ManagedUserProvider.java b/bundles/org.opensmarthouse.core.auth.local.provider/src/main/java/org/openhab/core/auth/local/core/internal/ManagedUserProvider.java
similarity index 90%
rename from bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/ManagedUserProvider.java
rename to bundles/org.opensmarthouse.core.auth.local.provider/src/main/java/org/openhab/core/auth/local/core/internal/ManagedUserProvider.java
index f456fafdf..0e2b3b1c6 100755
--- a/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/ManagedUserProvider.java
+++ b/bundles/org.opensmarthouse.core.auth.local.provider/src/main/java/org/openhab/core/auth/local/core/internal/ManagedUserProvider.java
@@ -10,11 +10,11 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.internal.auth;
+package org.openhab.core.auth.local.core.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.User;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
import org.openhab.core.common.registry.DefaultAbstractManagedProvider;
import org.openhab.core.common.registry.ManagedProvider;
import org.openhab.core.storage.StorageService;
diff --git a/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/UserRegistryImpl.java b/bundles/org.opensmarthouse.core.auth.local.provider/src/main/java/org/openhab/core/auth/local/core/internal/UserRegistryImpl.java
similarity index 77%
rename from bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/UserRegistryImpl.java
rename to bundles/org.opensmarthouse.core.auth.local.provider/src/main/java/org/openhab/core/auth/local/core/internal/UserRegistryImpl.java
index 37a1e0c66..d736f5989 100755
--- a/bundles/org.opensmarthouse.core.auth.core/src/main/java/org/openhab/core/internal/auth/UserRegistryImpl.java
+++ b/bundles/org.opensmarthouse.core.auth.local.provider/src/main/java/org/openhab/core/auth/local/core/internal/UserRegistryImpl.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.internal.auth;
+package org.openhab.core.auth.local.core.internal;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -28,15 +28,17 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.AuthenticationProvider;
+import org.openhab.core.auth.AuthenticationResult;
import org.openhab.core.auth.Credentials;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserApiToken;
-import org.openhab.core.auth.UserApiTokenCredentials;
-import org.openhab.core.auth.UserProvider;
-import org.openhab.core.auth.UserRegistry;
-import org.openhab.core.auth.UserSession;
-import org.openhab.core.auth.UsernamePasswordCredentials;
+import org.openhab.core.auth.local.GenericUser;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserApiToken;
+import org.openhab.core.auth.local.UserProvider;
+import org.openhab.core.auth.local.UserRegistry;
+import org.openhab.core.auth.local.UserSession;
+import org.openhab.core.auth.password.UsernamePasswordCredentials;
import org.openhab.core.common.registry.AbstractRegistry;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
@@ -54,16 +56,11 @@
* @author Yannick Schaus - initial contribution
*/
@NonNullByDefault
-@Component(service = UserRegistry.class, immediate = true)
+@Component(service = {UserRegistry.class, AuthenticationProvider.class}, immediate = true)
public class UserRegistryImpl extends AbstractRegistry implements UserRegistry {
private final Logger logger = LoggerFactory.getLogger(UserRegistryImpl.class);
- private static final int PASSWORD_ITERATIONS = 65536;
- private static final int APITOKEN_ITERATIONS = 1024;
- private static final String APITOKEN_PREFIX = "oh";
- private static final int KEY_LENGTH = 512;
- private static final String ALGORITHM = "PBKDF2WithHmacSHA512";
private static final SecureRandom RAND = new SecureRandom();
@Activate
@@ -132,7 +129,7 @@ private Optional hash(String password, String salt, int iterations) {
}
@Override
- public Authentication authenticate(Credentials credentials) throws AuthenticationException {
+ public AuthenticationResult authenticate(Credentials credentials) throws AuthenticationException {
if (credentials instanceof UsernamePasswordCredentials) {
UsernamePasswordCredentials usernamePasswordCreds = (UsernamePasswordCredentials) credentials;
User user = get(usernamePasswordCreds.getUsername());
@@ -147,32 +144,9 @@ public Authentication authenticate(Credentials credentials) throws Authenticatio
throw new AuthenticationException("Wrong password for user " + usernamePasswordCreds.getUsername());
}
- return new Authentication(managedUser.getName(), managedUser.getRoles().stream().toArray(String[]::new));
- } else if (credentials instanceof UserApiTokenCredentials) {
- UserApiTokenCredentials apiTokenCreds = (UserApiTokenCredentials) credentials;
- String[] apiTokenParts = apiTokenCreds.getApiToken().split("\\.");
- if (apiTokenParts.length != 3 || !APITOKEN_PREFIX.equals(apiTokenParts[0])) {
- throw new AuthenticationException("Invalid API token format");
- }
- for (User user : getAll()) {
- ManagedUser managedUser = (ManagedUser) user;
- for (UserApiToken userApiToken : managedUser.getApiTokens()) {
- // only check if the name in the token matches
- if (!userApiToken.getName().equals(apiTokenParts[1])) {
- continue;
- }
- String[] existingTokenHashAndSalt = userApiToken.getApiToken().split(":");
- String incomingTokenHash = hash(apiTokenCreds.getApiToken(), existingTokenHashAndSalt[1],
- APITOKEN_ITERATIONS).get();
-
- if (incomingTokenHash.equals(existingTokenHashAndSalt[0])) {
- return new Authentication(managedUser.getName(),
- managedUser.getRoles().stream().toArray(String[]::new), userApiToken.getScope());
- }
- }
- }
-
- throw new AuthenticationException("Unknown API token");
+ return new AuthenticationResult(new GenericUser(managedUser.getName()), credentials.getScheme(),
+ new Authentication(managedUser.getName(), managedUser.getRoles().stream().toArray(String[]::new))
+ );
}
throw new IllegalArgumentException("Invalid credential type");
diff --git a/bundles/org.opensmarthouse.core.auth.core/src/test/java/org/openhab/core/internal/auth/UserRegistryImplTest.java b/bundles/org.opensmarthouse.core.auth.local.provider/src/test/java/org/openhab/core/auth/local/core/internal/UserRegistryImplTest.java
similarity index 71%
rename from bundles/org.opensmarthouse.core.auth.core/src/test/java/org/openhab/core/internal/auth/UserRegistryImplTest.java
rename to bundles/org.opensmarthouse.core.auth.local.provider/src/test/java/org/openhab/core/auth/local/core/internal/UserRegistryImplTest.java
index 3d64d31a1..b466b7843 100644
--- a/bundles/org.opensmarthouse.core.auth.core/src/test/java/org/openhab/core/internal/auth/UserRegistryImplTest.java
+++ b/bundles/org.opensmarthouse.core.auth.local.provider/src/test/java/org/openhab/core/auth/local/core/internal/UserRegistryImplTest.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.internal.auth;
+package org.openhab.core.auth.local.core.internal;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
@@ -26,11 +26,10 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserApiTokenCredentials;
-import org.openhab.core.auth.UserSession;
-import org.openhab.core.auth.UsernamePasswordCredentials;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.password.UsernamePasswordCredentials;
+import org.openhab.core.auth.local.UserSession;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
@@ -103,24 +102,4 @@ public void testSessions() throws Exception {
assertEquals(user.getSessions().size(), 0);
}
- @Test
- public void testApiTokens() throws Exception {
- ManagedUser user = (ManagedUser) registry.register("username", "password", Set.of("administrator"));
- registry.added(managedProvider, user);
- assertNotNull(user);
- String token1 = registry.addUserApiToken(user, "token1", "scope1");
- String token2 = registry.addUserApiToken(user, "token2", "scope2");
- String token3 = registry.addUserApiToken(user, "token3", "scope3");
- assertEquals(user.getApiTokens().size(), 3);
- registry.authenticate(new UserApiTokenCredentials(token1));
- registry.authenticate(new UserApiTokenCredentials(token2));
- registry.authenticate(new UserApiTokenCredentials(token3));
- registry.removeUserApiToken(user,
- user.getApiTokens().stream().filter(t -> t.getName().equals("token1")).findAny().get());
- registry.removeUserApiToken(user,
- user.getApiTokens().stream().filter(t -> t.getName().equals("token2")).findAny().get());
- registry.removeUserApiToken(user,
- user.getApiTokens().stream().filter(t -> t.getName().equals("token3")).findAny().get());
- assertEquals(user.getApiTokens().size(), 0);
- }
}
diff --git a/bundles/org.opensmarthouse.core.auth.local/NOTICE b/bundles/org.opensmarthouse.core.auth.local/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.local/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.auth.local/pom.xml b/bundles/org.opensmarthouse.core.auth.local/pom.xml
new file mode 100755
index 000000000..d6870c582
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.local/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.local
+
+ OpenSmartHouse Core | Bundles | Local Authentication
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.core
+
+
+ org.osgi
+ osgi.cmpn
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/GenericUser.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/GenericUser.java
similarity index 73%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/GenericUser.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/GenericUser.java
index f0a0ef89b..36c414ee2 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/GenericUser.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/GenericUser.java
@@ -10,8 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -28,16 +29,23 @@ public class GenericUser implements User {
private String name;
private Set roles;
+ private Set permissions;
/**
* Constructs a user attributed with a set of roles.
*
* @param name the username (account name)
* @param roles the roles attributed to this user
+ * @param permissions the items user has access to
*/
- public GenericUser(String name, Set roles) {
+ public GenericUser(String name, Set roles, Set permissions) {
this.name = name;
this.roles = roles;
+ this.permissions = permissions;
+ }
+
+ public GenericUser(String name, Set roles) {
+ this(name, roles, Collections.emptySet());
}
/**
@@ -63,4 +71,13 @@ public String getUID() {
public Set getRoles() {
return roles;
}
+
+ @Override
+ public Set getPermissions() {
+ return permissions;
+ }
+
+ public String toString() {
+ return name + " (" + roles + " " + permissions + ")";
+ }
}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/ManagedUser.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/ManagedUser.java
similarity index 94%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/ManagedUser.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/ManagedUser.java
index 3f132200d..42d3e22d9 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/ManagedUser.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/ManagedUser.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
import java.util.ArrayList;
import java.util.HashSet;
@@ -32,6 +32,7 @@ public class ManagedUser implements User {
private String passwordHash;
private String passwordSalt;
private Set roles = new HashSet<>();
+ private Set permissions = new HashSet<>();
private @Nullable PendingToken pendingToken = null;
private List sessions = new ArrayList<>();
private List apiTokens = new ArrayList<>();
@@ -119,6 +120,14 @@ public void setRoles(Set roles) {
this.roles = roles;
}
+ public void setPermissions(Set permissions) {
+ this.permissions = permissions;
+ }
+
+ public Set getPermissions() {
+ return permissions;
+ }
+
/**
* Gets the pending token information for this user, if any.
*
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/PendingToken.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/PendingToken.java
similarity index 98%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/PendingToken.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/PendingToken.java
index 30342775f..108a86e29 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/PendingToken.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/PendingToken.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/User.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/User.java
similarity index 77%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/User.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/User.java
index 7f11c9b58..2efd3eed7 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/User.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/User.java
@@ -10,12 +10,13 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
import java.security.Principal;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.auth.Role;
import org.openhab.core.common.registry.Identifiable;
/**
@@ -32,5 +33,12 @@ public interface User extends Principal, Identifiable {
* @see Role
* @return role attributed to the user
*/
- public Set getRoles();
+ Set getRoles();
+
+ /**
+ * Returns items which given user have access to.
+ *
+ * @return Item identifiers user has access to.
+ */
+ Set getPermissions();
}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserApiToken.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserApiToken.java
similarity index 98%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserApiToken.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserApiToken.java
index 401ea9ebb..5a4194218 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserApiToken.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserApiToken.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
import java.util.Date;
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserProvider.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserProvider.java
similarity index 94%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserProvider.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserProvider.java
index 9faba4d6e..38c8c8175 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserProvider.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserProvider.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.common.registry.Provider;
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserRegistry.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserRegistry.java
similarity index 58%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserRegistry.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserRegistry.java
index dbdb14b4f..d8076f3a9 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserRegistry.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserRegistry.java
@@ -10,12 +10,22 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Optional;
import java.util.Set;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.auth.AuthenticationProvider;
import org.openhab.core.common.registry.Registry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* An interface for a generic {@link Registry} of {@link User} entities. User registries can also be used as
@@ -27,6 +37,12 @@
@NonNullByDefault
public interface UserRegistry extends Registry, AuthenticationProvider {
+ int APITOKEN_ITERATIONS = 1024;
+ String APITOKEN_PREFIX = "oh";
+ int PASSWORD_ITERATIONS = 65536;
+ int KEY_LENGTH = 512;
+ String ALGORITHM = "PBKDF2WithHmacSHA512";
+
/**
* Adds a new {@link User} in this registry. The implementation receives the clear text credentials and is
* responsible for their secure storage (for instance by hashing the password), then return the newly created
@@ -37,7 +53,7 @@ public interface UserRegistry extends Registry, AuthenticationProv
* @param roles the roles attributed to the new user
* @return the new registered {@link User} instance
*/
- public User register(String username, String password, Set roles);
+ User register(String username, String password, Set roles);
/**
* Change the password for an {@link User} in this registry. The implementation receives the new password and is
@@ -46,7 +62,7 @@ public interface UserRegistry extends Registry, AuthenticationProv
* @param username the username of the existing user
* @param newPassword the new password
*/
- public void changePassword(User user, String newPassword);
+ void changePassword(User user, String newPassword);
/**
* Adds a new session to the user profile
@@ -54,7 +70,7 @@ public interface UserRegistry extends Registry, AuthenticationProv
* @param user the user
* @param session the session to add
*/
- public void addUserSession(User user, UserSession session);
+ void addUserSession(User user, UserSession session);
/**
* Removes the specified session from the user profile
@@ -62,14 +78,14 @@ public interface UserRegistry extends Registry, AuthenticationProv
* @param user the user
* @param session the session to remove
*/
- public void removeUserSession(User user, UserSession session);
+ void removeUserSession(User user, UserSession session);
/**
* Clears all sessions from the user profile
*
* @param user the user
*/
- public void clearSessions(User user);
+ void clearSessions(User user);
/**
* Adds a new API token to the user profile. The implementation is responsible for storing the token in a secure way
@@ -80,7 +96,7 @@ public interface UserRegistry extends Registry, AuthenticationProv
* @param scope the scope this API token will be valid for
* @return the string that can be used as a Bearer token to match the new API token
*/
- public String addUserApiToken(User user, String name, String scope);
+ String addUserApiToken(User user, String name, String scope);
/**
* Removes the specified API token from the user profile
@@ -88,5 +104,25 @@ public interface UserRegistry extends Registry, AuthenticationProv
* @param user the user
* @param apiToken the API token
*/
- public void removeUserApiToken(User user, UserApiToken apiToken);
+ void removeUserApiToken(User user, UserApiToken apiToken);
+
+ static Optional hash(String password, String salt, int iterations) {
+ char[] chars = password.toCharArray();
+ byte[] bytes = salt.getBytes();
+
+ PBEKeySpec spec = new PBEKeySpec(chars, bytes, iterations, KEY_LENGTH);
+
+ Arrays.fill(chars, Character.MIN_VALUE);
+
+ try {
+ SecretKeyFactory fac = SecretKeyFactory.getInstance(ALGORITHM);
+ byte[] securePassword = fac.generateSecret(spec).getEncoded();
+ return Optional.of(Base64.getEncoder().encodeToString(securePassword));
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ LoggerFactory.getLogger(UserRegistry.class).error("Exception encountered while hashing", e);
+ return Optional.empty();
+ } finally {
+ spec.clearPassword();
+ }
+ }
}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserSession.java b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserSession.java
similarity index 99%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserSession.java
rename to bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserSession.java
index 674908f1d..98f4a139d 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UserSession.java
+++ b/bundles/org.opensmarthouse.core.auth.local/src/main/java/org/openhab/core/auth/local/UserSession.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.local;
import java.util.Date;
diff --git a/bundles/org.opensmarthouse.core.auth.password/NOTICE b/bundles/org.opensmarthouse.core.auth.password/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.password/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.auth.password/pom.xml b/bundles/org.opensmarthouse.core.auth.password/pom.xml
new file mode 100755
index 000000000..6543a52d9
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth.password/pom.xml
@@ -0,0 +1,23 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.auth.password
+
+ OpenSmartHouse Core | Bundles | Password Credentials
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UsernamePasswordCredentials.java b/bundles/org.opensmarthouse.core.auth.password/src/main/java/org/openhab/core/auth/password/UsernamePasswordCredentials.java
similarity index 68%
rename from bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UsernamePasswordCredentials.java
rename to bundles/org.opensmarthouse.core.auth.password/src/main/java/org/openhab/core/auth/password/UsernamePasswordCredentials.java
index 8205d6c53..80644e6b9 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/UsernamePasswordCredentials.java
+++ b/bundles/org.opensmarthouse.core.auth.password/src/main/java/org/openhab/core/auth/password/UsernamePasswordCredentials.java
@@ -10,7 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.auth;
+package org.openhab.core.auth.password;
+
+import org.openhab.core.auth.Credentials;
/**
* Credentials which represent user name and password.
@@ -22,6 +24,7 @@ public class UsernamePasswordCredentials implements Credentials {
private final String username;
private final String password;
+ private final String scheme;
/**
* Creates a new instance
@@ -30,8 +33,20 @@ public class UsernamePasswordCredentials implements Credentials {
* @param password password of the user
*/
public UsernamePasswordCredentials(String username, String password) {
+ this(username, password, "basic");
+ }
+
+ /**
+ * Creates a new instance with given scheme.
+ *
+ * @param username name of the user
+ * @param password password of the user
+ * @param scheme login scheme
+ */
+ public UsernamePasswordCredentials(String username, String password, String scheme) {
this.username = username;
this.password = password;
+ this.scheme = scheme;
}
/**
@@ -52,8 +67,13 @@ public String getPassword() {
return password;
}
+ @Override
+ public String getScheme() {
+ return scheme;
+ }
+
@Override
public String toString() {
- return username + ":" + password.replaceAll(".", "*");
+ return scheme + "(" + username + ":[protected])";
}
}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Authentication.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Authentication.java
index 15804ee37..cf691c5e8 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Authentication.java
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Authentication.java
@@ -12,8 +12,6 @@
*/
package org.openhab.core.auth;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.Set;
/**
@@ -22,7 +20,7 @@
* Each authentication must at least point to some identity (username), roles, and may also be valid for a specific
* scope only.
*
- * @author Łukasz Dywicki - Initial contribution
+ * @author Łukasz Dywicki - Initial contribution, item level security
* @author Kai Kreuzer - Added JavaDoc and switched from array to Set
* @author Yannick Schaus - Add scope
*/
@@ -31,6 +29,7 @@ public class Authentication {
private String username;
private Set roles;
private String scope;
+ private Set permissions;
/**
* no-args constructor required by gson
@@ -39,6 +38,7 @@ protected Authentication() {
this.username = null;
this.roles = null;
this.scope = null;
+ this.permissions = null;
}
/**
@@ -48,8 +48,7 @@ protected Authentication() {
* @param roles a variable list of roles that the user possesses.
*/
public Authentication(String username, String... roles) {
- this.username = username;
- this.roles = new HashSet<>(Arrays.asList(roles));
+ this(username, roles, "");
}
/**
@@ -60,8 +59,34 @@ public Authentication(String username, String... roles) {
* @param scope a scope this authentication is valid for
*/
public Authentication(String username, String[] roles, String scope) {
- this(username, roles);
+ this(username, roles, scope, new String[] {"*:*:*"});
+ }
+
+ /**
+ * Creates a new instance with a specific scope
+ *
+ * @param username name of the user associated to this authentication instance
+ * @param roles a variable list of roles that the user possesses.
+ * @param scope a scope this authentication is valid for
+ * @param permissions permissions associated with authentication
+ */
+ public Authentication(String username, String[] roles, String scope, String[] permissions) {
+ this(username, Set.of(roles), scope, Set.of(permissions));
+ }
+
+ /**
+ * Creates a new instance with a specific scope
+ *
+ * @param username name of the user associated to this authentication instance
+ * @param roles a variable list of roles that the user possesses.
+ * @param scope a scope this authentication is valid for
+ * @param permissions permissions associated with authentication
+ */
+ public Authentication(String username, Set roles, String scope, Set permissions) {
+ this.username = username;
+ this.roles = roles;
this.scope = scope;
+ this.permissions = permissions;
}
/**
@@ -90,4 +115,13 @@ public Set getRoles() {
public String getScope() {
return scope;
}
+
+ /**
+ * Retrieves the permissions this authentication has.
+ *
+ * @return an set of permissions (might be empty)
+ */
+ public Set getPermissions() {
+ return permissions;
+ }
}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationContextHolder.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationContextHolder.java
new file mode 100644
index 000000000..cc3ce07ef
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationContextHolder.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The access layer to an authentication context during execution of an action.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+public interface AuthenticationContextHolder {
+
+ @Nullable
+ Authentication getAuthentication();
+
+ Optional fetchAuthentication();
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationManager.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationManager.java
index 7c90034e5..c64677b0e 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationManager.java
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationManager.java
@@ -28,5 +28,5 @@ public interface AuthenticationManager {
* AuthenticationException.
* @throws AuthenticationException when none of available authentication methods succeeded.
*/
- Authentication authenticate(Credentials credentials) throws AuthenticationException;
+ AuthenticationResult authenticate(Credentials credentials) throws AuthenticationException;
}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationProvider.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationProvider.java
index dc951710d..8d95bf2fd 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationProvider.java
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationProvider.java
@@ -30,7 +30,7 @@ public interface AuthenticationProvider {
* @return null if credentials were not valid for this provider
* @throws AuthenticationException if authentication failed due to credentials mismatch.
*/
- Authentication authenticate(Credentials credentials) throws AuthenticationException;
+ AuthenticationResult authenticate(Credentials credentials) throws AuthenticationException;
/**
* Additional method to verify if given authentication provider can handle given type of credentials.
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationResult.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationResult.java
new file mode 100755
index 000000000..f8980924d
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthenticationResult.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+import java.security.Principal;
+
+/**
+ * Type describing successful authentication attempt.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+public class AuthenticationResult {
+
+ private final Principal principal;
+ private final String scheme;
+ private final Authentication authentication;
+
+ public AuthenticationResult(Principal principal, String scheme, Authentication authentication) {
+ this.principal = principal;
+ this.scheme = scheme;
+ this.authentication = authentication;
+ }
+
+ public Principal getPrincipal() {
+ return principal;
+ }
+
+ public String getScheme() {
+ return scheme;
+ }
+
+ public Authentication getAuthentication() {
+ return authentication;
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationException.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationException.java
new file mode 100644
index 000000000..652166ec3
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationException.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+/**
+ * The access layer to an authentication context during execution of an action.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+public class AuthorizationException extends Exception {
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationManager.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationManager.java
new file mode 100755
index 000000000..3e3b3ad14
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationManager.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+/**
+ * Authorization manager is main entry point for security checks.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+public interface AuthorizationManager {
+
+ boolean hasPermission(Permission permission, T object, Authentication authentication);
+
+ boolean hasPermission(Permission permission, String id, Class> type, Authentication authentication);
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationManagerFilters.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationManagerFilters.java
new file mode 100644
index 000000000..101a4c6d7
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/AuthorizationManagerFilters.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+/**
+ * Helpers for filtering out collections and returning trimmed data.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+public class AuthorizationManagerFilters {
+
+ public static List filter(Authentication authentication, BiFunction check, List values) {
+ return values.stream().filter(value -> check.apply(authentication, value))
+ .collect(Collectors.toList());
+ }
+
+ public static Set filter(Authentication authentication, BiFunction check, Set values) {
+ return values.stream().filter(value -> check.apply(authentication, value))
+ .collect(Collectors.toSet());
+ }
+
+ public static Map filter(Authentication authentication, BiFunction, Boolean> check, Map values) {
+ return values.entrySet().stream().filter(entry -> check.apply(authentication, entry))
+ .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Credentials.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Credentials.java
index d4ffd323a..ee97f79af 100755
--- a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Credentials.java
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Credentials.java
@@ -19,4 +19,6 @@
*/
public interface Credentials {
+ String getScheme();
+
}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/MutableAuthenticationContextHolder.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/MutableAuthenticationContextHolder.java
new file mode 100644
index 000000000..984ec0d36
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/MutableAuthenticationContextHolder.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+/**
+ * Writeable holder for authentication context which should be used early in processing chain.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+public interface MutableAuthenticationContextHolder {
+
+ void setAuthentication(Authentication authentication);
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/NamedPermission.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/NamedPermission.java
new file mode 100644
index 000000000..2818820d6
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/NamedPermission.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+import java.util.Objects;
+
+/**
+ * Simplest possible implementation of permission.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+public class NamedPermission implements Permission {
+
+ private final String code;
+
+ public NamedPermission(String code) {
+ this.code = code;
+ }
+
+ @Override
+ public String getCode() {
+ return code;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Permission)) {
+ return false;
+ }
+ Permission that = (Permission) o;
+ return Objects.equals(getCode(), that.getCode());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getCode());
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Permission.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Permission.java
new file mode 100644
index 000000000..2514845d5
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Permission.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+public interface Permission {
+
+ String getCode();
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/PermissionEvaluator.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/PermissionEvaluator.java
new file mode 100644
index 000000000..09e6db0fa
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/PermissionEvaluator.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+public interface PermissionEvaluator {
+
+ boolean supports(Class> type, Permission permission);
+
+ /**
+ * Decide if given value is accessible in given authentication context or not.
+ *
+ * @param permission Permission to be evaluated.
+ * @param authentication Authentication to verify.
+ * @param value Entity or resource to check.
+ * @return True if user can access given object.
+ */
+ boolean hasPermission(Permission permission, Authentication authentication, T value);
+
+ // same as above but uses type + id instead of entire object
+ boolean hasPermission(Permission permission, Authentication authentication, Class type, String id);
+
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Permissions.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Permissions.java
new file mode 100644
index 000000000..919471377
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/Permissions.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth;
+
+/**
+ * Common permission definitions.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+public interface Permissions {
+
+ Permission ALL = new NamedPermission("*");
+ Permission READ = new NamedPermission("read");
+ Permission STATE = new NamedPermission("state");
+ Permission COMMAND = new NamedPermission("command");
+ Permission MANAGE = new NamedPermission("manage");
+
+ Permission PERSISTENCE = new NamedPermission("persistence");
+}
diff --git a/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/holder/ThreadLocalContextHolder.java b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/holder/ThreadLocalContextHolder.java
new file mode 100644
index 000000000..81d8fa513
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.auth/src/main/java/org/openhab/core/auth/holder/ThreadLocalContextHolder.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.auth.holder;
+
+import java.util.Optional;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationContextHolder;
+import org.openhab.core.auth.MutableAuthenticationContextHolder;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Implementation of authentication context holder based on {@link ThreadLocal}.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+@Component(service = {AuthenticationContextHolder.class, MutableAuthenticationContextHolder.class})
+public class ThreadLocalContextHolder implements AuthenticationContextHolder, MutableAuthenticationContextHolder {
+
+ private final ThreadLocal authentication = new ThreadLocal<>();
+
+ @Override
+ public Authentication getAuthentication() {
+ return authentication.get();
+ }
+
+ @Override
+ public Optional fetchAuthentication() {
+ return Optional.ofNullable(authentication.get());
+ }
+
+ // this one should be visible only to few
+ public void setAuthentication(Authentication authentication) {
+ this.authentication.set(authentication);
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.config/src/main/java/org/openhab/core/config/core/Configuration.java b/bundles/org.opensmarthouse.core.config/src/main/java/org/openhab/core/config/core/Configuration.java
index 876b4b432..94ba22aab 100755
--- a/bundles/org.opensmarthouse.core.config/src/main/java/org/openhab/core/config/core/Configuration.java
+++ b/bundles/org.opensmarthouse.core.config/src/main/java/org/openhab/core/config/core/Configuration.java
@@ -19,8 +19,8 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -72,7 +72,7 @@ public Configuration(@Nullable Map properties) {
* @param alreadyNormalized flag if the properties are already normalized
*/
private Configuration(final Map properties, final boolean alreadyNormalized) {
- this.properties = synchronizedMap(alreadyNormalized ? new HashMap<>(properties) : normalizeTypes(properties));
+ this.properties = synchronizedMap(alreadyNormalized ? new LinkedHashMap<>(properties) : normalizeTypes(properties));
}
public T as(Class configurationClass) {
@@ -118,7 +118,7 @@ public Collection values() {
public Map getProperties() {
synchronized (properties) {
- return Collections.unmodifiableMap(new HashMap<>(properties));
+ return Collections.unmodifiableMap(new LinkedHashMap<>(properties));
}
}
diff --git a/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/I18nProviderImpl.java b/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/I18nProviderImpl.java
index 6b4c92e35..56d1dddf2 100755
--- a/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/I18nProviderImpl.java
+++ b/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/I18nProviderImpl.java
@@ -18,11 +18,13 @@
import java.time.DateTimeException;
import java.time.ZoneId;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
+import java.util.concurrent.CopyOnWriteArrayList;
import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.quantity.Angle;
@@ -40,6 +42,7 @@
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
+import org.openhab.core.i18n.ResourceTranslationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.i18n.UnitProvider;
@@ -57,6 +60,9 @@
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -78,6 +84,7 @@
* @author Markus Rathgeb - Initial contribution
* @author Stefan Triller - Initial contribution
* @author Erdoan Hadzhiyusein - Added time zone
+ * @author Łukasz Dywicki - Added support for dynamic translations
*/
@Component(immediate = true, configurationPid = I18nProviderImpl.CONFIGURATION_PID, property = {
Constants.SERVICE_PID + "=org.openhab.i18n", //
@@ -101,6 +108,7 @@ public class I18nProviderImpl
// TranslationProvider
private final ResourceBundleTracker resourceBundleTracker;
+ private final List translationProviders = new CopyOnWriteArrayList<>();
// LocationProvider
static final String LOCATION = "location";
@@ -146,6 +154,15 @@ protected synchronized void modified(Map config) {
setMeasurementSystem(measurementSystem);
}
+ @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
+ void addResourceTranslationProvider(ResourceTranslationProvider translationProvider) {
+ this.translationProviders.add(translationProvider);
+ }
+
+ void removeResourceTranslationProvider(ResourceTranslationProvider translationProvider) {
+ this.translationProviders.remove(translationProvider);
+ }
+
private void setMeasurementSystem(@Nullable String measurementSystem) {
SystemOfUnits oldMeasurementSystem = this.measurementSystem;
@@ -299,6 +316,13 @@ public Locale getLocale() {
@Override
public @Nullable String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
@Nullable Locale locale) {
+ for (ResourceTranslationProvider provider : translationProviders) {
+ String text = provider.getText(key, locale);
+ if (text != null) {
+ return text;
+ }
+ }
+
LanguageResourceBundleManager languageResource = this.resourceBundleTracker.getLanguageResource(bundle);
if (languageResource != null) {
diff --git a/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/LanguageResourceBundleManager.java b/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/LanguageResourceBundleManager.java
index 9426091c3..a2dd186d2 100755
--- a/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/LanguageResourceBundleManager.java
+++ b/bundles/org.opensmarthouse.core.i18n.core/src/main/java/org/openhab/core/internal/i18n/LanguageResourceBundleManager.java
@@ -22,6 +22,7 @@
import java.util.ResourceBundle.Control;
import org.openhab.core.common.osgi.ResourceBundleClassLoader;
+import org.openhab.core.i18n.ResourceTranslationProvider;
import org.openhab.core.i18n.LocaleProvider;
import org.osgi.framework.Bundle;
@@ -37,7 +38,7 @@
* @author Michael Grammling - Initial contribution
* @author Markus Rathgeb - Add locale provider support
*/
-public class LanguageResourceBundleManager {
+public class LanguageResourceBundleManager implements ResourceTranslationProvider {
/** The directory within the bundle where the resource files are searched. */
protected static final String RESOURCE_DIRECTORY = "/OH-INF/i18n";
@@ -135,6 +136,7 @@ private List determineResourceNames() {
*
* @return the translated text, or null if the key could not be translated
*/
+ @Override
public String getText(String resource, String key, Locale locale) {
if ((key != null) && (!key.isEmpty())) {
Locale effectiveLocale = locale != null ? locale : localeProvider.getLocale();
@@ -167,6 +169,7 @@ public String getText(String resource, String key, Locale locale) {
*
* @return the translated text, or null if the key could not be translated
*/
+ @Override
public String getText(String key, Locale locale) {
return getText(null, key, locale);
}
diff --git a/bundles/org.opensmarthouse.core.i18n/src/main/java/org/openhab/core/i18n/ResourceTranslationProvider.java b/bundles/org.opensmarthouse.core.i18n/src/main/java/org/openhab/core/i18n/ResourceTranslationProvider.java
new file mode 100644
index 000000000..bcc28c57c
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.i18n/src/main/java/org/openhab/core/i18n/ResourceTranslationProvider.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.i18n;
+
+import java.util.Locale;
+
+/**
+ * Interface for resource/translation provider which can be registered dynamically as a service.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+public interface ResourceTranslationProvider {
+
+ /**
+ * Returns a translation for the specified key in the specified locale (language) by only
+ * considering the specified resource section. The resource is equal to a base name and
+ * therefore it is mapped to one translation package (all files which belong to the base
+ * name).
+ *
+ * If no translation could be found, {@code null} is returned. If the location is not specified, the default
+ * location is used.
+ *
+ * @param resource the resource to be used for look-up (could be null or empty)
+ * @param key the key to be translated (could be null or empty)
+ * @param locale the locale (language) to be used (could be null)
+ *
+ * @return the translated text, or null if the key could not be translated
+ */
+ String getText(String resource, String key, Locale locale);
+
+ /**
+ * Returns a translation for the specified key in the specified locale (language)
+ * by considering all resources in the according bundle.
+ *
+ * If no translation could be found, {@code null} is returned. If the location is not specified, the default
+ * location is used.
+ *
+ * @param key the key to be translated (could be null or empty)
+ * @param locale the locale (language) to be used (could be null)
+ *
+ * @return the translated text, or null if the key could not be translated
+ */
+ String getText(String key, Locale locale);
+
+}
diff --git a/bundles/org.opensmarthouse.core.io.auth/NOTICE b/bundles/org.opensmarthouse.core.io.auth/NOTICE
new file mode 100755
index 000000000..bed08ed3f
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.auth/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.io.auth/pom.xml b/bundles/org.opensmarthouse.core.io.auth/pom.xml
new file mode 100755
index 000000000..432bcea7e
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.auth/pom.xml
@@ -0,0 +1,23 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.io.auth
+
+ OpenSmartHouse Core | Bundles | IO Authentication
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/CredentialsExtractor.java b/bundles/org.opensmarthouse.core.io.auth/src/main/java/org/openhab/core/io/auth/CredentialsExtractor.java
similarity index 95%
rename from bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/CredentialsExtractor.java
rename to bundles/org.opensmarthouse.core.io.auth/src/main/java/org/openhab/core/io/auth/CredentialsExtractor.java
index 613791ed9..3fa5f4c03 100755
--- a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/CredentialsExtractor.java
+++ b/bundles/org.opensmarthouse.core.io.auth/src/main/java/org/openhab/core/io/auth/CredentialsExtractor.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.http.auth;
+package org.openhab.core.io.auth;
import java.util.Optional;
diff --git a/bundles/org.opensmarthouse.core.io.console.user/pom.xml b/bundles/org.opensmarthouse.core.io.console.user/pom.xml
index 0f2aa46e7..886894d85 100644
--- a/bundles/org.opensmarthouse.core.io.console.user/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.console.user/pom.xml
@@ -14,6 +14,10 @@
OpenSmartHouse Core | Bundles | Console | User
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.io.console
diff --git a/bundles/org.opensmarthouse.core.io.console.user/src/main/java/org/openhab/core/io/console/internal/extension/UserConsoleCommandExtension.java b/bundles/org.opensmarthouse.core.io.console.user/src/main/java/org/openhab/core/io/console/internal/extension/UserConsoleCommandExtension.java
index 1ffab9c47..b2744497e 100644
--- a/bundles/org.opensmarthouse.core.io.console.user/src/main/java/org/openhab/core/io/console/internal/extension/UserConsoleCommandExtension.java
+++ b/bundles/org.opensmarthouse.core.io.console.user/src/main/java/org/openhab/core/io/console/internal/extension/UserConsoleCommandExtension.java
@@ -17,10 +17,10 @@
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserApiToken;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserApiToken;
+import org.openhab.core.auth.local.UserRegistry;
import org.openhab.core.io.console.Console;
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.apitoken/NOTICE b/bundles/org.opensmarthouse.core.io.http.auth.apitoken/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.apitoken/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.apitoken/pom.xml b/bundles/org.opensmarthouse.core.io.http.auth.apitoken/pom.xml
new file mode 100755
index 000000000..e6971de37
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.apitoken/pom.xml
@@ -0,0 +1,31 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.io.http.auth.apitoken
+
+ OpenSmartHouse Core | Bundles | HTTP/Apitoken Authentication
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.auth
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.facade
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.apitoken
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.apitoken/src/main/java/org/openhab/core/io/http/auth/apitoken/internal/ApitokenCredentialsExtractor.java b/bundles/org.opensmarthouse.core.io.http.auth.apitoken/src/main/java/org/openhab/core/io/http/auth/apitoken/internal/ApitokenCredentialsExtractor.java
new file mode 100644
index 000000000..7e3b7579f
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.apitoken/src/main/java/org/openhab/core/io/http/auth/apitoken/internal/ApitokenCredentialsExtractor.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.http.auth.apitoken.internal;
+
+import java.util.Optional;
+import org.openhab.core.auth.Credentials;
+import org.openhab.core.io.auth.CredentialsExtractor;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
+import org.osgi.service.component.annotations.Component;
+import org.openhab.core.auth.apitoken.UserApiTokenCredentials;
+
+/**
+ *
+ * @author Łukasz Dywicki - Extracted from {@link org.openhab.core.io.rest.auth.internal.AuthFilter}
+ */
+@Component(property = { "context=org.openhab.core.io.http.facade.HttpRequestDelegate" })
+public class ApitokenCredentialsExtractor implements CredentialsExtractor {
+
+ private static final String ALT_AUTH_HEADER = "X-OPENHAB-TOKEN";
+ private static final String API_TOKEN_PREFIX = "oh.";
+
+ @Override
+ public Optional retrieveCredentials(HttpRequestDelegate requestContext) {
+ return requestContext.getHeader(ALT_AUTH_HEADER)
+ .filter(token -> token.startsWith(API_TOKEN_PREFIX))
+ .map(token -> new UserApiTokenCredentials(token));
+ }
+}
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.basic/pom.xml b/bundles/org.opensmarthouse.core.io.http.auth.basic/pom.xml
index d92f0f284..0ea5f3a1c 100755
--- a/bundles/org.opensmarthouse.core.io.http.auth.basic/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.http.auth.basic/pom.xml
@@ -14,10 +14,21 @@
OpenSmartHouse Core | Bundles | HTTP Authentication Basic
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.password
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.auth
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.facade
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.io.http.auth
- ${project.version}
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.basic/src/main/java/org/openhab/core/io/http/auth/basic/internal/BasicCredentialsExtractor.java b/bundles/org.opensmarthouse.core.io.http.auth.basic/src/main/java/org/openhab/core/io/http/auth/basic/internal/BasicCredentialsExtractor.java
index 2fc4a0a71..c72874052 100755
--- a/bundles/org.opensmarthouse.core.io.http.auth.basic/src/main/java/org/openhab/core/io/http/auth/basic/internal/BasicCredentialsExtractor.java
+++ b/bundles/org.opensmarthouse.core.io.http.auth.basic/src/main/java/org/openhab/core/io/http/auth/basic/internal/BasicCredentialsExtractor.java
@@ -18,26 +18,27 @@
import javax.servlet.http.HttpServletRequest;
import org.openhab.core.auth.Credentials;
-import org.openhab.core.auth.UsernamePasswordCredentials;
-import org.openhab.core.io.http.auth.CredentialsExtractor;
+import org.openhab.core.auth.password.UsernamePasswordCredentials;
+import org.openhab.core.io.auth.CredentialsExtractor;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
import org.osgi.service.component.annotations.Component;
/**
* Extract user name and password from incoming request.
*
- * @author Łukasz Dywicki - Initial contribution.
+ * @author Łukasz Dywicki - Initial contribution, migration to Http request facade.
*/
-@Component(property = { "context=javax.servlet.http.HttpServletRequest" })
-public class BasicCredentialsExtractor implements CredentialsExtractor {
+@Component(property = { "context=org.openhab.core.io.http.facade.HttpRequestDelegate" })
+public class BasicCredentialsExtractor implements CredentialsExtractor {
@Override
- public Optional retrieveCredentials(HttpServletRequest request) {
- String authenticationHeader = request.getHeader("Authorization");
-
- if (authenticationHeader == null) {
- return Optional.empty();
- }
+ public Optional retrieveCredentials(HttpRequestDelegate request) {
+ return request.getAuthorizationHeader()
+ .filter(header -> header.contains(" "))
+ .flatMap(this::process);
+ }
+ private Optional process(String authenticationHeader) {
String[] tokens = authenticationHeader.split(" ");
if (tokens.length == 2) {
String authType = tokens[0];
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.cookie/pom.xml b/bundles/org.opensmarthouse.core.io.http.auth.cookie/pom.xml
new file mode 100755
index 000000000..c1fa0498d
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.cookie/pom.xml
@@ -0,0 +1,35 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.io.http.auth.cookie
+
+ OpenSmartHouse Core | Bundles | HTTP Cookie Authentication
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.cookie
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.auth
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.facade
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.auth
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.cookie/src/main/java/org/openhab/core/io/http/auth/cookie/internal/CookieCredentialsExtractor.java b/bundles/org.opensmarthouse.core.io.http.auth.cookie/src/main/java/org/openhab/core/io/http/auth/cookie/internal/CookieCredentialsExtractor.java
new file mode 100755
index 000000000..cfb64689e
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.cookie/src/main/java/org/openhab/core/io/http/auth/cookie/internal/CookieCredentialsExtractor.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.http.auth.cookie.internal;
+
+import java.util.Optional;
+
+import org.openhab.core.auth.Credentials;
+import org.openhab.core.auth.cookie.CookieCredentials;
+import org.openhab.core.io.auth.CredentialsExtractor;
+import org.openhab.core.io.http.facade.Cookie;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Extract session information from cookie inincoming request.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+@Component(property = { "context=org.openhab.core.io.http.facade.HttpRequestDelegate" })
+public class CookieCredentialsExtractor implements CredentialsExtractor {
+
+ public static final String SESSIONID_COOKIE_NAME = "X-OPENHAB-SESSIONID";
+
+ @Override
+ public Optional retrieveCredentials(HttpRequestDelegate request) {
+ return request.getCookie(SESSIONID_COOKIE_NAME).map(this::process);
+ }
+
+ private CookieCredentials process(Cookie cookie) {
+ return new CookieCredentials(cookie.getName(), cookie.getValue());
+ }
+}
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.jwt/NOTICE b/bundles/org.opensmarthouse.core.io.http.auth.jwt/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.jwt/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.jwt/pom.xml b/bundles/org.opensmarthouse.core.io.http.auth.jwt/pom.xml
new file mode 100755
index 000000000..09473249d
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.jwt/pom.xml
@@ -0,0 +1,31 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.io.http.auth.jwt
+
+ OpenSmartHouse Core | Bundles | HTTP/JWT Authentication
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.auth
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.facade
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.jwt
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.io.http.auth.jwt/src/main/java/org/openhab/core/io/http/auth/jwt/internal/JwtCredentialsExtractor.java b/bundles/org.opensmarthouse.core.io.http.auth.jwt/src/main/java/org/openhab/core/io/http/auth/jwt/internal/JwtCredentialsExtractor.java
new file mode 100644
index 000000000..2dc6beaf0
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth.jwt/src/main/java/org/openhab/core/io/http/auth/jwt/internal/JwtCredentialsExtractor.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.http.auth.jwt.internal;
+
+import java.util.Optional;
+import org.openhab.core.auth.Credentials;
+import org.openhab.core.auth.jwt.JWTCredentials;
+import org.openhab.core.io.auth.CredentialsExtractor;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * An extractor of request credentials which rely on token sent in authorization header.
+ *
+ * @author Łukasz Dywicki - Extracted from {@link org.openhab.core.io.rest.auth.internal.AuthFilter}
+ */
+@Component(property = { "context=org.openhab.core.io.http.facade.HttpRequestDelegate" })
+public class JwtCredentialsExtractor implements CredentialsExtractor {
+
+ @Override
+ public Optional retrieveCredentials(HttpRequestDelegate request) {
+ return request.getAuthorizationHeader()
+ .filter(header -> header.contains(" "))
+ .flatMap(this::process);
+ }
+
+ private Optional process(String authenticationHeader) {
+ String[] tokens = authenticationHeader.split(" ");
+ if (tokens.length == 2) {
+ if ("bearer".equalsIgnoreCase(tokens[0])) {
+ return Optional.of(new JWTCredentials(tokens[1]));
+ }
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/pom.xml b/bundles/org.opensmarthouse.core.io.http.auth/pom.xml
index a5ccff547..2ac561417 100755
--- a/bundles/org.opensmarthouse.core.io.http.auth/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.http.auth/pom.xml
@@ -18,6 +18,18 @@
org.opensmarthouse.core.bundlesorg.opensmarthouse.core.auth
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.password
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.auth
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.facade
+ org.opensmarthouse.core.bundlesorg.opensmarthouse.core.io.http
@@ -26,6 +38,10 @@
org.opensmarthouse.core.bundlesorg.opensmarthouse.core.i18n
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local
+ org.eclipse.jetty
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AbstractAuthPageServlet.java b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AbstractAuthPageServlet.java
index 217ec6962..5d27bb49c 100644
--- a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AbstractAuthPageServlet.java
+++ b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AbstractAuthPageServlet.java
@@ -26,16 +26,17 @@
import java.util.UUID;
import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthenticationException;
-import org.openhab.core.auth.AuthenticationProvider;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserRegistry;
-import org.openhab.core.auth.UsernamePasswordCredentials;
+import org.openhab.core.auth.AuthenticationManager;
+import org.openhab.core.auth.AuthenticationResult;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserRegistry;
+import org.openhab.core.auth.password.UsernamePasswordCredentials;
import org.openhab.core.i18n.LocaleProvider;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Reference;
@@ -60,7 +61,7 @@ public abstract class AbstractAuthPageServlet extends HttpServlet {
protected HttpService httpService;
protected UserRegistry userRegistry;
- protected AuthenticationProvider authProvider;
+ protected AuthenticationManager authManager;
protected LocaleProvider localeProvider;
protected @Nullable Instant lastAuthenticationFailure;
protected int authenticationFailureCount = 0;
@@ -70,11 +71,11 @@ public abstract class AbstractAuthPageServlet extends HttpServlet {
protected String pageTemplate;
public AbstractAuthPageServlet(BundleContext bundleContext, @Reference HttpService httpService,
- @Reference UserRegistry userRegistry, @Reference AuthenticationProvider authProvider,
+ @Reference UserRegistry userRegistry, @Reference AuthenticationManager authManager,
@Reference LocaleProvider localeProvider) {
this.httpService = httpService;
this.userRegistry = userRegistry;
- this.authProvider = authProvider;
+ this.authManager = authManager;
this.localeProvider = localeProvider;
pageTemplate = "";
@@ -130,12 +131,12 @@ protected User login(String username, String password) throws AuthenticationExce
}
// Authenticate the user with the supplied credentials
- UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
- Authentication auth = authProvider.authenticate(credentials);
- logger.debug("Login successful: {}", auth.getUsername());
+ UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password, HttpServletRequest.FORM_AUTH);
+ AuthenticationResult auth = authManager.authenticate(credentials);
+ logger.debug("Login successful: {}", auth.getPrincipal());
lastAuthenticationFailure = null;
authenticationFailureCount = 0;
- User user = userRegistry.get(auth.getUsername());
+ User user = userRegistry.get(auth.getAuthentication().getUsername());
if (user == null) {
throw new AuthenticationException("User not found");
}
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthenticationHandler.java b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthenticationHandler.java
index 45577f354..654712ca3 100755
--- a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthenticationHandler.java
+++ b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthenticationHandler.java
@@ -25,11 +25,13 @@
import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthenticationException;
import org.openhab.core.auth.AuthenticationManager;
+import org.openhab.core.auth.AuthenticationResult;
import org.openhab.core.auth.Credentials;
import org.openhab.core.io.http.Handler;
import org.openhab.core.io.http.HandlerContext;
import org.openhab.core.io.http.HandlerPriorities;
-import org.openhab.core.io.http.auth.CredentialsExtractor;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
+import org.openhab.core.io.auth.CredentialsExtractor;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
@@ -54,7 +56,7 @@ public class AuthenticationHandler implements Handler {
private final Logger logger = LoggerFactory.getLogger(AuthenticationHandler.class);
- private final List> extractors = new CopyOnWriteArrayList<>();
+ private final List> extractors = new CopyOnWriteArrayList<>();
private AuthenticationManager authenticationManager;
@@ -77,14 +79,14 @@ public void handle(final HttpServletRequest request, final HttpServletResponse r
}
int found = 0, failed = 0;
- for (CredentialsExtractor extractor : extractors) {
- Optional extracted = extractor.retrieveCredentials(request);
+ for (CredentialsExtractor extractor : extractors) {
+ Optional extracted = extractor.retrieveCredentials(new ServletRequestDelegate(request));
if (extracted.isPresent()) {
found++;
Credentials credentials = extracted.get();
try {
- Authentication authentication = authenticationManager.authenticate(credentials);
- request.setAttribute(Authentication.class.getName(), authentication);
+ AuthenticationResult authentication = authenticationManager.authenticate(credentials);
+ request.setAttribute(Authentication.class.getName(), authentication.getAuthentication());
context.execute(request, response);
return;
} catch (AuthenticationException e) {
@@ -167,12 +169,12 @@ public void unsetAuthenticationManager(AuthenticationManager authenticationManag
this.authenticationManager = null;
}
- @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, target = "(context=javax.servlet.http.HttpServletRequest)")
- public void addCredentialsExtractor(CredentialsExtractor extractor) {
+ @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, target = "(context=org.openhab.core.io.http.facade.HttpRequestDelegate)")
+ public void addCredentialsExtractor(CredentialsExtractor extractor) {
this.extractors.add(extractor);
}
- public void removeCredentialsExtractor(CredentialsExtractor extractor) {
+ public void removeCredentialsExtractor(CredentialsExtractor extractor) {
this.extractors.remove(extractor);
}
}
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthorizePageServlet.java b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthorizePageServlet.java
index d53eb5605..b1c26a228 100755
--- a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthorizePageServlet.java
+++ b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/AuthorizePageServlet.java
@@ -26,12 +26,12 @@
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.core.auth.AuthenticationException;
-import org.openhab.core.auth.AuthenticationProvider;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.PendingToken;
+import org.openhab.core.auth.AuthenticationManager;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.PendingToken;
import org.openhab.core.auth.Role;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserRegistry;
import org.openhab.core.i18n.LocaleProvider;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
@@ -64,9 +64,9 @@ public class AuthorizePageServlet extends AbstractAuthPageServlet {
@Activate
public AuthorizePageServlet(BundleContext bundleContext, @Reference HttpService httpService,
- @Reference UserRegistry userRegistry, @Reference AuthenticationProvider authProvider,
+ @Reference UserRegistry userRegistry, @Reference AuthenticationManager authManager,
@Reference LocaleProvider localeProvider) {
- super(bundleContext, httpService, userRegistry, authProvider, localeProvider);
+ super(bundleContext, httpService, userRegistry, authManager, localeProvider);
try {
httpService.registerServlet("/auth", this, null, null);
} catch (NamespaceException | ServletException e) {
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/ChangePasswordPageServlet.java b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/ChangePasswordPageServlet.java
index fa0dcfdd7..abc8efa09 100644
--- a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/ChangePasswordPageServlet.java
+++ b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/ChangePasswordPageServlet.java
@@ -21,10 +21,11 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.AuthenticationManager;
import org.openhab.core.auth.AuthenticationProvider;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserRegistry;
import org.openhab.core.i18n.LocaleProvider;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
@@ -52,9 +53,9 @@ public class ChangePasswordPageServlet extends AbstractAuthPageServlet {
@Activate
public ChangePasswordPageServlet(BundleContext bundleContext, @Reference HttpService httpService,
- @Reference UserRegistry userRegistry, @Reference AuthenticationProvider authProvider,
+ @Reference UserRegistry userRegistry, @Reference AuthenticationManager authManager,
@Reference LocaleProvider localeProvider) {
- super(bundleContext, httpService, userRegistry, authProvider, localeProvider);
+ super(bundleContext, httpService, userRegistry, authManager, localeProvider);
try {
httpService.registerServlet("/changePassword", this, null, null);
} catch (NamespaceException | ServletException e) {
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/CreateAPITokenPageServlet.java b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/CreateAPITokenPageServlet.java
index 2575386cb..c87d4c3f5 100644
--- a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/CreateAPITokenPageServlet.java
+++ b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/CreateAPITokenPageServlet.java
@@ -21,10 +21,11 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.AuthenticationManager;
import org.openhab.core.auth.AuthenticationProvider;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserRegistry;
import org.openhab.core.i18n.LocaleProvider;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
@@ -52,9 +53,9 @@ public class CreateAPITokenPageServlet extends AbstractAuthPageServlet {
@Activate
public CreateAPITokenPageServlet(BundleContext bundleContext, @Reference HttpService httpService,
- @Reference UserRegistry userRegistry, @Reference AuthenticationProvider authProvider,
+ @Reference UserRegistry userRegistry, @Reference AuthenticationManager authManager,
@Reference LocaleProvider localeProvider) {
- super(bundleContext, httpService, userRegistry, authProvider, localeProvider);
+ super(bundleContext, httpService, userRegistry, authManager, localeProvider);
try {
httpService.registerServlet("/createApiToken", this, null, null);
} catch (NamespaceException | ServletException e) {
diff --git a/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/ServletRequestDelegate.java b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/ServletRequestDelegate.java
new file mode 100644
index 000000000..a20b97794
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.auth/src/main/java/org/openhab/core/io/http/auth/internal/ServletRequestDelegate.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.http.auth.internal;
+
+import java.util.Arrays;
+import java.util.Optional;
+import javax.servlet.http.HttpServletRequest;
+import org.openhab.core.io.http.facade.Cookie;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
+
+/**
+ * Servlet request wrapper.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+public class ServletRequestDelegate implements HttpRequestDelegate {
+
+ private final HttpServletRequest request;
+
+ public ServletRequestDelegate(HttpServletRequest request) {
+ this.request = request;
+ }
+
+ @Override
+ public Optional getHeader(String headerName) {
+ return Optional.ofNullable(request.getHeader(headerName));
+ }
+
+ @Override
+ public Optional getCookie(String cookieName) {
+ return Arrays.stream(request.getCookies()).filter(cookie -> cookieName.equals(cookie.getName()))
+ .map(cookie -> new Cookie(cookie.getName(), cookie.getValue()))
+ .findFirst();
+ }
+}
diff --git a/bundles/org.opensmarthouse.core.io.http.facade/NOTICE b/bundles/org.opensmarthouse.core.io.http.facade/NOTICE
new file mode 100755
index 000000000..bed08ed3f
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.facade/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.io.http.facade/pom.xml b/bundles/org.opensmarthouse.core.io.http.facade/pom.xml
new file mode 100755
index 000000000..04305d13e
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.facade/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.io.http.facade
+
+ OpenSmartHouse Core | Bundles | IO HTTP Facade
+ Server side HTTP api facade
+
+
diff --git a/bundles/org.opensmarthouse.core.io.http.facade/src/main/java/org/openhab/core/io/http/facade/Cookie.java b/bundles/org.opensmarthouse.core.io.http.facade/src/main/java/org/openhab/core/io/http/facade/Cookie.java
new file mode 100644
index 000000000..1e684dc2e
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.facade/src/main/java/org/openhab/core/io/http/facade/Cookie.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.http.facade;
+
+/**
+ * An unified (facade) version of http cookie.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+public class Cookie {
+
+ private final String name;
+ private final String value;
+
+ public Cookie(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.io.http.facade/src/main/java/org/openhab/core/io/http/facade/HttpRequestDelegate.java b/bundles/org.opensmarthouse.core.io.http.facade/src/main/java/org/openhab/core/io/http/facade/HttpRequestDelegate.java
new file mode 100644
index 000000000..675042045
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.http.facade/src/main/java/org/openhab/core/io/http/facade/HttpRequestDelegate.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.http.facade;
+
+import java.util.Optional;
+
+/**
+ * A thin abstraction layer over HTTP request to bridge servlet and JAX-RS api.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+public interface HttpRequestDelegate {
+
+ Optional getHeader(String headerName);
+
+ default Optional getAuthorizationHeader() {
+ return getHeader("Authorization");
+ }
+
+ Optional getCookie(String cookieName);
+}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth.local/pom.xml b/bundles/org.opensmarthouse.core.io.rest.auth.local/pom.xml
new file mode 100755
index 000000000..5c32d2d85
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/pom.xml
@@ -0,0 +1,52 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.io.rest.auth.local
+
+ OpenSmartHouse Core | Bundles | REST | Local (o)Auth 2 Server
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.config
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.cookie
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.jwt
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.rest
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.rest.auth
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core
+
+
+
+ javax.annotation
+ javax.annotation-api
+
+
+ org.bitbucket.b_c
+ jose4j
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JwtHelper.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/JwtHelper.java
similarity index 90%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JwtHelper.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/JwtHelper.java
index 5212d063f..f9731cda0 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JwtHelper.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/JwtHelper.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
import java.io.BufferedReader;
import java.io.File;
@@ -39,7 +39,7 @@
import org.jose4j.lang.JoseException;
import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthenticationException;
-import org.openhab.core.auth.User;
+import org.openhab.core.auth.local.User;
import org.opensmarthouse.core.OpenSmartHouse;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
@@ -117,7 +117,9 @@ public String getJwtAccessToken(User user, String clientId, String scope, int to
jwtClaims.setClaim("client_id", clientId);
jwtClaims.setClaim("scope", scope);
jwtClaims.setStringListClaim("role",
- new ArrayList<>(user.getRoles() != null ? user.getRoles() : Collections.emptySet()));
+ new ArrayList<>(user.getRoles() != null ? user.getRoles() : Collections.emptySet()));
+ jwtClaims.setStringListClaim("permissions",
+ new ArrayList<>(user.getPermissions() != null ? user.getPermissions() : Collections.emptySet()));
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(jwtClaims.toJson());
@@ -149,10 +151,16 @@ public Authentication verifyAndParseJwtAccessToken(String jwt) throws Authentica
JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
String username = jwtClaims.getSubject();
List roles = jwtClaims.getStringListClaimValue("role");
+ List permissions = jwtClaims.getStringListClaimValue("permissions");
String scope = jwtClaims.getStringClaimValue("scope");
- return new Authentication(username, roles.toArray(new String[roles.size()]), scope);
+ return new Authentication(username, toArray(roles), scope, toArray(permissions));
} catch (InvalidJwtException | MalformedClaimException e) {
throw new AuthenticationException("Error while processing JWT token", e);
}
}
+
+ protected String[] toArray(List entries) {
+ return entries.toArray(new String[entries.size()]);
+ }
+
}
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/LocalJwtAuthenticationProvider.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/LocalJwtAuthenticationProvider.java
new file mode 100644
index 000000000..c45d782f6
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/LocalJwtAuthenticationProvider.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.rest.auth.local.internal;
+
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.AuthenticationProvider;
+import org.openhab.core.auth.AuthenticationResult;
+import org.openhab.core.auth.Credentials;
+import org.openhab.core.auth.jwt.JWTCredentials;
+import org.openhab.core.auth.local.GenericUser;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component
+public class LocalJwtAuthenticationProvider implements AuthenticationProvider {
+
+ private final JwtHelper jwtHelper;
+
+ @Activate
+ public LocalJwtAuthenticationProvider(@Reference JwtHelper jwtHelper) {
+ this.jwtHelper = jwtHelper;
+ }
+
+ @Override
+ public AuthenticationResult authenticate(Credentials credentials) throws AuthenticationException {
+ if (!(credentials instanceof JWTCredentials)) {
+ throw new IllegalArgumentException("Unsupported credentials");
+ }
+
+ String token = ((JWTCredentials) credentials).getToken();
+ Authentication authentication = jwtHelper.verifyAndParseJwtAccessToken(token);
+ return new AuthenticationResult(
+ new GenericUser(authentication.getUsername()), credentials.getScheme(), authentication
+ );
+ }
+
+ @Override
+ public boolean supports(Class extends Credentials> type) {
+ return JWTCredentials.class.isAssignableFrom(type);
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/SessionCookieAuthenticationProvider.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/SessionCookieAuthenticationProvider.java
new file mode 100644
index 000000000..8e214e268
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/SessionCookieAuthenticationProvider.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.rest.auth.local.internal;
+
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationException;
+import org.openhab.core.auth.AuthenticationProvider;
+import org.openhab.core.auth.AuthenticationResult;
+import org.openhab.core.auth.Credentials;
+import org.openhab.core.auth.cookie.CookieCredentials;
+import org.openhab.core.auth.local.GenericUser;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserRegistry;
+import org.openhab.core.auth.local.UserSession;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * An authentication provider which rely on session cookie and try bo bind it with one of existing users.
+ *
+ * This logic (possibly in similar shape) originally was located in JAXRS authentication filter.
+ * The advantage of this provider is possibility to use shared cookie authentication both for servlet
+ * and REST resources.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+@Component
+public class SessionCookieAuthenticationProvider implements AuthenticationProvider {
+
+ private final UserRegistry registry;
+
+ @Activate
+ public SessionCookieAuthenticationProvider(@Reference UserRegistry registry) {
+ this.registry = registry;
+ }
+
+ @Override
+ public AuthenticationResult authenticate(Credentials credentials) throws AuthenticationException {
+ if (!(credentials instanceof CookieCredentials)) {
+ throw new IllegalArgumentException("Unsupported credentials");
+ }
+
+ String session = ((CookieCredentials) credentials).getValue();
+ for (User user : registry.getAll()) {
+ if (user instanceof ManagedUser) {
+ ManagedUser managed = (ManagedUser) user;
+ for (UserSession userSession : managed.getSessions()) {
+ if (session.equals(userSession.getSessionId())) {
+ return new AuthenticationResult(new GenericUser(user.getName()), credentials.getScheme(),
+ new Authentication(user.getName(), user.getRoles(), null, user.getPermissions())
+ );
+ }
+ }
+ }
+ }
+
+ throw new AuthenticationException("Failed to authenticate cookie");
+ }
+
+ @Override
+ public boolean supports(Class extends Credentials> type) {
+ return CookieCredentials.class.isAssignableFrom(type);
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenEndpointException.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenEndpointException.java
similarity index 97%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenEndpointException.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenEndpointException.java
index 088cd8666..fdb256f5a 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenEndpointException.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenEndpointException.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.AuthenticationException;
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResource.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResource.java
similarity index 97%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResource.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResource.java
index d5a557ef8..6d267463c 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResource.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResource.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
import java.net.URI;
import java.security.MessageDigest;
@@ -43,17 +43,17 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.jose4j.base64url.Base64Url;
-import org.openhab.core.auth.ManagedUser;
-import org.openhab.core.auth.PendingToken;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserApiToken;
-import org.openhab.core.auth.UserRegistry;
-import org.openhab.core.auth.UserSession;
+import org.openhab.core.auth.local.ManagedUser;
+import org.openhab.core.auth.local.PendingToken;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserApiToken;
+import org.openhab.core.auth.local.UserRegistry;
+import org.openhab.core.auth.local.UserSession;
import org.openhab.core.io.rest.JSONResponse;
import org.openhab.core.io.rest.RESTConstants;
import org.openhab.core.io.rest.RESTResource;
import org.openhab.core.io.rest.Stream2JSONInputStream;
-import org.openhab.core.io.rest.auth.internal.TokenEndpointException.ErrorType;
+import org.openhab.core.io.rest.auth.local.internal.TokenEndpointException.ErrorType;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResponseDTO.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResponseDTO.java
similarity index 94%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResponseDTO.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResponseDTO.java
index 82d4fd718..40f1e261a 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResponseDTO.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResponseDTO.java
@@ -10,9 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
-import org.openhab.core.auth.User;
+import org.openhab.core.auth.local.User;
/**
* A DTO object for a successful token endpoint response, as per RFC 6749, Section 5.1.
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResponseErrorDTO.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResponseErrorDTO.java
similarity index 94%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResponseErrorDTO.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResponseErrorDTO.java
index 263f607a4..530bdb23e 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/TokenResponseErrorDTO.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/TokenResponseErrorDTO.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
/**
* A DTO object for an unsuccessful token endpoint response, as per RFC 6749, Section 5.2.
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserApiTokenDTO.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserApiTokenDTO.java
similarity index 93%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserApiTokenDTO.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserApiTokenDTO.java
index 885391d1e..fcf15fb4b 100644
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserApiTokenDTO.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserApiTokenDTO.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
import java.util.Date;
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserDTO.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserDTO.java
similarity index 88%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserDTO.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserDTO.java
index b713e6c11..011823105 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserDTO.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserDTO.java
@@ -10,11 +10,11 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
import java.util.Collection;
-import org.openhab.core.auth.User;
+import org.openhab.core.auth.local.User;
/**
* A DTO representing a {@link User}.
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserSecurityContext.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserSecurityContext.java
similarity index 59%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserSecurityContext.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserSecurityContext.java
index 449718f86..13dfbe885 100644
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserSecurityContext.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserSecurityContext.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
import java.security.Principal;
@@ -19,7 +19,8 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.auth.Authentication;
-import org.openhab.core.auth.User;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.io.rest.auth.AuthenticationSecurityContext;
/**
* This {@link SecurityContext} contains information about a user, roles and authorizations granted to a client
@@ -30,31 +31,39 @@
@NonNullByDefault
public class UserSecurityContext implements AuthenticationSecurityContext {
- private User user;
- private Authentication authentication;
- private String authenticationScheme;
+ private final Principal principal;
+ private final Authentication authentication;
+ private final String authenticationScheme;
+ private final boolean secure;
+
+ public UserSecurityContext(Principal principal, Authentication authentication,
+ String scheme) {
+ this(principal, authentication, scheme, false);
+ }
/**
- * Constructs a security context from an instance of {@link User}
+ * Constructs a security context from an instance of Principal.
*
- * @param user the user
- * @param the related {@link Authentication}
+ * @param principal the principal
+ * @param authentication the related {@link Authentication}
* @param authenticationScheme the scheme that was used to authenticate the user, e.g. "Basic"
+ * @param secure determine if request was done over secure channel - ie. HTTPs.
*/
- public UserSecurityContext(User user, Authentication authentication, String authenticationScheme) {
- this.user = user;
+ public UserSecurityContext(Principal principal, Authentication authentication, String authenticationScheme, boolean secure) {
this.authentication = authentication;
+ this.principal = principal;
this.authenticationScheme = authenticationScheme;
+ this.secure = secure;
}
@Override
public Principal getUserPrincipal() {
- return user;
+ return principal;
}
@Override
public boolean isUserInRole(@Nullable String role) {
- return user.getRoles().contains(role);
+ return authentication.getRoles().contains(role);
}
@Override
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserSessionDTO.java b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserSessionDTO.java
similarity index 94%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserSessionDTO.java
rename to bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserSessionDTO.java
index 07888c6ba..f0166c7c6 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/UserSessionDTO.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth.local/src/main/java/org/openhab/core/io/rest/auth/local/internal/UserSessionDTO.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth.local.internal;
import java.util.Date;
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/pom.xml b/bundles/org.opensmarthouse.core.io.rest.auth/pom.xml
index 739011eda..a03f44ac8 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.rest.auth/pom.xml
@@ -39,6 +39,17 @@
org.bitbucket.b_cjose4j
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.http.facade
+ 0.9.3-SNAPSHOT
+ compile
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.auth
+ compile
+
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthenticationSecurityContext.java b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/AuthenticationSecurityContext.java
similarity index 89%
rename from bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthenticationSecurityContext.java
rename to bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/AuthenticationSecurityContext.java
index 28033bc76..e4b80aa70 100644
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthenticationSecurityContext.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/AuthenticationSecurityContext.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.io.rest.auth.internal;
+package org.openhab.core.io.rest.auth;
import javax.ws.rs.core.SecurityContext;
@@ -27,5 +27,5 @@ public interface AuthenticationSecurityContext extends SecurityContext {
*
* @return the authentication instance
*/
- public Authentication getAuthentication();
+ Authentication getAuthentication();
}
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/ApplicationSecurityContext.java b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/ApplicationSecurityContext.java
new file mode 100755
index 000000000..20474b938
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/ApplicationSecurityContext.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.rest.auth.internal;
+
+import java.security.Principal;
+import javax.ws.rs.core.SecurityContext;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.io.rest.auth.AuthenticationSecurityContext;
+
+@NonNullByDefault
+public class ApplicationSecurityContext implements AuthenticationSecurityContext {
+
+ private Authentication authentication;
+ private final boolean secure;
+ private final String scheme;
+
+ public ApplicationSecurityContext(Authentication authentication, boolean secure, String scheme) {
+ this.authentication = authentication;
+ this.secure = secure;
+ this.scheme = scheme;
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return new Principal() {
+ @Override
+ public String getName() {
+ return authentication.getUsername();
+ }
+ };
+ }
+
+ @Override
+ public boolean isUserInRole(@Nullable String role) {
+ return authentication.getRoles().contains(role);
+ }
+
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
+
+ @Override
+ public String getAuthenticationScheme() {
+ return scheme;
+ }
+
+ @Override
+ public Authentication getAuthentication() {
+ return authentication;
+ }
+}
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthFilter.java b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthFilter.java
index b7d351b26..537bf9b4a 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthFilter.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthFilter.java
@@ -13,34 +13,35 @@
package org.openhab.core.io.rest.auth.internal;
import java.io.IOException;
-import java.util.Base64;
+import java.util.List;
import java.util.Map;
-
+import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
-import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.ext.Provider;
-
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthenticationException;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserApiTokenCredentials;
-import org.openhab.core.auth.UserRegistry;
-import org.openhab.core.auth.UsernamePasswordCredentials;
+import org.openhab.core.auth.AuthenticationManager;
+import org.openhab.core.auth.AuthenticationResult;
+import org.openhab.core.auth.Credentials;
import org.openhab.core.config.core.ConfigurableService;
-import org.openhab.core.io.rest.JSONResponse;
+import org.openhab.core.io.auth.CredentialsExtractor;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
import org.openhab.core.io.rest.RESTConstants;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsExtension;
@@ -65,21 +66,15 @@
public class AuthFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
- private static final String ALT_AUTH_HEADER = "X-OPENHAB-TOKEN";
- private static final String API_TOKEN_PREFIX = "oh.";
-
protected static final String CONFIG_URI = "system:restauth";
- private static final String CONFIG_ALLOW_BASIC_AUTH = "allowBasicAuth";
- private static final String CONFIG_IMPLICIT_USER_ROLE = "implicitUserRole";
- private boolean allowBasicAuth = false;
- private boolean implicitUserRole = true;
+ private boolean enabled = true;
@Reference
- private JwtHelper jwtHelper;
+ private AuthenticationManager authenticationManager;
+
+ private List> extractors = new CopyOnWriteArrayList<>();
- @Reference
- private UserRegistry userRegistry;
@Activate
protected void activate(Map config) {
@@ -88,91 +83,56 @@ protected void activate(Map config) {
@Modified
protected void modified(@Nullable Map properties) {
- if (properties != null) {
- Object value = properties.get(CONFIG_ALLOW_BASIC_AUTH);
- allowBasicAuth = value != null && "true".equals(value.toString());
- value = properties.get(CONFIG_IMPLICIT_USER_ROLE);
- implicitUserRole = value == null || !"false".equals(value.toString());
- }
- }
- private SecurityContext authenticateBearerToken(String token) throws AuthenticationException {
- if (token.startsWith(API_TOKEN_PREFIX)) {
- UserApiTokenCredentials credentials = new UserApiTokenCredentials(token);
- Authentication auth = userRegistry.authenticate(credentials);
- User user = userRegistry.get(auth.getUsername());
- if (user == null) {
- throw new AuthenticationException("User not found in registry");
- }
- return new UserSecurityContext(user, auth, "ApiToken");
- } else {
- Authentication auth = jwtHelper.verifyAndParseJwtAccessToken(token);
- return new JwtSecurityContext(auth);
- }
- }
-
- private SecurityContext authenticateUsernamePassword(String username, String password)
- throws AuthenticationException {
- UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
- Authentication auth = userRegistry.authenticate(credentials);
- User user = userRegistry.get(auth.getUsername());
- if (user == null) {
- throw new AuthenticationException("User not found in registry");
- }
- return new UserSecurityContext(user, auth, "Basic");
}
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
- try {
- String altTokenHeader = requestContext.getHeaderString(ALT_AUTH_HEADER);
- if (altTokenHeader != null) {
- requestContext.setSecurityContext(authenticateBearerToken(altTokenHeader));
+ if (this.enabled) {
+ if (authenticationManager == null) {
+ Response response = Response.status(Status.UNAUTHORIZED).entity("Failed to authenticate request.").build();
+ requestContext.abortWith(response);
return;
}
- String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
- if (authHeader != null) {
- String[] authParts = authHeader.split(" ");
- if (authParts.length == 2) {
- if ("Bearer".equalsIgnoreCase(authParts[0])) {
- requestContext.setSecurityContext(authenticateBearerToken(authParts[1]));
+ int found = 0, failed = 0;
+ for (CredentialsExtractor extractor : extractors) {
+ Optional extracted = extractor.retrieveCredentials(new JaxRsRequestDelegate(requestContext));
+ if (extracted.isPresent()) {
+ found++;
+ Credentials credentials = extracted.get();
+ try {
+ AuthenticationResult authentication = authenticationManager.authenticate(credentials);
+ requestContext.setSecurityContext(new ApplicationSecurityContext(authentication.getAuthentication(), false, authentication.getScheme()));
return;
- } else if ("Basic".equalsIgnoreCase(authParts[0])) {
- try {
- String[] decodedCredentials = new String(Base64.getDecoder().decode(authParts[1]), "UTF-8")
- .split(":");
- if (decodedCredentials.length > 2) {
- throw new AuthenticationException("Invalid Basic authentication credential format");
- }
- switch (decodedCredentials.length) {
- case 1:
- requestContext.setSecurityContext(authenticateBearerToken(decodedCredentials[0]));
- break;
- case 2:
- if (!allowBasicAuth) {
- throw new AuthenticationException(
- "Basic authentication with username/password is not allowed");
- }
- requestContext.setSecurityContext(
- authenticateUsernamePassword(decodedCredentials[0], decodedCredentials[1]));
- }
-
- return;
- } catch (AuthenticationException e) {
- throw new AuthenticationException("Invalid Basic authentication credentials", e);
+ } catch (AuthenticationException e) {
+ failed++;
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to authenticate using credentials {}", credentials, e);
+ } else {
+ logger.info("Failed to authenticate using credentials {}", credentials);
}
}
}
}
- if (implicitUserRole) {
+ if (true) {
requestContext.setSecurityContext(new AnonymousUserSecurityContext());
+ return;
}
- } catch (AuthenticationException e) {
- logger.warn("Unauthorized API request: {}", e.getMessage());
- requestContext.abortWith(JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "Invalid credentials"));
+ Response response = Response.status(Status.UNAUTHORIZED).entity("Could not authenticate request. Found " + found
+ + " credentials in request out of which " + failed + " were invalid").build();
+ requestContext.abortWith(response);
}
}
+
+ @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, target = "(context=org.openhab.core.io.http.facade.HttpRequestDelegate)")
+ public void addCredentialsExtractor(CredentialsExtractor extractor) {
+ this.extractors.add(extractor);
+ }
+
+ public void removeCredentialsExtractor(CredentialsExtractor extractor) {
+ this.extractors.remove(extractor);
+ }
}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthzFilter.java b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthzFilter.java
new file mode 100644
index 000000000..d99422031
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/AuthzFilter.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.rest.auth.internal;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.ext.Provider;
+import org.openhab.core.auth.MutableAuthenticationContextHolder;
+import org.openhab.core.io.rest.RESTConstants;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
+import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
+import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsExtension;
+
+/**
+ * A propagation of security context through {@link org.openhab.core.auth.AuthenticationContextHolder}.
+ *
+ * @author Łukasz Dywicki - Initial contribution.
+ */
+@Component
+@JaxrsExtension
+@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
+@Priority(Priorities.AUTHORIZATION)
+@Provider
+public class AuthzFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+ private final MutableAuthenticationContextHolder authenticationContextHolder;
+
+ @Activate
+ public AuthzFilter(@Reference MutableAuthenticationContextHolder authenticationContextHolder) {
+ this.authenticationContextHolder = authenticationContextHolder;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+ SecurityContext securityContext = requestContext.getSecurityContext();
+ if (securityContext instanceof ApplicationSecurityContext) {
+ authenticationContextHolder.setAuthentication(((ApplicationSecurityContext) securityContext).getAuthentication());
+ }
+ }
+
+ @Override
+ public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+ authenticationContextHolder.setAuthentication(null);
+ }
+
+}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JaxRsRequestDelegate.java b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JaxRsRequestDelegate.java
new file mode 100644
index 000000000..6e1092fe8
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JaxRsRequestDelegate.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.io.rest.auth.internal;
+
+import java.util.Optional;
+import javax.ws.rs.container.ContainerRequestContext;
+import org.openhab.core.io.http.facade.Cookie;
+import org.openhab.core.io.http.facade.HttpRequestDelegate;
+
+/**
+ * JAX-RS request wrapper.
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+public class JaxRsRequestDelegate implements HttpRequestDelegate {
+
+ private final ContainerRequestContext request;
+
+ public JaxRsRequestDelegate(ContainerRequestContext request) {
+ this.request = request;
+ }
+
+ @Override
+ public Optional getHeader(String headerName) {
+ return Optional.ofNullable(request.getHeaderString(headerName));
+ }
+
+ @Override
+ public Optional getCookie(String cookieName) {
+ if (request.getCookies().containsKey(cookieName)) {
+ javax.ws.rs.core.Cookie cookie = request.getCookies().get(cookieName);
+ return Optional.of(new Cookie(cookie.getName(), cookie.getValue()));
+ }
+ return Optional.empty();
+ }
+}
diff --git a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JwtSecurityContext.java b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JwtSecurityContext.java
index ae47ec7ba..5e87692de 100755
--- a/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JwtSecurityContext.java
+++ b/bundles/org.opensmarthouse.core.io.rest.auth/src/main/java/org/openhab/core/io/rest/auth/internal/JwtSecurityContext.java
@@ -19,7 +19,7 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.auth.Authentication;
-import org.openhab.core.auth.GenericUser;
+import org.openhab.core.io.rest.auth.AuthenticationSecurityContext;
/**
* This {@link SecurityContext} contains information about a user, roles and authorizations granted to a client as
@@ -38,7 +38,12 @@ public JwtSecurityContext(Authentication authentication) {
@Override
public Principal getUserPrincipal() {
- return new GenericUser(authentication.getUsername());
+ return new Principal() {
+ @Override
+ public String getName() {
+ return authentication.getUsername();
+ }
+ };
}
@Override
diff --git a/bundles/org.opensmarthouse.core.io.rest.item/pom.xml b/bundles/org.opensmarthouse.core.io.rest.item/pom.xml
index 256265fa2..71fc17bf8 100755
--- a/bundles/org.opensmarthouse.core.io.rest.item/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.rest.item/pom.xml
@@ -51,6 +51,10 @@
org.osgiosgi.enroute.hamcrest.wrapper
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.rest.auth
+
diff --git a/bundles/org.opensmarthouse.core.io.rest.item/src/main/java/org/openhab/core/io/rest/core/internal/item/ItemResource.java b/bundles/org.opensmarthouse.core.io.rest.item/src/main/java/org/openhab/core/io/rest/core/internal/item/ItemResource.java
index 0c0b70e84..34b192e0b 100755
--- a/bundles/org.opensmarthouse.core.io.rest.item/src/main/java/org/openhab/core/io/rest/core/internal/item/ItemResource.java
+++ b/bundles/org.opensmarthouse.core.io.rest.item/src/main/java/org/openhab/core/io/rest/core/internal/item/ItemResource.java
@@ -47,8 +47,13 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationContextHolder;
+import org.openhab.core.auth.AuthorizationManager;
+import org.openhab.core.auth.Permissions;
import org.openhab.core.auth.Role;
import org.openhab.core.events.EventPublisher;
+import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.rest.DTOMapper;
import org.openhab.core.io.rest.JSONResponse;
import org.openhab.core.io.rest.LocaleService;
@@ -145,13 +150,21 @@ public class ItemResource implements RESTResource {
*/
private static void respectForwarded(final UriBuilder uriBuilder, final @Context HttpHeaders httpHeaders) {
Optional.ofNullable(httpHeaders.getHeaderString("X-Forwarded-Host")).ifPresent(host -> {
+ if (host.contains(",")) {
+ host = host.split(",")[0];
+ }
final String[] parts = host.split(":");
uriBuilder.host(parts[0]);
if (parts.length > 1) {
uriBuilder.port(Integer.parseInt(parts[1]));
}
});
- Optional.ofNullable(httpHeaders.getHeaderString("X-Forwarded-Proto")).ifPresent(uriBuilder::scheme);
+ Optional.ofNullable(httpHeaders.getHeaderString("X-Forwarded-Proto")).map(scheme -> {
+ if (scheme.contains(",")) {
+ return scheme.split(",")[0];
+ }
+ return scheme;
+ }).ifPresent(uriBuilder::scheme);
}
private final Logger logger = LoggerFactory.getLogger(ItemResource.class);
@@ -161,9 +174,12 @@ private static void respectForwarded(final UriBuilder uriBuilder, final @Context
private final ItemBuilderFactory itemBuilderFactory;
private final ItemRegistry itemRegistry;
private final LocaleService localeService;
+ private final TranslationProvider translationProvider;
private final ManagedItemProvider managedItemProvider;
private final MetadataRegistry metadataRegistry;
private final MetadataSelectorMatcher metadataSelectorMatcher;
+ private final AuthorizationManager authorizationManager;
+ private final AuthenticationContextHolder authenticationContextHolder;
@Activate
public ItemResource(//
@@ -172,17 +188,23 @@ public ItemResource(//
final @Reference ItemBuilderFactory itemBuilderFactory, //
final @Reference ItemRegistry itemRegistry, //
final @Reference LocaleService localeService, //
+ final @Reference TranslationProvider translationProvider, //
final @Reference ManagedItemProvider managedItemProvider,
final @Reference MetadataRegistry metadataRegistry,
- final @Reference MetadataSelectorMatcher metadataSelectorMatcher) {
+ final @Reference MetadataSelectorMatcher metadataSelectorMatcher,
+ final @Reference AuthorizationManager authorizationManager,
+ final @Reference AuthenticationContextHolder authenticationContextHolder) {
this.dtoMapper = dtoMapper;
this.eventPublisher = eventPublisher;
this.itemBuilderFactory = itemBuilderFactory;
this.itemRegistry = itemRegistry;
this.localeService = localeService;
+ this.translationProvider = translationProvider;
this.managedItemProvider = managedItemProvider;
this.metadataRegistry = metadataRegistry;
this.metadataSelectorMatcher = metadataSelectorMatcher;
+ this.authorizationManager = authorizationManager;
+ this.authenticationContextHolder = authenticationContextHolder;
}
private UriBuilder uriBuilder(final UriInfo uriInfo, final HttpHeaders httpHeaders) {
@@ -209,10 +231,13 @@ public Response getItems(final @Context UriInfo uriInfo, final @Context HttpHead
final UriBuilder uriBuilder = uriBuilder(uriInfo, httpHeaders);
uriBuilder.path("{itemName}");
+ final Authentication authentication = authenticationContextHolder.getAuthentication();
Stream itemStream = getItems(type, tags).stream() //
+ .filter(item -> authorizationManager.hasPermission(Permissions.READ, item, authentication))
.map(item -> EnrichedItemDTOMapper.map(item, recursive, null, uriBuilder, locale)) //
.peek(dto -> addMetadata(dto, namespaces, null)) //
- .peek(dto -> dto.editable = isEditable(dto.name));
+ .peek(dto -> dto.editable = isEditable(dto.name))
+ .peek(dto -> dto.label = translationProvider.getText(null, "item." + dto.name, dto.label, locale));
itemStream = dtoMapper.limitToFields(itemStream, fields);
return Response.ok(new Stream2JSONInputStream(itemStream)).build();
}
@@ -228,6 +253,11 @@ public Response getItemData(final @Context UriInfo uriInfo, final @Context HttpH
@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language,
@QueryParam("metadata") @Parameter(description = "metadata selector") @Nullable String namespaceSelector,
@PathParam("itemname") @Parameter(description = "item name") String itemname) {
+
+ if (!authorizationManager.hasPermission(Permissions.READ, itemname, Item.class, authenticationContextHolder.getAuthentication())) {
+ return Response.status(Status.UNAUTHORIZED).build();
+ }
+
final Locale locale = localeService.getLocale(language);
final Set namespaces = splitAndFilterNamespaces(namespaceSelector, locale);
@@ -239,6 +269,7 @@ public Response getItemData(final @Context UriInfo uriInfo, final @Context HttpH
EnrichedItemDTO dto = EnrichedItemDTOMapper.map(item, true, null, uriBuilder(uriInfo, httpHeaders), locale);
addMetadata(dto, namespaces, null);
dto.editable = isEditable(dto.name);
+ dto.label = translationProvider.getText(null, "item." + dto.name, dto.label, locale);
return JSONResponse.createResponse(Status.OK, dto, null);
} else {
return getItemNotFoundResponse(itemname);
@@ -262,6 +293,10 @@ private Set splitAndFilterNamespaces(@Nullable String namespaceSelector,
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "404", description = "Item not found") })
public Response getPlainItemState(@PathParam("itemname") @Parameter(description = "item name") String itemname) {
+ if (!authorizationManager.hasPermission(Permissions.STATE, itemname, Item.class, authenticationContextHolder.getAuthentication())) {
+ return Response.status(Status.UNAUTHORIZED).build();
+ }
+
// get item
Item item = getItem(itemname);
@@ -287,6 +322,11 @@ public Response putItemState(
@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language,
@PathParam("itemname") @Parameter(description = "item name") String itemname,
@Parameter(description = "valid item state (e.g. ON, OFF)", required = true) String value) {
+
+ if (!authorizationManager.hasPermission(Permissions.STATE, itemname, Item.class, authenticationContextHolder.getAuthentication())) {
+ return Response.status(Status.UNAUTHORIZED).build();
+ }
+
final Locale locale = localeService.getLocale(language);
// get Item
@@ -321,6 +361,11 @@ public Response putItemState(
@ApiResponse(responseCode = "400", description = "Item command null") })
public Response postItemCommand(@PathParam("itemname") @Parameter(description = "item name") String itemname,
@Parameter(description = "valid item command (e.g. ON, OFF, UP, DOWN, REFRESH)", required = true) String value) {
+
+ if (!authorizationManager.hasPermission(Permissions.COMMAND, itemname, Item.class, authenticationContextHolder.getAuthentication())) {
+ return Response.status(Status.UNAUTHORIZED).build();
+ }
+
Item item = getItem(itemname);
Command command = null;
if (item != null) {
@@ -718,7 +763,7 @@ private JsonObject buildStatusObject(String itemName, String status, @Nullable S
/**
* helper: Response to be sent to client if a Thing cannot be found
*
- * @param thingUID
+ * @param itemname
* @return Response configured for 'item not found'
*/
private static Response getItemNotFoundResponse(String itemname) {
diff --git a/bundles/org.opensmarthouse.core.io.rest.persistence/pom.xml b/bundles/org.opensmarthouse.core.io.rest.persistence/pom.xml
index 6f18bd31f..9e7bc22f3 100755
--- a/bundles/org.opensmarthouse.core.io.rest.persistence/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.rest.persistence/pom.xml
@@ -26,6 +26,10 @@
javax.annotationjavax.annotation-api
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.rest.auth
+
diff --git a/bundles/org.opensmarthouse.core.io.rest.persistence/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java b/bundles/org.opensmarthouse.core.io.rest.persistence/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java
index bb8b317e8..d50250e77 100755
--- a/bundles/org.opensmarthouse.core.io.rest.persistence/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java
+++ b/bundles/org.opensmarthouse.core.io.rest.persistence/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java
@@ -20,6 +20,8 @@
import java.util.List;
import java.util.Locale;
+import java.util.Set;
+import java.util.function.BiFunction;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -37,6 +39,11 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationContextHolder;
+import org.openhab.core.auth.AuthorizationManager;
+import org.openhab.core.auth.AuthorizationManagerFilters;
+import org.openhab.core.auth.Permissions;
import org.openhab.core.auth.Role;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.rest.JSONResponse;
@@ -53,6 +60,7 @@
import org.openhab.core.persistence.FilterCriteria.Ordering;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.ModifiablePersistenceService;
+import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.persistence.PersistenceService;
import org.openhab.core.persistence.PersistenceServiceRegistry;
import org.openhab.core.persistence.QueryablePersistenceService;
@@ -115,17 +123,23 @@ public class PersistenceResource implements RESTResource {
private final LocaleService localeService;
private final PersistenceServiceRegistry persistenceServiceRegistry;
private final TimeZoneProvider timeZoneProvider;
+ private final AuthorizationManager authorizationManager;
+ private final AuthenticationContextHolder authenticationContextHolder;
@Activate
public PersistenceResource( //
final @Reference ItemRegistry itemRegistry, //
final @Reference LocaleService localeService,
final @Reference PersistenceServiceRegistry persistenceServiceRegistry,
- final @Reference TimeZoneProvider timeZoneProvider) {
+ final @Reference TimeZoneProvider timeZoneProvider,
+ final @Reference AuthorizationManager authorizationManager,
+ final @Reference AuthenticationContextHolder authenticationContextHolder) {
this.itemRegistry = itemRegistry;
this.localeService = localeService;
this.persistenceServiceRegistry = persistenceServiceRegistry;
this.timeZoneProvider = timeZoneProvider;
+ this.authorizationManager = authorizationManager;
+ this.authenticationContextHolder = authenticationContextHolder;
}
@GET
@@ -172,6 +186,10 @@ public Response httpGetPersistenceItemData(@Context HttpHeaders headers,
@Parameter(description = "Page number of data to return. This parameter will enable paging.") @QueryParam("page") int pageNumber,
@Parameter(description = "The length of each page.") @QueryParam("pagelength") int pageLength,
@Parameter(description = "Gets one value before and after the requested period.") @QueryParam("boundary") boolean boundary) {
+
+ if (!authorizationManager.hasPermission(Permissions.READ, itemName, Item.class, authenticationContextHolder.getAuthentication())) {
+ return Response.status(Status.UNAUTHORIZED).build();
+ }
return getItemHistoryDTO(serviceId, itemName, startTime, endTime, pageNumber, pageLength, boundary);
}
@@ -184,6 +202,7 @@ public Response httpGetPersistenceItemData(@Context HttpHeaders headers,
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = String.class)))),
@ApiResponse(responseCode = "400", description = "Invalid filter parameters"),
@ApiResponse(responseCode = "404", description = "Unknown persistence service") })
+
public Response httpDeletePersistenceServiceItem(@Context HttpHeaders headers,
@Parameter(description = "Id of the persistence service.", required = true) @QueryParam("serviceId") String serviceId,
@Parameter(description = "The item name.") @PathParam("itemname") String itemName,
@@ -361,7 +380,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName,
/**
* Gets a list of persistence services currently configured in the system
*
- * @return list of persistence services as {@link ServiceBean}
+ * @return list of persistence services as {@link PersistenceServiceDTO}
*/
private List getPersistenceServiceList(Locale locale) {
List dtoList = new ArrayList<>();
@@ -407,7 +426,9 @@ private Response getServiceItemList(@Nullable String serviceId) {
QueryablePersistenceService qService = (QueryablePersistenceService) service;
- return JSONResponse.createResponse(Status.OK, qService.getItemInfo(), "");
+ Set itemInfo = AuthorizationManagerFilters.filter(authenticationContextHolder.getAuthentication(),
+ new AuthorizationCheck(authorizationManager), qService.getItemInfo());
+ return JSONResponse.createResponse(Status.OK, itemInfo, "");
}
private Response deletePersistenceItemData(@Nullable String serviceId, String itemName, @Nullable String timeBegin,
@@ -508,4 +529,18 @@ private Response putItemState(@Nullable String serviceId, String itemName, Strin
mService.store(item, Date.from(dateTime.toInstant()), state);
return Response.status(Status.OK).build();
}
+
+ static class AuthorizationCheck implements BiFunction {
+
+ private final AuthorizationManager authorizationManager;
+
+ AuthorizationCheck(AuthorizationManager authorizationManager) {
+ this.authorizationManager = authorizationManager;
+ }
+
+ @Override
+ public Boolean apply(Authentication auth, PersistenceItemInfo info) {
+ return authorizationManager.hasPermission(Permissions.READ, info.getName(), Item.class, auth);
+ }
+ }
}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.io.rest.sse/pom.xml b/bundles/org.opensmarthouse.core.io.rest.sse/pom.xml
index f0eaa224d..0d7a498ea 100755
--- a/bundles/org.opensmarthouse.core.io.rest.sse/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.rest.sse/pom.xml
@@ -44,6 +44,10 @@
junitjunit
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.io.rest.auth
+
diff --git a/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/SseResource.java b/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/SseResource.java
index 6aee746e9..bc177108a 100755
--- a/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/SseResource.java
+++ b/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/SseResource.java
@@ -12,16 +12,20 @@
*/
package org.openhab.core.io.rest.sse;
+import static org.openhab.core.io.rest.sse.internal.SseSinkItemInfo.canAccessItem;
import static org.openhab.core.io.rest.sse.internal.SseSinkItemInfo.hasConnectionId;
import static org.openhab.core.io.rest.sse.internal.SseSinkItemInfo.tracksItem;
+import static org.openhab.core.io.rest.sse.internal.SseSinkTopicInfo.hasItemAccess;
import static org.openhab.core.io.rest.sse.internal.SseSinkTopicInfo.matchesTopic;
import java.io.IOException;
+import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.function.Predicate;
import javax.annotation.security.RolesAllowed;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletResponse;
@@ -36,22 +40,31 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.sse.OutboundSseEvent;
import javax.ws.rs.sse.Sse;
import javax.ws.rs.sse.SseEventSink;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthenticationContextHolder;
+import org.openhab.core.auth.AuthorizationManager;
+import org.openhab.core.auth.Permissions;
import org.openhab.core.auth.Role;
import org.openhab.core.events.Event;
import org.openhab.core.io.rest.RESTConstants;
import org.openhab.core.io.rest.RESTResource;
import org.openhab.core.io.rest.SseBroadcaster;
+import org.openhab.core.io.rest.auth.AuthenticationSecurityContext;
import org.openhab.core.io.rest.sse.internal.SseItemStatesEventBuilder;
import org.openhab.core.io.rest.sse.internal.SsePublisher;
import org.openhab.core.io.rest.sse.internal.SseSinkItemInfo;
import org.openhab.core.io.rest.sse.internal.SseSinkTopicInfo;
import org.openhab.core.io.rest.sse.internal.dto.EventDTO;
import org.openhab.core.io.rest.sse.internal.util.SseUtil;
+import org.openhab.core.items.Item;
+import org.openhab.core.items.events.ItemEvent;
import org.openhab.core.items.events.ItemStateChangedEvent;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@@ -104,11 +117,16 @@ public class SseResource implements RESTResource, SsePublisher {
private final SseBroadcaster itemStatesBroadcaster = new SseBroadcaster<>();
private final SseItemStatesEventBuilder itemStatesEventBuilder;
private final SseBroadcaster topicBroadcaster = new SseBroadcaster<>();
+ private final AuthorizationManager authorizationManager;
+ private final AuthenticationContextHolder authenticationContextHolder;
private ExecutorService executorService;
@Activate
- public SseResource(@Reference SseItemStatesEventBuilder itemStatesEventBuilder) {
+ public SseResource(@Reference SseItemStatesEventBuilder itemStatesEventBuilder, @Reference
+ AuthorizationManager authorizationManager, @Reference AuthenticationContextHolder authenticationContextHolder) {
+ this.authorizationManager = authorizationManager;
+ this.authenticationContextHolder = authenticationContextHolder;
this.executorService = Executors.newSingleThreadExecutor();
this.itemStatesEventBuilder = itemStatesEventBuilder;
}
@@ -156,14 +174,14 @@ private void addCommonResponseHeaders(final HttpServletResponse response) {
@Operation(operationId = "getEvents", summary = "Get all events.", responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "400", description = "Topic is empty or contains invalid characters") })
- public void listen(@Context final SseEventSink sseEventSink, @Context final HttpServletResponse response,
+ public void listen(@Context final SseEventSink sseEventSink, @Context final HttpServletResponse response, @Context SecurityContext securityContext,
@QueryParam("topics") @Parameter(description = "topics") String eventFilter) {
if (!SseUtil.isValidTopicFilter(eventFilter)) {
response.setStatus(Status.BAD_REQUEST.getStatusCode());
return;
}
- topicBroadcaster.add(sseEventSink, new SseSinkTopicInfo(eventFilter));
+ topicBroadcaster.add(sseEventSink, new SseSinkTopicInfo(eventFilter, retrieveAuthentication(securityContext)));
addCommonResponseHeaders(response);
}
@@ -172,7 +190,13 @@ private void handleEventBroadcastTopic(Event event) {
final EventDTO eventDTO = SseUtil.buildDTO(event);
final OutboundSseEvent sseEvent = SseUtil.buildEvent(sse.newEventBuilder(), eventDTO);
- topicBroadcaster.sendIf(sseEvent, matchesTopic(eventDTO.topic));
+ Predicate predicate = (sink) -> true;
+ if (event instanceof ItemEvent) {
+ String item = ((ItemEvent) event).getItemName();
+ predicate = hasItemAccess(authorizationManager, item);
+ }
+
+ topicBroadcaster.sendIf(sseEvent, matchesTopic(eventDTO.topic).and(predicate));
}
/**
@@ -186,8 +210,8 @@ private void handleEventBroadcastTopic(Event event) {
@Produces(MediaType.SERVER_SENT_EVENTS)
@Operation(operationId = "initNewStateTacker", summary = "Initiates a new item state tracker connection", responses = {
@ApiResponse(responseCode = "200", description = "OK") })
- public void getStateEvents(@Context final SseEventSink sseEventSink, @Context final HttpServletResponse response) {
- final SseSinkItemInfo sinkItemInfo = new SseSinkItemInfo();
+ public void getStateEvents(@Context final SseEventSink sseEventSink, @Context final HttpServletResponse response, @Context SecurityContext securityContext) {
+ final SseSinkItemInfo sinkItemInfo = new SseSinkItemInfo(retrieveAuthentication(securityContext));
itemStatesBroadcaster.add(sseEventSink, sinkItemInfo);
addCommonResponseHeaders(response);
@@ -208,7 +232,7 @@ public void getStateEvents(@Context final SseEventSink sseEventSink, @Context fi
@Operation(operationId = "updateItemListForStateUpdates", summary = "Changes the list of items a SSE connection will receive state updates to.", responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "Unknown connectionId") })
- public Object updateTrackedItems(@PathParam("connectionId") String connectionId,
+ public Object updateTrackedItems(@PathParam("connectionId") String connectionId, @Context SecurityContext securityContext,
@Parameter(description = "items") Set itemNames) {
Optional itemStateInfo = itemStatesBroadcaster.getInfoIf(hasConnectionId(connectionId))
.findFirst();
@@ -216,9 +240,17 @@ public Object updateTrackedItems(@PathParam("connectionId") String connectionId,
return Response.status(Status.NOT_FOUND).build();
}
+ Set subscribedItemNames = new LinkedHashSet<>();
+ @Nullable Authentication authentication = retrieveAuthentication(securityContext);
+ for (String item : itemNames) {
+ if (authorizationManager.hasPermission(Permissions.READ, item, Item.class, authentication)) {
+ subscribedItemNames.add(item);
+ }
+ }
+
itemStateInfo.get().updateTrackedItems(itemNames);
- OutboundSseEvent itemStateEvent = itemStatesEventBuilder.buildEvent(sse.newEventBuilder(), itemNames);
+ OutboundSseEvent itemStateEvent = itemStatesEventBuilder.buildEvent(sse.newEventBuilder(), subscribedItemNames);
if (itemStateEvent != null) {
itemStatesBroadcaster.sendIf(itemStateEvent, hasConnectionId(connectionId));
}
@@ -233,7 +265,9 @@ public Object updateTrackedItems(@PathParam("connectionId") String connectionId,
*/
public void handleEventBroadcastItemState(final ItemStateChangedEvent stateChangeEvent) {
String itemName = stateChangeEvent.getItemName();
- boolean isTracked = itemStatesBroadcaster.getInfoIf(info -> true).anyMatch(tracksItem(itemName));
+ boolean isTracked = itemStatesBroadcaster.getInfoIf(info -> true).anyMatch(tracksItem(itemName)
+ .and(canAccessItem(authorizationManager, itemName))
+ );
if (isTracked) {
OutboundSseEvent event = itemStatesEventBuilder.buildEvent(sse.newEventBuilder(), Set.of(itemName));
if (event != null) {
@@ -241,4 +275,20 @@ public void handleEventBroadcastItemState(final ItemStateChangedEvent stateChang
}
}
}
+
+ /**
+ * The security context propagated through ThreadLocal is sometimes lost hence use of this
+ * helper method which allows attaching of security context managed by request container itself.
+ *
+ * @param securityContext Request security context.
+ * @return Authentication or null if it cannot be found.
+ */
+ @Nullable
+ private Authentication retrieveAuthentication(SecurityContext securityContext) {
+ return Optional.ofNullable(securityContext)
+ .filter(AuthenticationSecurityContext.class::isInstance)
+ .map(AuthenticationSecurityContext.class::cast)
+ .map(AuthenticationSecurityContext::getAuthentication)
+ .orElse(authenticationContextHolder.getAuthentication());
+ }
}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkItemInfo.java b/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkItemInfo.java
index 560b4d263..aa315cc7c 100644
--- a/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkItemInfo.java
+++ b/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkItemInfo.java
@@ -18,6 +18,11 @@
import java.util.function.Predicate;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthorizationManager;
+import org.openhab.core.auth.Permissions;
+import org.openhab.core.items.Item;
/**
* The specific information we need to hold for a SSE sink which tracks item state updates.
@@ -29,6 +34,11 @@ public class SseSinkItemInfo {
private final String connectionId = UUID.randomUUID().toString();
private final Set trackedItems = new CopyOnWriteArraySet<>();
+ private final @Nullable Authentication authentication;
+
+ public SseSinkItemInfo(@Nullable Authentication authentication) {
+ this.authentication = authentication;
+ }
/**
* Gets the connection identifier of this {@link SseSinkItemInfo}
@@ -56,4 +66,13 @@ public static Predicate hasConnectionId(String connectionId) {
public static Predicate tracksItem(String itemName) {
return info -> info.trackedItems.contains(itemName);
}
+
+ public static Predicate canAccessItem(AuthorizationManager manager, String itemName) {
+ return info -> {
+ if (info.authentication == null) {
+ return false;
+ }
+ return manager.hasPermission(Permissions.READ, itemName, Item.class, info.authentication);
+ };
+ }
}
diff --git a/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkTopicInfo.java b/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkTopicInfo.java
index 1d11be620..2f11deae9 100644
--- a/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkTopicInfo.java
+++ b/bundles/org.opensmarthouse.core.io.rest.sse/src/main/java/org/openhab/core/io/rest/sse/internal/SseSinkTopicInfo.java
@@ -16,7 +16,12 @@
import java.util.function.Predicate;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.AuthorizationManager;
+import org.openhab.core.auth.Permissions;
import org.openhab.core.io.rest.sse.internal.util.SseUtil;
+import org.openhab.core.items.Item;
/**
* The specific information we need to hold for a SSE sink which subscribes to event topics.
@@ -27,12 +32,23 @@
public class SseSinkTopicInfo {
private final List regexFilters;
+ private final @Nullable Authentication authentication;
- public SseSinkTopicInfo(String topicFilter) {
+ public SseSinkTopicInfo(String topicFilter, @Nullable Authentication authentication) {
this.regexFilters = SseUtil.convertToRegex(topicFilter);
+ this.authentication = authentication;
}
public static Predicate matchesTopic(final String topic) {
return info -> info.regexFilters.stream().anyMatch(topic::matches);
}
+
+ public static Predicate hasItemAccess(final AuthorizationManager manager, final String item) {
+ return info -> {
+ if (info.authentication == null) {
+ return false;
+ }
+ return manager.hasPermission(Permissions.READ, item, Item.class, info.authentication);
+ };
+ }
}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.io.rest/src/main/java/org/openhab/core/io/rest/Stream2JSONInputStream.java b/bundles/org.opensmarthouse.core.io.rest/src/main/java/org/openhab/core/io/rest/Stream2JSONInputStream.java
index 50f9b6fca..09594bed8 100755
--- a/bundles/org.opensmarthouse.core.io.rest/src/main/java/org/openhab/core/io/rest/Stream2JSONInputStream.java
+++ b/bundles/org.opensmarthouse.core.io.rest/src/main/java/org/openhab/core/io/rest/Stream2JSONInputStream.java
@@ -17,6 +17,7 @@
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
+import java.util.function.Predicate;
import java.util.stream.Stream;
import org.openhab.core.library.types.DateTimeType;
@@ -41,6 +42,7 @@ public class Stream2JSONInputStream extends InputStream implements JSONInputStre
private boolean firstIteratorElement;
private final Gson gson = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS).create();
+ private Predicate predicate;
/**
* Creates a new {@link Stream2JSONInputStream} backed by the given {@link Stream} source.
@@ -53,11 +55,22 @@ public Stream2JSONInputStream(Stream> source) {
throw new IllegalArgumentException("The source must not be null!");
}
- iterator = source.map(e -> gson.toJson(e)).iterator();
+ iterator = source.filter(this::filter).map(e -> gson.toJson(e)).iterator();
jsonElementStream = new ByteArrayInputStream(new byte[0]);
firstIteratorElement = true;
}
+ public void setFilter(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ private boolean filter(Object element) {
+ if (this.predicate == null) {
+ return true;
+ }
+ return predicate.test(element);
+ }
+
@Override
public int read() throws IOException {
int result = jsonElementStream.read();
diff --git a/bundles/org.opensmarthouse.core.io.transport.mqtt/pom.xml b/bundles/org.opensmarthouse.core.io.transport.mqtt/pom.xml
index 27504b049..b1adc3ff4 100755
--- a/bundles/org.opensmarthouse.core.io.transport.mqtt/pom.xml
+++ b/bundles/org.opensmarthouse.core.io.transport.mqtt/pom.xml
@@ -16,7 +16,6 @@
org.opensmarthouse.core.bundlesorg.opensmarthouse.core.config
- ${project.version}com.hivemq
@@ -30,7 +29,6 @@
org.opensmarthouse.core.bundlesorg.opensmarthouse.core.test
- ${project.version}test
diff --git a/bundles/org.opensmarthouse.core.item.auth/pom.xml b/bundles/org.opensmarthouse.core.item.auth/pom.xml
new file mode 100755
index 000000000..f5a1b0015
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.item.auth/pom.xml
@@ -0,0 +1,26 @@
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.item.auth
+
+ OpenSmartHouse Core | Bundles | Item | Auth
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.item
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.item.auth/src/main/java/org/openhab/core/item/auth/internal/ItemPermissionEvaluator.java b/bundles/org.opensmarthouse.core.item.auth/src/main/java/org/openhab/core/item/auth/internal/ItemPermissionEvaluator.java
new file mode 100644
index 000000000..e64742ca1
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.item.auth/src/main/java/org/openhab/core/item/auth/internal/ItemPermissionEvaluator.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2019-2020 Contributors to the OpenSmartHouse project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.item.auth.internal;
+
+import java.util.Set;
+import org.openhab.core.auth.Authentication;
+import org.openhab.core.auth.Permission;
+import org.openhab.core.auth.PermissionEvaluator;
+import org.openhab.core.auth.Permissions;
+import org.openhab.core.auth.Role;
+import org.openhab.core.items.Item;
+import org.osgi.service.component.annotations.Component;
+
+@Component
+public class ItemPermissionEvaluator implements PermissionEvaluator {
+
+ @Override
+ public boolean supports(Class> type, Permission permission) {
+ return Item.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public boolean hasPermission(Permission permission, Authentication authentication, Item value) {
+ if (authentication.getRoles().contains(Role.ADMIN)) {
+ return true;
+ }
+
+ Set permissions = authentication.getPermissions();
+ String item = value.getName();
+ return evaluate(permission, permissions, item);
+ }
+
+ @Override
+ public boolean hasPermission(Permission permission, Authentication authentication, Class type, String item) {
+ if (authentication.getRoles().contains(Role.ADMIN)) {
+ return true;
+ }
+
+ Set permissions = authentication.getPermissions();
+ return evaluate(permission, permissions, item);
+ }
+
+ protected boolean evaluate(Permission permission, Set permissions, String item) {
+ if (permissions == null) {
+ return false;
+ }
+
+ return isPermitted(permissions, permission, item) ||
+ isPermitted(permissions, permission, "*") ||
+ isPermitted(permissions, Permissions.ALL, item);
+ }
+
+ private boolean isPermitted(Set permissions, Permission permission, String item) {
+ return permissions.contains(permission.getCode() + ":item:" + item);
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java
index c55f9b2f8..4964df99a 100755
--- a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java
+++ b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/GroupFunctionHelper.java
@@ -30,11 +30,13 @@
import org.openhab.core.internal.items.function.dimensional.Avg;
import org.openhab.core.internal.items.function.dimensional.Max;
import org.openhab.core.internal.items.function.dimensional.Min;
+import org.openhab.core.internal.items.function.dimensional.Sub;
import org.openhab.core.internal.items.function.dimensional.Sum;
import org.openhab.core.items.GroupFunction;
import org.openhab.core.items.Item;
import org.openhab.core.items.dto.GroupFunctionDTO;
import org.openhab.core.library.items.NumberItem;
+import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
import org.openhab.core.types.TypeParser;
@@ -103,7 +105,12 @@ private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, @N
case "AVG":
return new Avg(dimension);
case "SUM":
- return new Sum(dimension);
+ if (function.params != null && function.params.length == 1) {
+ return new Sum(dimension, OnOffType.from(function.params[0]));
+ }
+ return new Sum(dimension, OnOffType.ON);
+ case "SUB":
+ return new Sub(dimension);
case "MIN":
return new Min(dimension);
case "MAX":
@@ -161,7 +168,12 @@ private GroupFunction createDefaultGroupFunction(GroupFunctionDTO function, @Nul
case "AVG":
return new org.openhab.core.internal.items.function.Avg();
case "SUM":
- return new org.openhab.core.internal.items.function.Sum();
+ if (function.params != null && function.params.length == 1) {
+ return new org.openhab.core.internal.items.function.Sum(OnOffType.from(function.params[0]));
+ }
+ return new org.openhab.core.internal.items.function.Sum(OnOffType.ON);
+ case "SUB":
+ return new org.openhab.core.internal.items.function.Sub();
case "MIN":
return new org.openhab.core.internal.items.function.Min();
case "MAX":
diff --git a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/Sub.java b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/Sub.java
new file mode 100644
index 000000000..cc31eacf4
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/Sub.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.internal.items.function;
+
+import java.math.BigDecimal;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.items.GroupFunction;
+import org.openhab.core.items.Item;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * This subtracts values between members of the group.
+ *
+ * In this regard this group kind is dependant on the order its members are defined!
+ *
+ * @author Łukasz Dywicki - Initial contribution
+ */
+@NonNullByDefault
+public class Sub implements GroupFunction {
+
+ public Sub() {
+ }
+
+ @Override
+ public State calculate(@Nullable Set items) {
+ if (items == null || items.isEmpty()) {
+ return UnDefType.UNDEF;
+ }
+
+ BigDecimal sub = null;
+ for (Item item : items) {
+ DecimalType itemState = item.getStateAs(DecimalType.class);
+ if (itemState == null) {
+ // we got an empty value which means that we can't reliably calculate end value
+ // we break the loop here to avoid invalid results
+ return UnDefType.UNDEF;
+ }
+
+ if (sub == null) {
+ sub = itemState.toBigDecimal();
+ } else {
+ sub = sub.subtract(itemState.toBigDecimal());
+ }
+ }
+ return new DecimalType(sub);
+ }
+
+ @Override
+ public @Nullable T getStateAs(@Nullable Set items, Class stateClass) {
+ State state = calculate(items);
+ if (stateClass.isInstance(state)) {
+ return stateClass.cast(state);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public State[] getParameters() {
+ return new State[0];
+ }
+
+ @Override
+ public String toString() {
+ return "SUB";
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/Sum.java b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/Sum.java
index 411bc9789..e56c2e189 100644
--- a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/Sum.java
+++ b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/Sum.java
@@ -19,6 +19,8 @@
import org.openhab.core.items.GroupFunction;
import org.openhab.core.items.Item;
import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.UnDefType;
import org.openhab.core.types.State;
/**
@@ -31,7 +33,10 @@
@NonNullByDefault
public class Sum implements GroupFunction {
- public Sum() {
+ private final OnOffType allMembers;
+
+ public Sum(OnOffType allMembers) {
+ this.allMembers = allMembers;
}
@Override
@@ -42,6 +47,10 @@ public State calculate(@Nullable Set items) {
DecimalType itemState = item.getStateAs(DecimalType.class);
if (itemState != null) {
sum = sum.add(itemState.toBigDecimal());
+ } else {
+ if (OnOffType.ON == allMembers) {
+ return UnDefType.UNDEF;
+ }
}
}
}
@@ -60,6 +69,12 @@ public State calculate(@Nullable Set items) {
@Override
public State[] getParameters() {
- return new State[0];
+ return new State[] { allMembers};
}
+
+ @Override
+ public String toString() {
+ return "SUM(" + allMembers + ")";
+ }
+
}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/dimensional/Sub.java b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/dimensional/Sub.java
new file mode 100644
index 000000000..d69959f54
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/dimensional/Sub.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.internal.items.function.dimensional;
+
+import java.util.Set;
+import javax.measure.Quantity;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.items.Item;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * Subtract value between members {@link QuantityType}.
+ *
+ * @author Łukas Dywicki - Initial contribution
+ */
+@NonNullByDefault
+public class Sub extends DimensionalGroupFunction {
+
+ public Sub(Class extends Quantity>> dimension) {
+ super(dimension);
+ }
+
+ @Override
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public State calculate(@Nullable Set items) {
+ if (items == null || items.isEmpty()) {
+ return UnDefType.UNDEF;
+ }
+
+ QuantityType> sub = null;
+ for (Item item : items) {
+ if (isSameDimension(item)) {
+ QuantityType itemState = item.getStateAs(QuantityType.class);
+ if (itemState == null) {
+ // we got an empty value which means that we can't reliably calculate end value
+ // we break the loop here to avoid invalid results
+ return UnDefType.UNDEF;
+ }
+
+ if (sub == null) {
+ sub = itemState; // initialise the sub from the first item
+ } else {
+ if (sub.getUnit().isCompatible(itemState.getUnit())) {
+ sub = sub.subtract(itemState);
+ } else {
+ // we got an incompatible value, we must break the loop here to avoid invalid results
+ return UnDefType.UNDEF;
+ }
+ }
+ }
+ }
+
+ return sub;
+ }
+
+ @Override
+ public String toString() {
+ return "SUB()";
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/dimensional/Sum.java b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/dimensional/Sum.java
index 4ddae66ea..0c9d46051 100644
--- a/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/dimensional/Sum.java
+++ b/bundles/org.opensmarthouse.core.item.core/src/main/java/org/openhab/core/internal/items/function/dimensional/Sum.java
@@ -17,6 +17,7 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.items.Item;
+import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
@@ -29,8 +30,11 @@
@NonNullByDefault
public class Sum extends DimensionalGroupFunction {
- public Sum(Class extends Quantity>> dimension) {
+ private final OnOffType allMembers;
+
+ public Sum(Class extends Quantity>> dimension, OnOffType allMembers) {
super(dimension);
+ this.allMembers = allMembers;
}
@Override
@@ -50,6 +54,10 @@ public State calculate(@Nullable Set items) {
} else if (sum.getUnit().isCompatible(itemState.getUnit())) {
sum = sum.add(itemState);
}
+ } else {
+ if (allMembers == OnOffType.ON) {
+ return UnDefType.UNDEF;
+ }
}
}
}
@@ -57,4 +65,13 @@ public State calculate(@Nullable Set items) {
return sum != null ? sum : UnDefType.UNDEF;
}
+ @Override
+ public State[] getParameters() {
+ return new State[] {allMembers};
+ }
+
+ @Override
+ public String toString() {
+ return "SUM(" + allMembers + ")";
+ }
}
\ No newline at end of file
diff --git a/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/ArithmeticGroupFunctionTest.java b/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/ArithmeticGroupFunctionTest.java
index eeb480220..1b2c2d4bd 100755
--- a/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/ArithmeticGroupFunctionTest.java
+++ b/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/ArithmeticGroupFunctionTest.java
@@ -225,7 +225,7 @@ public void testSumFunction() {
items.add(new TestItem("TestItem4", UnDefType.UNDEF));
items.add(new TestItem("TestItem5", new DecimalType("122.41")));
- function = new Sum();
+ function = new Sum(OnOffType.OFF);
State state = function.calculate(items);
assertEquals(new DecimalType("234.95"), state);
diff --git a/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/QuantityTypeArithmeticGroupFunctionTest.java b/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/QuantityTypeArithmeticGroupFunctionTest.java
index f29ef8f06..499186919 100755
--- a/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/QuantityTypeArithmeticGroupFunctionTest.java
+++ b/bundles/org.opensmarthouse.core.item.core/src/test/java/org/openhab/core/internal/items/function/QuantityTypeArithmeticGroupFunctionTest.java
@@ -38,6 +38,7 @@
import org.openhab.core.items.Item;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.library.items.NumberItem;
+import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
@@ -71,7 +72,7 @@ public void testSumFunctionQuantityType() {
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("122.41 °C")));
- function = new Sum(Temperature.class);
+ function = new Sum(Temperature.class, OnOffType.OFF);
State state = function.calculate(items);
assertEquals(new QuantityType<>("234.95 °C"), state);
@@ -85,7 +86,7 @@ public void testSumFunctionQuantityTypeDifferentUnits() {
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K")));
- function = new Sum(Temperature.class);
+ function = new Sum(Temperature.class, OnOffType.OFF);
State state = function.calculate(items);
assertEquals(new QuantityType<>("234.95 °C"), state);
@@ -98,7 +99,7 @@ public void testSumFunctionQuantityTypeIncompatibleUnits() {
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
- function = new Sum(Temperature.class);
+ function = new Sum(Temperature.class, OnOffType.OFF);
State state = function.calculate(items);
assertEquals(new QuantityType<>("23.54 °C"), state);
@@ -244,7 +245,7 @@ public void testSumFunctionQuantityTypeWithGroups() {
items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("5 W")));
items.add(createGroupItem("TestGroup1", Power.class, new QuantityType<>("5 W")));
- function = new Sum(Power.class);
+ function = new Sum(Power.class, OnOffType.OFF);
State state = function.calculate(items);
assertEquals(new QuantityType<>("10 W"), state);
diff --git a/bundles/org.opensmarthouse.core.karaf.jaas/NOTICE b/bundles/org.opensmarthouse.core.karaf.jaas/NOTICE
new file mode 100755
index 000000000..3e53bd427
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.karaf.jaas/NOTICE
@@ -0,0 +1,18 @@
+This content is produced and maintained by the OpenSmartHouse project.
+
+* Project home: https://opensmarthouse.org
+
+Elements of this bundle are copyright to the following -:
+
+* The Eclipse Foundation, having come from the Eclipse SmartHome project
+* The openHAB project
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/opensmarthouse/opensmarthouse-core
diff --git a/bundles/org.opensmarthouse.core.karaf.jaas/pom.xml b/bundles/org.opensmarthouse.core.karaf.jaas/pom.xml
new file mode 100755
index 000000000..c4c66e6e0
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.karaf.jaas/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+ 4.0.0
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.reactor.bundles
+ 0.9.3-SNAPSHOT
+
+
+ org.opensmarthouse.core.karaf.jaas
+
+ OpenSmartHouse Core | Bundles | Karaf JAAS Integration
+
+
+
+ org.opensmarthouse.core.bundles
+ org.opensmarthouse.core.auth.local
+
+
+
+ org.apache.karaf.jaas
+ org.apache.karaf.jaas.modules
+ ${karaf.compile.version}
+ provided
+
+
+ org.apache.karaf.jaas
+ org.apache.karaf.jaas.boot
+ ${karaf.compile.version}
+ provided
+
+
+
+
diff --git a/bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserBackingEngine.java b/bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserBackingEngine.java
similarity index 95%
rename from bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserBackingEngine.java
rename to bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserBackingEngine.java
index be5a78da1..dbd54f9a1 100755
--- a/bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserBackingEngine.java
+++ b/bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserBackingEngine.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.karaf.internal.jaas;
+package org.openhab.core.karaf.jaas.internal;
import java.security.Principal;
import java.util.Collections;
@@ -24,10 +24,10 @@
import org.apache.karaf.jaas.boot.principal.RolePrincipal;
import org.apache.karaf.jaas.boot.principal.UserPrincipal;
import org.apache.karaf.jaas.modules.BackingEngine;
-import org.openhab.core.auth.ManagedUser;
+import org.openhab.core.auth.local.ManagedUser;
import org.openhab.core.auth.Role;
-import org.openhab.core.auth.User;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.User;
+import org.openhab.core.auth.local.UserRegistry;
/**
* A Karaf backing engine for the {@link UserRegistry}
diff --git a/bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserBackingEngineFactory.java b/bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserBackingEngineFactory.java
similarity index 93%
rename from bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserBackingEngineFactory.java
rename to bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserBackingEngineFactory.java
index 69c7d659d..7224764ed 100755
--- a/bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserBackingEngineFactory.java
+++ b/bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserBackingEngineFactory.java
@@ -10,13 +10,13 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.karaf.internal.jaas;
+package org.openhab.core.karaf.jaas.internal;
import java.util.Map;
import org.apache.karaf.jaas.modules.BackingEngine;
import org.apache.karaf.jaas.modules.BackingEngineFactory;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.UserRegistry;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
diff --git a/bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserRealm.java b/bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserRealm.java
similarity index 91%
rename from bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserRealm.java
rename to bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserRealm.java
index 6552eb910..202e25f67 100755
--- a/bundles/org.opensmarthouse.core.karaf/src/main/java/org/openhab/core/karaf/internal/jaas/ManagedUserRealm.java
+++ b/bundles/org.opensmarthouse.core.karaf.jaas/src/main/java/org/openhab/core/karaf/jaas/internal/ManagedUserRealm.java
@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
-package org.openhab.core.karaf.internal.jaas;
+package org.openhab.core.karaf.jaas.internal;
import java.util.HashMap;
import java.util.Map;
@@ -20,8 +20,7 @@
import org.apache.karaf.jaas.boot.ProxyLoginModule;
import org.apache.karaf.jaas.config.JaasRealm;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.openhab.core.auth.UserRegistry;
+import org.openhab.core.auth.local.UserRegistry;
import org.osgi.service.component.annotations.Component;
/**
@@ -30,7 +29,6 @@
* @author Yannick Schaus - initial contribution
*/
@Component(service = JaasRealm.class)
-@Service
public class ManagedUserRealm implements JaasRealm {
public static final String REALM_NAME = "openhab";
diff --git a/bundles/org.opensmarthouse.core.karaf/pom.xml b/bundles/org.opensmarthouse.core.karaf/pom.xml
index 2e52ee457..1c4f105b2 100755
--- a/bundles/org.opensmarthouse.core.karaf/pom.xml
+++ b/bundles/org.opensmarthouse.core.karaf/pom.xml
@@ -49,12 +49,6 @@
${karaf.compile.version}provided
-
- org.apache.karaf.jaas
- org.apache.karaf.jaas.modules
- ${karaf.compile.version}
- provided
-
diff --git a/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java b/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java
index 26e3cf586..aad266728 100755
--- a/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java
+++ b/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java
@@ -60,7 +60,8 @@ public List getItems() {
@Override
public String toString() {
return String.format("%s [items=%s, alias=%s, strategies=%s, filters=%s]", getClass().getSimpleName(),
- Arrays.toString(items.toArray()), alias, Arrays.toString(strategies.toArray()),
- Arrays.toString(filters.toArray()));
+ Arrays.toString(items.toArray()), alias,
+ strategies == null ? "" : Arrays.toString(strategies.toArray()),
+ filters == null ? "" : Arrays.toString(filters.toArray()));
}
}
diff --git a/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java b/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java
index 5f35e64d3..c9532b976 100755
--- a/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java
+++ b/bundles/org.opensmarthouse.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java
@@ -25,6 +25,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.function.Predicate;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.NamedThreadFactory;
@@ -38,6 +39,7 @@
import org.openhab.core.items.StateChangeListener;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.HistoricItem;
+import org.openhab.core.persistence.PersistenceFilter;
import org.openhab.core.persistence.PersistenceItemConfiguration;
import org.openhab.core.persistence.PersistenceManager;
import org.openhab.core.persistence.PersistenceService;
@@ -149,6 +151,7 @@ private void handleStateEvent(Item item, boolean onlyChanges) {
if (hasStrategy(config, itemConfig, onlyChanges ? PersistenceStrategy.Globals.CHANGE
: PersistenceStrategy.Globals.UPDATE)) {
if (appliesToItem(itemConfig, item)) {
+ logger.trace("Persistence config {} causes storing of item {} state {}", itemConfig.getAlias(), item.getName(), item.getState());
persistenceServices.get(serviceName).store(item, itemConfig.getAlias());
}
}
@@ -189,6 +192,17 @@ private boolean hasStrategy(PersistenceServiceConfiguration config, PersistenceI
*/
private boolean appliesToItem(PersistenceItemConfiguration config, Item item) {
for (PersistenceConfig itemCfg : config.getItems()) {
+ // handling of items excluded from certain configuration, a filter works as an removal criteria!
+ if (config.getFilters() != null) {
+ for (PersistenceFilter filter : config.getFilters()) {
+ if (filter instanceof Predicate) {
+ logger.trace("Checking filter {}", filter);
+ if (((Predicate) filter).test(item)) {
+ return false;
+ }
+ }
+ }
+ }
if (itemCfg instanceof PersistenceAllConfig) {
return true;
}
diff --git a/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderFactoryImpl.java b/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderFactoryImpl.java
index 4d6ded5ce..4ae5ba80e 100644
--- a/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderFactoryImpl.java
+++ b/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderFactoryImpl.java
@@ -31,12 +31,12 @@ public final class ProfileTypeBuilderFactoryImpl implements ProfileTypeBuilderFa
@Override
public ProfileTypeBuilder newState(ProfileTypeUID profileTypeUID, String label) {
- return new ProfileTypeBuilderImpl.StateProfileTypeBuilder(profileTypeUID, label);
+ return ProfileTypeBuilder.newState(profileTypeUID, label);
}
@Override
public ProfileTypeBuilder newTrigger(ProfileTypeUID profileTypeUID, String label) {
- return new ProfileTypeBuilderImpl.TriggerProfileTypeBuilder(profileTypeUID, label);
+ return ProfileTypeBuilder.newTrigger(profileTypeUID, label);
}
}
diff --git a/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderImpl.java b/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderImpl.java
index ffadce4ac..638dd090d 100644
--- a/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderImpl.java
+++ b/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/internal/profiles/ProfileTypeBuilderImpl.java
@@ -18,10 +18,7 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.profiles.ProfileType;
-import org.openhab.core.thing.profiles.ProfileTypeBuilder;
-import org.openhab.core.thing.profiles.ProfileTypeBuilderFactory;
import org.openhab.core.thing.profiles.ProfileTypeUID;
-import org.openhab.core.thing.profiles.StateProfile;
import org.openhab.core.thing.profiles.StateProfileType;
import org.openhab.core.thing.profiles.TriggerProfileType;
import org.openhab.core.thing.type.ChannelTypeUID;
@@ -36,7 +33,7 @@
* @param the concrete {@link ProfileType} sub-interface.
*/
@NonNullByDefault
-public abstract class ProfileTypeBuilderImpl implements ProfileTypeBuilder {
+public abstract class ProfileTypeBuilderImpl {
protected final ProfileTypeUID profileTypeUID;
protected final Collection supportedItemTypes = new HashSet<>();
@@ -49,38 +46,32 @@ public abstract class ProfileTypeBuilderImpl implements P
this.label = label;
}
- @Override
- public ProfileTypeBuilder withSupportedItemTypes(String... itemType) {
+ public ProfileTypeBuilderImpl withSupportedItemTypes(String... itemType) {
supportedItemTypes.addAll(Arrays.asList(itemType));
return this;
}
- @Override
- public ProfileTypeBuilder withSupportedItemTypes(Collection itemTypes) {
+ public ProfileTypeBuilderImpl withSupportedItemTypes(Collection itemTypes) {
supportedItemTypes.addAll(itemTypes);
return this;
}
- @Override
- public ProfileTypeBuilder withSupportedChannelTypeUIDs(ChannelTypeUID... channelTypeUIDs) {
+ public ProfileTypeBuilderImpl withSupportedChannelTypeUIDs(ChannelTypeUID... channelTypeUIDs) {
supportedChannelTypeUIDs.addAll(Arrays.asList(channelTypeUIDs));
return this;
}
- @Override
- public ProfileTypeBuilder withSupportedChannelTypeUIDs(Collection channelTypeUIDs) {
+ public ProfileTypeBuilderImpl withSupportedChannelTypeUIDs(Collection channelTypeUIDs) {
supportedChannelTypeUIDs.addAll(channelTypeUIDs);
return this;
}
- @Override
- public ProfileTypeBuilder withSupportedItemTypesOfChannel(String... supportedItemTypesOfChannel) {
+ public ProfileTypeBuilderImpl withSupportedItemTypesOfChannel(String... supportedItemTypesOfChannel) {
this.supportedItemTypesOfChannel.addAll(Arrays.asList(supportedItemTypesOfChannel));
return this;
}
- @Override
- public ProfileTypeBuilder withSupportedItemTypesOfChannel(Collection supportedItemTypesOfChannel) {
+ public ProfileTypeBuilderImpl withSupportedItemTypesOfChannel(Collection supportedItemTypesOfChannel) {
this.supportedItemTypesOfChannel.addAll(supportedItemTypesOfChannel);
return this;
}
@@ -91,7 +82,6 @@ public StateProfileTypeBuilder(ProfileTypeUID profileTypeUID, String label) {
super(profileTypeUID, label);
}
- @Override
public StateProfileType build() {
return new StateProfileTypeImpl(profileTypeUID, label, supportedItemTypes, supportedItemTypesOfChannel);
}
@@ -103,7 +93,6 @@ public TriggerProfileTypeBuilder(ProfileTypeUID profileTypeUID, String label) {
super(profileTypeUID, label);
}
- @Override
public TriggerProfileType build() {
return new TriggerProfileTypeImpl(profileTypeUID, label, supportedItemTypes, supportedChannelTypeUIDs);
}
diff --git a/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/profiles/ProfileTypeBuilder.java b/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/profiles/ProfileTypeBuilder.java
index 3995bab16..841c9de7c 100644
--- a/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/profiles/ProfileTypeBuilder.java
+++ b/bundles/org.opensmarthouse.core.thing/src/main/java/org/openhab/core/thing/profiles/ProfileTypeBuilder.java
@@ -12,8 +12,13 @@
*/
package org.openhab.core.thing.profiles;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.internal.profiles.StateProfileTypeImpl;
+import org.openhab.core.thing.internal.profiles.TriggerProfileTypeImpl;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
@@ -22,12 +27,58 @@
* It can be used to obtain instances instead of implementing any of the interfaces derived from {@link ProfileType}.
*
* @author Simon Kaufmann - Initial contribution
- * @author Łukasz Dywicki - Refactoring to interface, extraction of {@link ProfileTypeBuilderFactory}.
*
* @param the concrete {@link ProfileType} sub-interface.
*/
@NonNullByDefault
-public interface ProfileTypeBuilder {
+public final class ProfileTypeBuilder {
+
+ @FunctionalInterface
+ private interface ProfileTypeFactory {
+ T create(ProfileTypeUID profileTypeUID, String label, Collection supportedItemTypes,
+ Collection supportedItemTypesOfChannel, Collection supportedChannelTypeUIDs);
+ }
+
+ private final ProfileTypeFactory profileTypeFactory;
+ private final ProfileTypeUID profileTypeUID;
+ private final Collection supportedItemTypes = new HashSet<>();
+ private final Collection supportedItemTypesOfChannel = new HashSet<>();
+ private final Collection supportedChannelTypeUIDs = new HashSet<>();
+ private final String label;
+
+ private ProfileTypeBuilder(ProfileTypeUID profileTypeUID, String label, ProfileTypeFactory profileTypeFactory) {
+ this.profileTypeFactory = profileTypeFactory;
+ this.profileTypeUID = profileTypeUID;
+ this.label = label;
+ }
+
+ /**
+ * Obtain a new builder for a {@link StateProfileType} instance.
+ *
+ * @param profileTypeUID the {@link ProfileTypeUID}
+ * @param label a human-readable label
+ * @return the new builder instance
+ */
+ public static ProfileTypeBuilder newState(ProfileTypeUID profileTypeUID, String label) {
+ return new ProfileTypeBuilder<>(profileTypeUID, label,
+ (leProfileTypeUID, leLabel, leSupportedItemTypes, leSupportedItemTypesOfChannel,
+ leSupportedChannelTypeUIDs) -> new StateProfileTypeImpl(leProfileTypeUID, leLabel,
+ leSupportedItemTypes, leSupportedItemTypesOfChannel));
+ }
+
+ /**
+ * Obtain a new builder for a {@link TriggerProfileType} instance.
+ *
+ * @param profileTypeUID the {@link ProfileTypeUID}
+ * @param label a human-readable label
+ * @return the new builder instance
+ */
+ public static ProfileTypeBuilder newTrigger(ProfileTypeUID profileTypeUID, String label) {
+ return new ProfileTypeBuilder<>(profileTypeUID, label,
+ (leProfileTypeUID, leLabel, leSupportedItemTypes, leSupportedItemTypesOfChannel,
+ leSupportedChannelTypeUIDs) -> new TriggerProfileTypeImpl(leProfileTypeUID, leLabel,
+ leSupportedItemTypes, leSupportedChannelTypeUIDs));
+ }
/**
* Declare that the given item type(s) are supported by a profile of this type.
@@ -35,15 +86,21 @@ public interface ProfileTypeBuilder {
* @param itemType
* @return the builder itself
*/
- ProfileTypeBuilder withSupportedItemTypes(String... itemType);
+ public ProfileTypeBuilder withSupportedItemTypes(String... itemType) {
+ supportedItemTypes.addAll(Arrays.asList(itemType));
+ return this;
+ }
/**
* Declare that the given item type(s) are supported by a profile of this type.
*
- * @param itemTypes
+ * @param itemType
* @return the builder itself
*/
- ProfileTypeBuilder withSupportedItemTypes(Collection itemTypes);
+ public ProfileTypeBuilder withSupportedItemTypes(Collection itemTypes) {
+ supportedItemTypes.addAll(itemTypes);
+ return this;
+ }
/**
* Declare that the given channel type(s) are supported by a profile of this type.
@@ -51,7 +108,10 @@ public interface ProfileTypeBuilder {
* @param channelTypeUIDs
* @return the builder itself
*/
- ProfileTypeBuilder withSupportedChannelTypeUIDs(ChannelTypeUID... channelTypeUIDs);
+ public ProfileTypeBuilder withSupportedChannelTypeUIDs(ChannelTypeUID... channelTypeUIDs) {
+ supportedChannelTypeUIDs.addAll(Arrays.asList(channelTypeUIDs));
+ return this;
+ }
/**
* Declare that the given channel type(s) are supported by a profile of this type.
@@ -59,7 +119,10 @@ public interface ProfileTypeBuilder {
* @param channelTypeUIDs
* @return the builder itself
*/
- ProfileTypeBuilder withSupportedChannelTypeUIDs(Collection channelTypeUIDs);
+ public ProfileTypeBuilder withSupportedChannelTypeUIDs(Collection channelTypeUIDs) {
+ supportedChannelTypeUIDs.addAll(channelTypeUIDs);
+ return this;
+ }
/**
* Declare that channels with these item type(s) are compatible with profiles of this type.
@@ -67,7 +130,10 @@ public interface ProfileTypeBuilder {
* @param supportedItemTypesOfChannel item types on channel to which this profile type is compatible with
* @return the builder itself
*/
- ProfileTypeBuilder withSupportedItemTypesOfChannel(String... supportedItemTypesOfChannel);
+ public ProfileTypeBuilder withSupportedItemTypesOfChannel(String... supportedItemTypesOfChannel) {
+ this.supportedItemTypesOfChannel.addAll(Arrays.asList(supportedItemTypesOfChannel));
+ return this;
+ }
/**
* Declare that channels with these item type(s) are compatible with profiles of this type.
@@ -75,13 +141,19 @@ public interface ProfileTypeBuilder {
* @param supportedItemTypesOfChannel item types on channel to which this profile type is compatible with
* @return the builder itself
*/
- ProfileTypeBuilder withSupportedItemTypesOfChannel(Collection supportedItemTypesOfChannel);
+ public ProfileTypeBuilder withSupportedItemTypesOfChannel(Collection supportedItemTypesOfChannel) {
+ this.supportedItemTypesOfChannel.addAll(supportedItemTypesOfChannel);
+ return this;
+ }
/**
* Create a profile type instance with the previously given parameters.
*
* @return the according subtype of {@link ProfileType}
*/
- T build();
+ public T build() {
+ return profileTypeFactory.create(profileTypeUID, label, supportedItemTypes, supportedItemTypesOfChannel,
+ supportedChannelTypeUIDs);
+ }
}
diff --git a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/components/UIProvider.java b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/components/UIProvider.java
new file mode 100755
index 000000000..0d55fbbc0
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/components/UIProvider.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.ui.components;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.common.registry.Provider;
+import org.openhab.core.common.registry.Registry;
+
+@NonNullByDefault
+public interface UIProvider extends Provider {
+
+ String getNamespace();
+
+}
diff --git a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentProvider.java b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentProvider.java
index 297ef9e8b..892a03787 100644
--- a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentProvider.java
+++ b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentProvider.java
@@ -23,6 +23,7 @@
import org.openhab.core.storage.Storage;
import org.openhab.core.storage.StorageService;
import org.openhab.core.ui.components.RootUIComponent;
+import org.openhab.core.ui.components.UIProvider;
/**
* A namespace-specific {@link ManagedProvider} for UI components.
@@ -31,7 +32,7 @@
*/
@NonNullByDefault
public class UIComponentProvider extends AbstractProvider
- implements ManagedProvider {
+ implements ManagedProvider, UIProvider {
private String namespace;
private volatile Storage storage;
@@ -48,6 +49,11 @@ public UIComponentProvider(String namespace, StorageService storageService) {
this.getClass().getClassLoader());
}
+ @Override
+ public String getNamespace() {
+ return namespace;
+ }
+
@Override
public Collection getAll() {
List components = new ArrayList<>();
diff --git a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryFactoryImpl.java b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryFactoryImpl.java
index 66f49f5a2..62a125943 100644
--- a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryFactoryImpl.java
+++ b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryFactoryImpl.java
@@ -13,33 +13,79 @@
package org.openhab.core.ui.internal.components;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.openhab.core.common.registry.ManagedProvider;
+import org.openhab.core.common.registry.Provider;
import org.openhab.core.storage.StorageService;
+import org.openhab.core.ui.components.RootUIComponent;
import org.openhab.core.ui.components.UIComponentRegistryFactory;
+import org.openhab.core.ui.components.UIProvider;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
/**
- * Implementation for a {@link UIComponentRegistryFactory} using a {@link StorageService} and a
- * {@link UIComponentProvider}.
+ * Implementation for a {@link UIComponentRegistryFactory} using a set of {@link UIComponentProvider}.
*
* @author Yannick Schaus - Initial contribution
+ * @author Łukasz Dywicki - Removed explicit dependency on storage providers.
*/
@Component(service = UIComponentRegistryFactory.class, immediate = true)
public class UIComponentRegistryFactoryImpl implements UIComponentRegistryFactory {
- Map registries = new HashMap<>();
-
- @Reference
- StorageService storageService;
+ Map registries = new ConcurrentHashMap<>();
+ Map> providers = new ConcurrentHashMap<>();
@Override
public UIComponentRegistryImpl getRegistry(String namespace) {
UIComponentRegistryImpl registry = registries.get(namespace);
if (registry == null) {
- registry = new UIComponentRegistryImpl(namespace, storageService);
+ Set namespaceProviders = this.providers.get(namespace);
+ ManagedProvider<@NonNull RootUIComponent, @NonNull String> managedProvider = null;
+ if (namespaceProviders != null) {
+ for (UIProvider provider : namespaceProviders) {
+ if (provider instanceof ManagedProvider) {
+ managedProvider = (ManagedProvider<@NonNull RootUIComponent, @NonNull String>) provider;
+ break;
+ }
+ }
+ }
+ registry = new UIComponentRegistryImpl(namespace, managedProvider, namespaceProviders);
registries.put(namespace, registry);
}
return registry;
}
+
+ @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
+ void addProvider(UIProvider provider) {
+ if (registries.containsKey(provider.getNamespace())) {
+ registries.get(provider.getNamespace()).addProvider(provider);
+ }
+ registerProvider(provider);
+ }
+
+ void removeProvider(UIProvider provider) {
+ if (registries.containsKey(provider.getNamespace())) {
+ registries.get(provider.getNamespace()).removeProvider(provider);
+ }
+ deregisterProvider(provider);
+ }
+
+ private void registerProvider(UIProvider provider) {
+ if (!providers.containsKey(provider.getNamespace())) {
+ providers.put(provider.getNamespace(), new HashSet<>());
+ }
+ providers.get(provider.getNamespace()).add(provider);
+ }
+
+ private void deregisterProvider(UIProvider provider) {
+ if (providers.containsKey(provider.getNamespace())) {
+ providers.get(provider.getNamespace()).remove(provider);
+ }
+ }
}
diff --git a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryImpl.java b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryImpl.java
index 5d2c9ad4c..dfd1d7a25 100644
--- a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryImpl.java
+++ b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentRegistryImpl.java
@@ -12,11 +12,16 @@
*/
package org.openhab.core.ui.internal.components;
+import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.registry.AbstractRegistry;
+import org.openhab.core.common.registry.ManagedProvider;
+import org.openhab.core.common.registry.Provider;
import org.openhab.core.storage.StorageService;
import org.openhab.core.ui.components.RootUIComponent;
import org.openhab.core.ui.components.UIComponentRegistry;
+import org.openhab.core.ui.components.UIProvider;
/**
* Implementation of a {@link UIComponentRegistry} using a {@link UIComponentProvider}.
@@ -29,20 +34,32 @@ public class UIComponentRegistryImpl extends AbstractRegistry managedProvider, @Nullable Set providers) {
super(null);
this.namespace = namespace;
- this.storageService = storageService;
- UIComponentProvider provider = new UIComponentProvider(namespace, storageService);
- addProvider(provider);
- setManagedProvider(provider);
+
+ if (managedProvider != null) {
+ setManagedProvider(managedProvider);
+ }
+ if (providers != null && !providers.isEmpty()) {
+ for (Provider provider : providers) {
+ addProvider(provider);
+ }
+ }
+ }
+
+ public void addProvider(Provider provider) {
+ super.addProvider(provider);
}
+
+ public void removeProvider(Provider provider) {
+ super.removeProvider(provider);
+ }
+
}
diff --git a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/provider/ManagedPageProvider.java b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/provider/ManagedPageProvider.java
new file mode 100644
index 000000000..8a5c20dec
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/provider/ManagedPageProvider.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.ui.internal.components.provider;
+
+import org.openhab.core.common.registry.ManagedProvider;
+import org.openhab.core.common.registry.Provider;
+import org.openhab.core.storage.StorageService;
+import org.openhab.core.ui.components.UIProvider;
+import org.openhab.core.ui.internal.components.UIComponentProvider;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component(service = {Provider.class, ManagedProvider.class, UIProvider.class})
+public class ManagedPageProvider extends UIComponentProvider {
+
+ @Activate
+ public ManagedPageProvider(@Reference StorageService storageService) {
+ super("ui:page", storageService);
+ }
+
+}
diff --git a/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/provider/ManagedWidgetProvider.java b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/provider/ManagedWidgetProvider.java
new file mode 100644
index 000000000..497712135
--- /dev/null
+++ b/bundles/org.opensmarthouse.core.ui/src/main/java/org/openhab/core/ui/internal/components/provider/ManagedWidgetProvider.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2020 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.ui.internal.components.provider;
+
+import org.openhab.core.common.registry.ManagedProvider;
+import org.openhab.core.common.registry.Provider;
+import org.openhab.core.storage.StorageService;
+import org.openhab.core.ui.components.UIProvider;
+import org.openhab.core.ui.internal.components.UIComponentProvider;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component(service = {Provider.class, ManagedProvider.class, UIProvider.class})
+public class ManagedWidgetProvider extends UIComponentProvider {
+
+ @Activate
+ public ManagedWidgetProvider(@Reference StorageService storageService) {
+ super("ui:widget", storageService);
+ }
+
+}
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 7a0f0244a..ee7e5776e 100755
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -21,9 +21,17 @@
org.opensmarthouse.core.audio.coreorg.opensmarthouse.core.authorg.opensmarthouse.core.auth.core
+ org.opensmarthouse.core.auth.apitoken
+ org.opensmarthouse.core.auth.apitoken.provider
+ org.opensmarthouse.core.auth.cookieorg.opensmarthouse.core.auth.jaas
+ org.opensmarthouse.core.auth.jwt
+ org.opensmarthouse.core.auth.jwt.provider
+ org.opensmarthouse.core.auth.local
+ org.opensmarthouse.core.auth.local.providerorg.opensmarthouse.core.auth.oauth2clientorg.opensmarthouse.core.auth.oauth2client.core
+ org.opensmarthouse.core.auth.passwordorg.opensmarthouse.core.automationorg.opensmarthouse.core.automation.module.mediaorg.opensmarthouse.core.automation.module.script
@@ -55,6 +63,7 @@
org.opensmarthouse.core.i18norg.opensmarthouse.core.i18n.coreorg.opensmarthouse.core.id
+ org.opensmarthouse.core.io.authorg.opensmarthouse.core.io.bin2jsonorg.opensmarthouse.core.io.consoleorg.opensmarthouse.core.io.console.eclipse
@@ -63,13 +72,18 @@
org.opensmarthouse.core.io.console.userorg.opensmarthouse.core.io.httporg.opensmarthouse.core.io.http.auth
+ org.opensmarthouse.core.io.http.auth.apitokenorg.opensmarthouse.core.io.http.auth.basic
+ org.opensmarthouse.core.io.http.auth.cookie
+ org.opensmarthouse.core.io.http.auth.jwt
+ org.opensmarthouse.core.io.http.facadeorg.opensmarthouse.core.io.jetty.certificateorg.opensmarthouse.core.io.monitororg.opensmarthouse.core.io.netorg.opensmarthouse.core.io.restorg.opensmarthouse.core.io.rest.audioorg.opensmarthouse.core.io.rest.auth
+ org.opensmarthouse.core.io.rest.auth.localorg.opensmarthouse.core.io.rest.bindingorg.opensmarthouse.core.io.rest.channelorg.opensmarthouse.core.io.rest.config
@@ -99,8 +113,10 @@
org.opensmarthouse.core.io.transport.serial.rxtx.rfc2217org.opensmarthouse.core.io.transport.upnporg.opensmarthouse.core.item
+ org.opensmarthouse.core.item.authorg.opensmarthouse.core.item.coreorg.opensmarthouse.core.karaf
+ org.opensmarthouse.core.karaf.jaasorg.opensmarthouse.core.library.dimensionorg.opensmarthouse.core.library.itemorg.opensmarthouse.core.library.type
diff --git a/features/karaf/framework/src/main/resources/resources/bin/oh2_dir_layout b/features/karaf/framework/src/main/resources/resources/bin/oh2_dir_layout
index 679620d4c..b685235b9 100644
--- a/features/karaf/framework/src/main/resources/resources/bin/oh2_dir_layout
+++ b/features/karaf/framework/src/main/resources/resources/bin/oh2_dir_layout
@@ -16,11 +16,11 @@ if [ -z ${OSH_RUNTIME} ]; then
fi
if [ -z ${OSH_USERDATA} ]; then
- export OSH_USERDATA="${OSH_HOME}/data"
+ export OSH_USERDATA="${OSH_HOME}/userdata"
fi
if [ -z ${OSH_LOGDIR} ]; then
- export OSH_LOGDIR="${OSH_USERDATA}/log"
+ export OSH_LOGDIR="${OSH_RUNTIME}/log"
fi
if [ -z ${OSH_BACKUPS} ]; then
diff --git a/features/karaf/opensmarthouse-core/src/main/feature/feature.xml b/features/karaf/opensmarthouse-core/src/main/feature/feature.xml
index 7c1158beb..8896656d2 100755
--- a/features/karaf/opensmarthouse-core/src/main/feature/feature.xml
+++ b/features/karaf/opensmarthouse-core/src/main/feature/feature.xml
@@ -251,6 +251,21 @@
mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth/${project.version}
+
+ opensmarthouse-core-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.apitoken/${project.version}
+
+
+
+ opensmarthouse-core-auth-apitoken
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.apitoken/${project.version}
+
+
+
+ opensmarthouse-core-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.cookie/${project.version}
+
+
opensmarthouse-core-authopensmarthouse-core-storage
@@ -259,9 +274,34 @@
opensmarthouse-core-auth
+ opensmarthouse-core-auth-local
+ opensmarthouse-core-auth-passwordmvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.jaas/${project.version}
+
+ opensmarthouse-core-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.jwt/${project.version}
+
+
+
+ opensmarthouse-tp;filter:="(feature=jose4j)"
+ opensmarthouse-tp-jose4j
+ opensmarthouse-core-auth-jwt
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.jwt.provider/${project.version}
+
+
+
+ opensmarthouse-core-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.local/${project.version}
+
+
+
+ opensmarthouse-core-auth-local
+ opensmarthouse-core-auth-password
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.local.provider/${project.version}
+
+
mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.oauth2client/${project.version}
@@ -275,6 +315,17 @@
mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.oauth2client.core/${project.version}
+
+ opensmarthouse-core-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.auth.password/${project.version}
+
+
+
+ opensmarthouse-core-auth
+ opensmarthouse-core-item
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.item.auth/${project.version}
+
+
opensmarthouse-tp-gsonopensmarthouse-core
@@ -445,6 +496,11 @@
mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.console.karaf/${project.version}
+
+ opensmarthouse-core-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.auth/${project.version}
+
+
httpmvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.http/${project.version}
@@ -452,12 +508,28 @@
opensmarthouse-tp-jax-rs
+ opensmarthouse-core-io-auth
+ opensmarthouse-core-io-http-facadeopensmarthouse-core-io-httpopensmarthouse-core-i18nopensmarthouse-core-auth-jaasmvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.http.auth/${project.version}
+
+ opensmarthouse-core-base
+ opensmarthouse-core-auth-apitoken
+ opensmarthouse-core-io-http-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.http.auth.apitoken/${project.version}
+
+
+
+ opensmarthouse-core-base
+ opensmarthouse-core-auth-cookie
+ opensmarthouse-core-io-http-auth
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.http.auth.cookie/${project.version}
+
+
opensmarthouse-core-basemvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.http.auth.basic/${project.version}
@@ -468,13 +540,37 @@
+
+ opensmarthouse-core-base
+ opensmarthouse-core-io-http-auth
+ opensmarthouse-core-auth-jwt
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.http.auth.jwt/${project.version}
+
+
+
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.http.facade/${project.version}
+
+
opensmarthouse-core-auth-core
+ opensmarthouse-core-io-auth
+ opensmarthouse-core-io-http-facadeopensmarthouse-core-io-restopensmarthouse-core-configmvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.rest.auth/${project.version}
+
+
+
+ opensmarthouse-tp-jax-rsopensmarthouse-tp;filter:="(feature=jose4j)"opensmarthouse-tp-jose4j
+
+ opensmarthouse-core-io-http-auth-jwt
+ opensmarthouse-core-io-rest-auth
+ opensmarthouse-core-auth-cookie
+ opensmarthouse-core-auth-jwt
+ opensmarthouse-core-auth-local-provider
+ mvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.rest.auth.local/${project.version}
@@ -496,6 +592,7 @@
opensmarthouse-core-library-itemopensmarthouse-core-io-rest
+ opensmarthouse-core-authopensmarthouse-core-registryopensmarthouse-core-configopensmarthouse-core-thing
@@ -506,6 +603,9 @@
opensmarthouse-tp-javax-injectopensmarthouse-core-io-rest
+ opensmarthouse-core-io-rest-auth
+ opensmarthouse-core-io-http-auth-cookie
+ opensmarthouse-core-authopensmarthouse-core-itemopensmarthouse-core-transformmvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.rest.sse/${project.version}
@@ -824,11 +924,17 @@
opensmarthouse-tp-commons-netopensmarthouse-core-baseopensmarthouse-core-auth-jaas
+ opensmarthouse-core-auth-jwt
+ opensmarthouse-core-auth-jwt-provider
+ opensmarthouse-core-auth-password
+ opensmarthouse-core-auth-local
+ opensmarthouse-core-auth-local-provideropensmarthouse-core-ephemerisopensmarthouse-core-io-console-karafopensmarthouse-core-io-httpopensmarthouse-core-io-http-authopensmarthouse-core-io-rest-auth
+ opensmarthouse-core-io-rest-auth-localopensmarthouse-core-io-rest-audioopensmarthouse-core-io-rest-extensionopensmarthouse-core-io-rest-sitemap
@@ -1000,6 +1106,7 @@
opensmarthouse-core-io-rest
+ opensmarthouse-core-authopensmarthouse-core-persistencemvn:org.opensmarthouse.core.bundles/org.opensmarthouse.core.io.rest.persistence/${project.version}
diff --git a/pom.xml b/pom.xml
index 5a820054d..b4b1365fe 100755
--- a/pom.xml
+++ b/pom.xml
@@ -427,6 +427,11 @@ Import-Package: \\
build-helper-maven-plugin${mojohaus-buildhelper-plugin.version}
+
+ org.apache.karaf.tooling
+ karaf-maven-plugin
+ ${karaf.tooling.version}
+ com.mycila
@@ -527,19 +532,6 @@ Import-Package: \\
-
-
- org.commonjava.maven.plugins
- directory-maven-plugin
- [0.3.1,)
-
- highest-basedir
-
-
-
-
-
-
@@ -550,10 +542,10 @@ Import-Package: \\
sat-plugin${sat.version}
- ${basedirRoot}/tools/static-code-analysis/pmd/suppressions.properties
- ${basedirRoot}/tools/static-code-analysis/checkstyle/ruleset.properties
- ${basedirRoot}/tools/static-code-analysis/checkstyle/suppressions.xml
- ${basedirRoot}/tools/static-code-analysis/spotbugs/suppressions.xml
+ ${maven.multiModuleProjectDirectory}/tools/static-code-analysis/pmd/suppressions.properties
+ ${maven.multiModuleProjectDirectory}/tools/static-code-analysis/checkstyle/ruleset.properties
+ ${maven.multiModuleProjectDirectory}/tools/static-code-analysis/checkstyle/suppressions.xml
+ ${maven.multiModuleProjectDirectory}/tools/static-code-analysis/spotbugs/suppressions.xml
@@ -663,23 +655,6 @@ Import-Package: \\
-
- org.commonjava.maven.plugins
- directory-maven-plugin
- 0.3.1
-
-
- directories
-
- highest-basedir
-
- initialize
-
- basedirRoot
-
-
-
- org.apache.maven.pluginsmaven-enforcer-plugin