[auth] Decouple session & API token management from ManagedUser implementation & JaasAuthenticationProvider fixes (#5325)

Signed-off-by: Florian Hotze <dev@florianhotze.com>
This commit is contained in:
Florian Hotze
2026-02-04 23:07:44 +01:00
committed by GitHub
parent 4644636284
commit 450c104d5a
7 changed files with 209 additions and 121 deletions
@@ -72,10 +72,10 @@ public class JaasAuthenticationProvider implements AuthenticationProvider {
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Principal userPrincipal = new GenericUser(name);
Subject subject = new Subject(true, Set.of(userPrincipal), Set.of(), Set.of(userCredentials));
Subject subject = new Subject(DEFAULT_REALM.equals(realmName), Set.of(userPrincipal), Set.of(),
Set.of(userCredentials));
Thread.currentThread().setContextClassLoader(ManagedUserLoginModule.class.getClassLoader());
LoginContext loginContext = new LoginContext(realmName, subject, new CallbackHandler() {
CallbackHandler callbackHandler = new CallbackHandler() {
@Override
public void handle(@NonNullByDefault({}) Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
@@ -89,7 +89,16 @@ public class JaasAuthenticationProvider implements AuthenticationProvider {
}
}
}
}, new ManagedUserLoginConfiguration());
};
LoginContext loginContext;
if (DEFAULT_REALM.equals(realmName)) {
Thread.currentThread().setContextClassLoader(ManagedUserLoginModule.class.getClassLoader());
loginContext = new LoginContext(realmName, subject, callbackHandler,
new ManagedUserLoginConfiguration());
} else {
loginContext = new LoginContext(realmName, subject, callbackHandler);
}
loginContext.login();
return getAuthentication(name, loginContext.getSubject());
@@ -106,12 +115,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider {
}
private String[] getRoles(Set<Principal> principals) {
String[] roles = new String[principals.size()];
int i = 0;
for (Principal principal : principals) {
roles[i++] = principal.getName();
}
return roles;
return principals.stream().map(Principal::getName).distinct().toArray(String[]::new);
}
@Activate
@@ -27,9 +27,9 @@ import javax.ws.rs.core.HttpHeaders;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.core.auth.AuthenticatedUser;
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.Role;
import org.openhab.core.auth.User;
@@ -168,15 +168,15 @@ public class AuthorizePageServlet extends AbstractAuthPageServlet {
String authorizationCode = UUID.randomUUID().toString().replace("-", "");
if (user instanceof ManagedUser managedUser) {
if (user instanceof AuthenticatedUser authenticatedUser) {
String codeChallenge = params.containsKey("code_challenge") ? params.get("code_challenge")[0] : null;
String codeChallengeMethod = params.containsKey("code_challenge_method")
? params.get("code_challenge_method")[0]
: null;
PendingToken pendingToken = new PendingToken(authorizationCode, clientId, baseRedirectUri, scope,
codeChallenge, codeChallengeMethod);
managedUser.setPendingToken(pendingToken);
userRegistry.update(managedUser);
authenticatedUser.setPendingToken(pendingToken);
userRegistry.update(authenticatedUser);
}
String state = params.containsKey("state") ? params.get("state")[0] : null;
@@ -22,9 +22,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.AuthenticatedUser;
import org.openhab.core.auth.AuthenticationException;
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.i18n.LocaleProvider;
@@ -101,8 +101,9 @@ public class CreateAPITokenPageServlet extends AbstractAuthPageServlet {
User user = login(username, password);
String newApiToken;
if (user instanceof ManagedUser managedUser) {
if (managedUser.getApiTokens().stream().anyMatch(apiToken -> apiToken.getName().equals(tokenName))) {
if (user instanceof AuthenticatedUser authenticatedUser) {
if (authenticatedUser.getApiTokens().stream()
.anyMatch(apiToken -> apiToken.getName().equals(tokenName))) {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().append(
getPageBody(params, getLocalizedMessage("auth.createapitoken.name.unique.fail"), false));
@@ -119,7 +120,7 @@ public class CreateAPITokenPageServlet extends AbstractAuthPageServlet {
}
newApiToken = userRegistry.addUserApiToken(user, tokenName, tokenScope);
} else {
throw new AuthenticationException("User is not managed");
throw new AuthenticationException("User authentication is not managed by openHAB");
}
String resultMessage = getLocalizedMessage("auth.createapitoken.success") + "<br /><br /><code>"
@@ -13,6 +13,7 @@
package org.openhab.core.io.rest.auth.internal;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
@@ -41,7 +42,7 @@ import javax.ws.rs.core.SecurityContext;
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.AuthenticatedUser;
import org.openhab.core.auth.PendingToken;
import org.openhab.core.auth.User;
import org.openhab.core.auth.UserApiToken;
@@ -145,6 +146,7 @@ public class TokenResource implements RESTResource {
@Path("/sessions")
@Operation(operationId = "getSessionsForCurrentUser", summary = "List the sessions associated to the authenticated user.", responses = {
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = UserSessionDTO.class)))),
@ApiResponse(responseCode = "400", description = "User authentication is not managed by openHAB"),
@ApiResponse(responseCode = "401", description = "User is not authenticated"),
@ApiResponse(responseCode = "404", description = "User not found") })
@Produces({ MediaType.APPLICATION_JSON })
@@ -153,12 +155,16 @@ public class TokenResource implements RESTResource {
return JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "User is not authenticated");
}
ManagedUser user = (ManagedUser) userRegistry.get(securityContext.getUserPrincipal().getName());
User user = userRegistry.get(securityContext.getUserPrincipal().getName());
if (user == null) {
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "User not found");
}
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
"User authentication is not managed by openHAB");
}
Stream<UserSessionDTO> sessions = user.getSessions().stream().map(this::toUserSessionDTO);
Stream<UserSessionDTO> sessions = authenticatedUser.getSessions().stream().map(this::toUserSessionDTO);
return Response.ok(new Stream2JSONInputStream(sessions)).build();
}
@@ -166,6 +172,7 @@ public class TokenResource implements RESTResource {
@Path("/apitokens")
@Operation(operationId = "getApiTokens", summary = "List the API tokens associated to the authenticated user.", responses = {
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = UserApiTokenDTO.class)))),
@ApiResponse(responseCode = "400", description = "User authentication is not managed by openHAB"),
@ApiResponse(responseCode = "401", description = "User is not authenticated"),
@ApiResponse(responseCode = "404", description = "User not found") })
@Produces({ MediaType.APPLICATION_JSON })
@@ -174,12 +181,16 @@ public class TokenResource implements RESTResource {
return JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "User is not authenticated");
}
ManagedUser user = (ManagedUser) userRegistry.get(securityContext.getUserPrincipal().getName());
User user = userRegistry.get(securityContext.getUserPrincipal().getName());
if (user == null) {
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "User not found");
}
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
"User authentication is not managed by openHAB");
}
Stream<UserApiTokenDTO> sessions = user.getApiTokens().stream().map(this::toUserApiTokenDTO);
Stream<UserApiTokenDTO> sessions = authenticatedUser.getApiTokens().stream().map(this::toUserApiTokenDTO);
return Response.ok(new Stream2JSONInputStream(sessions)).build();
}
@@ -187,6 +198,7 @@ public class TokenResource implements RESTResource {
@Path("/apitokens/{name}")
@Operation(operationId = "removeApiToken", summary = "Revoke a specified API token associated to the authenticated user.", responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "400", description = "User authentication is not managed by openHAB"),
@ApiResponse(responseCode = "401", description = "User is not authenticated"),
@ApiResponse(responseCode = "404", description = "User or API token not found") })
public Response removeApiToken(@Context SecurityContext securityContext, @PathParam("name") String name) {
@@ -194,12 +206,16 @@ public class TokenResource implements RESTResource {
return JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "User is not authenticated");
}
ManagedUser user = (ManagedUser) userRegistry.get(securityContext.getUserPrincipal().getName());
User user = userRegistry.get(securityContext.getUserPrincipal().getName());
if (user == null) {
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "User not found");
}
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
"User authentication is not managed by openHAB");
}
Optional<UserApiToken> userApiToken = user.getApiTokens().stream()
Optional<UserApiToken> userApiToken = authenticatedUser.getApiTokens().stream()
.filter(apiToken -> apiToken.getName().equals(name)).findAny();
if (userApiToken.isEmpty()) {
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "No API token found with that name");
@@ -214,6 +230,7 @@ public class TokenResource implements RESTResource {
@Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
@Operation(operationId = "deleteSession", summary = "Delete the session associated with a refresh token.", responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "400", description = "User authentication is not managed by openHAB"),
@ApiResponse(responseCode = "401", description = "User is not authenticated"),
@ApiResponse(responseCode = "404", description = "User or refresh token not found") })
public Response deleteSession(@Nullable @FormParam("refresh_token") String refreshToken,
@@ -223,16 +240,22 @@ public class TokenResource implements RESTResource {
return JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "User is not authenticated");
}
ManagedUser user = (ManagedUser) userRegistry.get(securityContext.getUserPrincipal().getName());
User user = userRegistry.get(securityContext.getUserPrincipal().getName());
if (user == null) {
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "User not found");
}
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
"User authentication is not managed by openHAB");
}
Optional<UserSession> session;
if (refreshToken != null) {
session = user.getSessions().stream().filter(s -> s.getRefreshToken().equals(refreshToken)).findAny();
session = authenticatedUser.getSessions().stream().filter(s -> s.getRefreshToken().equals(refreshToken))
.findAny();
} else if (id != null) {
session = user.getSessions().stream().filter(s -> s.getSessionId().startsWith(id + "-")).findAny();
session = authenticatedUser.getSessions().stream().filter(s -> s.getSessionId().startsWith(id + "-"))
.findAny();
} else {
throw new IllegalArgumentException("no refresh_token or id specified");
}
@@ -249,7 +272,8 @@ public class TokenResource implements RESTResource {
// jakarta.ws.rs/jakarta.ws.rs-api/3.1.0 or newer
response.header("Set-Cookie",
SESSIONID_COOKIE_FORMAT.formatted(UUID.randomUUID(), domainUri.getHost()));
} catch (Exception e) {
} catch (URISyntaxException e) {
logger.error("Unexpected error deleting session", e);
}
}
@@ -272,20 +296,26 @@ public class TokenResource implements RESTResource {
private Response processAuthorizationCodeGrant(String code, String redirectUri, String clientId,
@Nullable String codeVerifier, boolean useCookie) throws TokenEndpointException, NoSuchAlgorithmException {
// find a user with the authorization code pending
Optional<User> user = userRegistry.getAll().stream().filter(u -> {
ManagedUser managedUser = (ManagedUser) u;
Optional<User> optionalUser = userRegistry.getAll().stream().filter(u -> {
if (!(u instanceof AuthenticatedUser authenticatedUser)) {
return false;
}
@Nullable
PendingToken pendingToken = managedUser.getPendingToken();
PendingToken pendingToken = authenticatedUser.getPendingToken();
return (pendingToken != null && pendingToken.getAuthorizationCode().equals(code));
}).findAny();
if (user.isEmpty()) {
if (optionalUser.isEmpty()) {
logger.warn("Couldn't find a user with the provided authentication code pending");
throw new TokenEndpointException(ErrorType.INVALID_GRANT);
}
ManagedUser managedUser = (ManagedUser) user.get();
PendingToken pendingToken = managedUser.getPendingToken();
User user = optionalUser.get();
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
logger.warn("User '{}' authentication is not managed by openHAB", user.getName());
throw new TokenEndpointException(ErrorType.INVALID_CLIENT);
}
PendingToken pendingToken = authenticatedUser.getPendingToken();
if (pendingToken == null) {
throw new TokenEndpointException(ErrorType.INVALID_GRANT);
}
@@ -335,12 +365,12 @@ public class TokenResource implements RESTResource {
}
// create an access token
String accessToken = jwtHelper.getJwtAccessToken(managedUser, clientId, scope, TOKEN_LIFETIME);
String accessToken = jwtHelper.getJwtAccessToken(authenticatedUser, clientId, scope, TOKEN_LIFETIME);
UserSession newSession = new UserSession(sessionId, newRefreshToken, clientId, redirectUri, scope);
ResponseBuilder response = Response.ok(
new TokenResponseDTO(accessToken, "bearer", TOKEN_LIFETIME * 60, newRefreshToken, scope, managedUser));
ResponseBuilder response = Response.ok(new TokenResponseDTO(accessToken, "bearer", TOKEN_LIFETIME * 60,
newRefreshToken, scope, authenticatedUser));
// if the client has requested an http-only cookie for the session, set it
if (useCookie) {
@@ -366,9 +396,9 @@ public class TokenResource implements RESTResource {
}
// add the new session to the user profile and clear the pending token information
managedUser.getSessions().add(newSession);
managedUser.setPendingToken(null);
userRegistry.update(managedUser);
authenticatedUser.getSessions().add(newSession);
authenticatedUser.setPendingToken(null);
userRegistry.update(authenticatedUser);
return response.build();
}
@@ -380,42 +410,52 @@ public class TokenResource implements RESTResource {
}
// find a user associated with the provided refresh token
Optional<User> refreshTokenUser = userRegistry.getAll().stream().filter(
u -> ((ManagedUser) u).getSessions().stream().anyMatch(s -> refreshToken.equals(s.getRefreshToken())))
Optional<User> optionalRefreshTokenUser = userRegistry.getAll().stream()
.filter(u -> u instanceof AuthenticatedUser).filter(u -> ((AuthenticatedUser) u).getSessions().stream()
.anyMatch(s -> refreshToken.equals(s.getRefreshToken())))
.findAny();
if (refreshTokenUser.isEmpty()) {
if (optionalRefreshTokenUser.isEmpty()) {
logger.warn("Couldn't find a user with a session matching the provided refresh_token");
throw new TokenEndpointException(ErrorType.INVALID_GRANT);
}
// get the session from the refresh token
ManagedUser refreshTokenManagedUser = (ManagedUser) refreshTokenUser.get();
UserSession session = refreshTokenManagedUser.getSessions().stream()
.filter(s -> s.getRefreshToken().equals(refreshToken)).findAny().get();
User refreshTokenUser = optionalRefreshTokenUser.get();
if (!(refreshTokenUser instanceof AuthenticatedUser refreshTokenAuthenticatedUser)) {
logger.warn("User '{}' authentication is not managed by openHAB", refreshTokenUser.getName());
throw new TokenEndpointException(ErrorType.INVALID_CLIENT);
}
Optional<UserSession> optSession = refreshTokenAuthenticatedUser.getSessions().stream()
.filter(s -> s.getRefreshToken().equals(refreshToken)).findAny();
if (optSession.isEmpty()) {
logger.warn("Not refreshing token for user {}, missing session", refreshTokenAuthenticatedUser.getName());
throw new TokenEndpointException(ErrorType.INVALID_GRANT);
}
UserSession session = optSession.get();
// if the cookie flag is present on the session, verify that the cookie is present and corresponds
// to this session
if (session.hasSessionCookie()) {
if (sessionCookie == null || !sessionCookie.getValue().equals(session.getSessionId())) {
logger.warn("Not refreshing token for session {} of user {}, missing or invalid session cookie",
session.getSessionId(), refreshTokenManagedUser.getName());
throw new TokenEndpointException(ErrorType.INVALID_GRANT);
}
if (session.hasSessionCookie()
&& (sessionCookie == null || !sessionCookie.getValue().equals(session.getSessionId()))) {
logger.warn("Not refreshing token for session {} of user {}, missing or invalid session cookie",
session.getSessionId(), refreshTokenAuthenticatedUser.getName());
throw new TokenEndpointException(ErrorType.INVALID_GRANT);
}
// issue a new access token
String refreshedAccessToken = jwtHelper.getJwtAccessToken(refreshTokenManagedUser, clientId, session.getScope(),
TOKEN_LIFETIME);
String refreshedAccessToken = jwtHelper.getJwtAccessToken(refreshTokenAuthenticatedUser, clientId,
session.getScope(), TOKEN_LIFETIME);
logger.debug("Refreshing session {} of user {}", session.getSessionId(), refreshTokenManagedUser.getName());
logger.debug("Refreshing session {} of user {}", session.getSessionId(),
refreshTokenAuthenticatedUser.getName());
ResponseBuilder refreshResponse = Response.ok(new TokenResponseDTO(refreshedAccessToken, "bearer",
TOKEN_LIFETIME * 60, refreshToken, session.getScope(), refreshTokenManagedUser));
TOKEN_LIFETIME * 60, refreshToken, session.getScope(), refreshTokenAuthenticatedUser));
// update the last refresh time of the session in the user's profile
session.setLastRefreshTime(new Date());
userRegistry.update(refreshTokenManagedUser);
userRegistry.update(refreshTokenAuthenticatedUser);
return refreshResponse.build();
}
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2010-2026 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.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* A {@link User} that is authenticated by the system itself, i.e., session state and API tokens are managed by the
* system.
*
* @author Florian Hotze - Initial contribution
*/
@NonNullByDefault
public interface AuthenticatedUser extends User {
/**
* Gets the pending token information for this user, if any.
*
* @return the pending token information or null if there is none
*/
@Nullable
PendingToken getPendingToken();
/**
* Sets or clears the pending token information for this user.
*
* @param pendingToken the pending token information or null to clear it
*/
void setPendingToken(@Nullable PendingToken pendingToken);
/**
* Gets the current persistent sessions for this user.
*
* @return the list of sessions
*/
List<UserSession> getSessions();
/**
* Replaces the list of sessions by a new one.
*
* @param sessions the new list of sessions
*/
void setSessions(List<UserSession> sessions);
/**
* Gets the long-term API tokens for this user
*
* @return the API tokens
*/
List<UserApiToken> getApiTokens();
/**
* Replaces the list of API tokens by a new one.
*
* @param apiTokens the new API tokens
*/
void setApiTokens(List<UserApiToken> apiTokens);
}
@@ -26,7 +26,7 @@ import org.eclipse.jdt.annotation.Nullable;
* @author Yannick Schaus - initial contribution
*/
@NonNullByDefault
public class ManagedUser implements User {
public class ManagedUser implements AuthenticatedUser {
private String name;
private String passwordHash;
@@ -118,56 +118,32 @@ public class ManagedUser implements User {
this.roles = roles;
}
/**
* Gets the pending token information for this user, if any.
*
* @return the pending token information or null if there is none
*/
@Override
public @Nullable PendingToken getPendingToken() {
return pendingToken;
}
/**
* Sets or clears the pending token information for this user.
*
* @param pendingToken the pending token information or null to clear it
*/
@Override
public void setPendingToken(@Nullable PendingToken pendingToken) {
this.pendingToken = pendingToken;
}
/**
* Gets the current persistent sessions for this user.
*
* @return the list of sessions
*/
@Override
public List<UserSession> getSessions() {
return sessions;
}
/**
* Replaces the list of sessions by a new one.
*
* @param sessions the new list of sessions
*/
@Override
public void setSessions(List<UserSession> sessions) {
this.sessions = sessions;
}
/**
* Gets the long-term API tokens for this user
*
* @return the API tokens
*/
@Override
public List<UserApiToken> getApiTokens() {
return apiTokens;
}
/**
* Replaces the list of API tokens by a new one.
*
* @param apiTokens the new API tokens
*/
@Override
public void setApiTokens(List<UserApiToken> apiTokens) {
this.apiTokens = apiTokens;
}
@@ -26,6 +26,7 @@ import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.auth.AuthenticatedUser;
import org.openhab.core.auth.Authentication;
import org.openhab.core.auth.AuthenticationException;
import org.openhab.core.auth.Credentials;
@@ -153,19 +154,20 @@ public class UserRegistryImpl extends AbstractRegistry<User, String, UserProvide
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 (user instanceof AuthenticatedUser authenticatedUser) {
for (UserApiToken userApiToken : authenticatedUser.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());
if (incomingTokenHash.equals(existingTokenHashAndSalt[0])) {
return new Authentication(authenticatedUser.getName(),
authenticatedUser.getRoles().toArray(String[]::new), userApiToken.getScope());
}
}
}
}
@@ -192,47 +194,43 @@ public class UserRegistryImpl extends AbstractRegistry<User, String, UserProvide
@Override
public void addUserSession(User user, UserSession session) {
if (!(user instanceof ManagedUser)) {
throw new IllegalArgumentException("User is not managed: " + user.getName());
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
throw new IllegalArgumentException("User authentication is not managed by openHAB: " + user.getName());
}
ManagedUser managedUser = (ManagedUser) user;
managedUser.getSessions().add(session);
authenticatedUser.getSessions().add(session);
update(user);
}
@Override
public void removeUserSession(User user, UserSession session) {
if (!(user instanceof ManagedUser)) {
throw new IllegalArgumentException("User is not managed: " + user.getName());
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
throw new IllegalArgumentException("User authentication is not managed by openHAB: " + user.getName());
}
ManagedUser managedUser = (ManagedUser) user;
managedUser.getSessions().remove(session);
authenticatedUser.getSessions().remove(session);
update(user);
}
@Override
public void clearSessions(User user) {
if (!(user instanceof ManagedUser)) {
throw new IllegalArgumentException("User is not managed: " + user.getName());
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
throw new IllegalArgumentException("User authentication is not managed by openHAB: " + user.getName());
}
ManagedUser managedUser = (ManagedUser) user;
managedUser.getSessions().clear();
authenticatedUser.getSessions().clear();
update(user);
}
@Override
public String addUserApiToken(User user, String name, String scope) {
if (!(user instanceof ManagedUser)) {
throw new IllegalArgumentException("User is not managed: " + user.getName());
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
throw new IllegalArgumentException("User authentication is not managed by openHAB: " + user.getName());
}
if (!name.matches("[a-zA-Z0-9]*")) {
throw new IllegalArgumentException("API token name format invalid, alphanumeric characters only");
}
ManagedUser managedUser = (ManagedUser) user;
String tokenSalt = generateSalt(KEY_LENGTH / 8).get();
byte[] rnd = new byte[64];
RAND.nextBytes(rnd);
@@ -242,7 +240,7 @@ public class UserRegistryImpl extends AbstractRegistry<User, String, UserProvide
UserApiToken userApiToken = new UserApiToken(name, tokenHash + ":" + tokenSalt, scope);
managedUser.getApiTokens().add(userApiToken);
authenticatedUser.getApiTokens().add(userApiToken);
update(user);
return token;
@@ -250,12 +248,11 @@ public class UserRegistryImpl extends AbstractRegistry<User, String, UserProvide
@Override
public void removeUserApiToken(User user, UserApiToken userApiToken) {
if (!(user instanceof ManagedUser)) {
throw new IllegalArgumentException("User is not managed: " + user.getName());
if (!(user instanceof AuthenticatedUser authenticatedUser)) {
throw new IllegalArgumentException("User authentication is not managed by openHAB: " + user.getName());
}
ManagedUser managedUser = (ManagedUser) user;
managedUser.getApiTokens().remove(userApiToken);
authenticatedUser.getApiTokens().remove(userApiToken);
update(user);
}