mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-01-25 19:55:48 +01:00
REST API: Re-enable auth (#1482)
* Add JAX-RS annotations to auth-related filter & dynamic feature. * Remove unnecessary Activator. * Remove BND file. * Build error responses explicity instead of throwing exceptions in TokenResource to avoid logging. Fixes #1477 Signed-off-by: Yannick Schaus <github@schaus.net>
This commit is contained in:
parent
5e8d7554d8
commit
bd976cf937
@ -1,2 +0,0 @@
|
||||
Bundle-SymbolicName: ${project.artifactId}
|
||||
Bundle-Activator: org.openhab.core.io.rest.auth.internal.Activator
|
@ -1,46 +0,0 @@
|
||||
/**
|
||||
* 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 org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
|
||||
/**
|
||||
* Registers dynamic features contained in this bundle.
|
||||
*
|
||||
* @author Yannick Schaus - initial contribution
|
||||
*/
|
||||
public class Activator implements BundleActivator {
|
||||
private static Activator instance;
|
||||
|
||||
private ServiceRegistration<?> rolesAllowedDynamicFeatureRegistration;
|
||||
|
||||
public static Activator getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(BundleContext context) throws Exception {
|
||||
rolesAllowedDynamicFeatureRegistration = context.registerService(RolesAllowedDynamicFeatureImpl.class.getName(),
|
||||
new RolesAllowedDynamicFeatureImpl(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
instance = null;
|
||||
if (rolesAllowedDynamicFeatureRegistration != null) {
|
||||
rolesAllowedDynamicFeatureRegistration.unregister();
|
||||
}
|
||||
}
|
||||
}
|
@ -16,18 +16,23 @@ import java.io.IOException;
|
||||
|
||||
import javax.annotation.Priority;
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
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.Status;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import org.openhab.core.auth.Authentication;
|
||||
import org.openhab.core.io.rest.JSONResponse;
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
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;
|
||||
|
||||
/**
|
||||
* This filter is responsible for parsing a token provided with a request, and hydrating a {@link SecurityContext} from
|
||||
@ -36,9 +41,11 @@ import org.osgi.service.component.annotations.Reference;
|
||||
* @author Yannick Schaus - initial contribution
|
||||
*/
|
||||
@PreMatching
|
||||
@Component
|
||||
@JaxrsExtension
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
@Priority(Priorities.AUTHENTICATION)
|
||||
@Provider
|
||||
@Component(immediate = true, service = AuthFilter.class)
|
||||
public class AuthFilter implements ContainerRequestFilter {
|
||||
private static final String COOKIE_AUTH_HEADER = "X-OPENHAB-AUTH-HEADER";
|
||||
private static final String ALT_AUTH_HEADER = "X-OPENHAB-TOKEN";
|
||||
@ -81,7 +88,7 @@ public class AuthFilter implements ContainerRequestFilter {
|
||||
}
|
||||
}
|
||||
} catch (AuthenticationException e) {
|
||||
throw new NotAuthorizedException("Invalid token");
|
||||
requestContext.abortWith(JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "Invalid token"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,17 +22,22 @@ import javax.annotation.Priority;
|
||||
import javax.annotation.security.DenyAll;
|
||||
import javax.annotation.security.PermitAll;
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.ws.rs.ForbiddenException;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
import javax.ws.rs.Priorities;
|
||||
import javax.ws.rs.container.ContainerRequestContext;
|
||||
import javax.ws.rs.container.ContainerRequestFilter;
|
||||
import javax.ws.rs.container.DynamicFeature;
|
||||
import javax.ws.rs.container.ResourceInfo;
|
||||
import javax.ws.rs.core.FeatureContext;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import org.openhab.core.auth.Role;
|
||||
import org.openhab.core.io.rest.JSONResponse;
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsExtension;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -48,6 +53,9 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Yannick Schaus - port to openHAB with modifications
|
||||
*/
|
||||
@Provider
|
||||
@Component
|
||||
@JaxrsExtension
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + RESTConstants.JAX_RS_NAME + ")")
|
||||
public class RolesAllowedDynamicFeatureImpl implements DynamicFeature {
|
||||
private final Logger logger = LoggerFactory.getLogger(RolesAllowedDynamicFeatureImpl.class);
|
||||
|
||||
@ -114,7 +122,9 @@ public class RolesAllowedDynamicFeatureImpl implements DynamicFeature {
|
||||
}
|
||||
|
||||
if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
|
||||
throw new NotAuthorizedException("User is not authenticated");
|
||||
requestContext.abortWith(
|
||||
JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "User is not authenticated"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (final String role : rolesAllowed) {
|
||||
@ -124,7 +134,8 @@ public class RolesAllowedDynamicFeatureImpl implements DynamicFeature {
|
||||
}
|
||||
}
|
||||
|
||||
throw new ForbiddenException("User is authenticated but doesn't have access to this resource");
|
||||
requestContext.abortWith(JSONResponse.createErrorResponse(Status.FORBIDDEN,
|
||||
"User is authenticated but doesn't have access to this resource"));
|
||||
}
|
||||
|
||||
private static boolean isAuthenticated(final ContainerRequestContext requestContext) {
|
||||
|
@ -24,8 +24,6 @@ import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.CookieParam;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
@ -47,6 +45,7 @@ import org.openhab.core.auth.PendingToken;
|
||||
import org.openhab.core.auth.User;
|
||||
import org.openhab.core.auth.UserRegistry;
|
||||
import org.openhab.core.auth.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;
|
||||
@ -141,12 +140,12 @@ public class TokenResource implements RESTResource {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response getSessions(@Context SecurityContext securityContext) {
|
||||
if (securityContext.getUserPrincipal() == null) {
|
||||
throw new NotAuthorizedException("User not authenticated");
|
||||
return JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "User is not authenticated");
|
||||
}
|
||||
|
||||
ManagedUser user = (ManagedUser) userRegistry.get(securityContext.getUserPrincipal().getName());
|
||||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "User not found");
|
||||
}
|
||||
|
||||
Stream<UserSessionDTO> sessions = user.getSessions().stream().map(this::toUserSessionDTO);
|
||||
@ -161,12 +160,12 @@ public class TokenResource implements RESTResource {
|
||||
public Response deleteSession(@FormParam("refresh_token") String refreshToken, @FormParam("id") String id,
|
||||
@Context SecurityContext securityContext) {
|
||||
if (securityContext.getUserPrincipal() == null) {
|
||||
throw new NotAuthorizedException("User not authenticated");
|
||||
return JSONResponse.createErrorResponse(Status.UNAUTHORIZED, "User is not authenticated");
|
||||
}
|
||||
|
||||
ManagedUser user = (ManagedUser) userRegistry.get(securityContext.getUserPrincipal().getName());
|
||||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "User not found");
|
||||
}
|
||||
|
||||
Optional<UserSession> session;
|
||||
@ -178,7 +177,7 @@ public class TokenResource implements RESTResource {
|
||||
throw new IllegalArgumentException("no refresh_token or id specified");
|
||||
}
|
||||
if (session.isEmpty()) {
|
||||
throw new NotFoundException("Session not found");
|
||||
return JSONResponse.createErrorResponse(Status.NOT_FOUND, "Session not found");
|
||||
}
|
||||
|
||||
ResponseBuilder response = Response.ok();
|
||||
|
Loading…
Reference in New Issue
Block a user