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:
Yannick Schaus 2020-05-20 21:54:45 +02:00 committed by GitHub
parent 5e8d7554d8
commit bd976cf937
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 62 deletions

View File

@ -1,2 +0,0 @@
Bundle-SymbolicName: ${project.artifactId}
Bundle-Activator: org.openhab.core.io.rest.auth.internal.Activator

View File

@ -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();
}
}
}

View File

@ -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"));
}
}
}

View File

@ -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) {

View File

@ -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();