session support

This commit is contained in:
Athou
2014-08-09 15:25:41 +02:00
parent 21ec54408e
commit 33b683d037
10 changed files with 202 additions and 77 deletions

View File

@@ -1,18 +1,23 @@
package com.commafeed.frontend.auth;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.StringUtil;
import com.commafeed.CommaFeedApplication;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserRole.Role;
import com.commafeed.backend.service.UserService;
import com.google.common.base.Optional;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.api.model.Parameter;
import com.sun.jersey.core.spi.component.ComponentContext;
@@ -20,66 +25,93 @@ import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
@Slf4j
public class SecurityCheckProvider implements InjectableProvider<SecurityCheck, Parameter> {
private static class SecurityCheckInjectable<T> extends AbstractHttpContextInjectable<User> {
private static final String PREFIX = "Basic";
public static class SecurityCheckUserServiceProvider extends SingletonTypeInjectableProvider<Context, UserService> {
private final UserService userService;
private Role role;
private final boolean apiKeyAllowed;
private SecurityCheckInjectable(UserService userService, Role role, boolean apiKeyAllowed) {
this.userService = userService;
this.role = role;
this.apiKeyAllowed = apiKeyAllowed;
}
@Override
public User getValue(HttpContext c) {
final String header = c.getRequest().getHeaderValue(HttpHeaders.AUTHORIZATION);
try {
if (header != null) {
final int space = header.indexOf(' ');
if (space > 0) {
final String method = header.substring(0, space);
if (PREFIX.equalsIgnoreCase(method)) {
final String decoded = B64Code.decode(header.substring(space + 1), StringUtil.__ISO_8859_1);
final int i = decoded.indexOf(':');
if (i > 0) {
final String username = decoded.substring(0, i);
final String password = decoded.substring(i + 1);
final User user = userService.login(username, password);
if (user != null && user.hasRole(role)) {
return user;
}
}
}
}
} else {
String apiKey = c.getUriInfo().getPathParameters().getFirst("apiKey");
if (apiKey != null && apiKeyAllowed) {
User user = userService.login(apiKey);
if (user != null && user.hasRole(role)) {
return user;
}
}
}
} catch (IllegalArgumentException e) {
log.debug("Error decoding credentials", e);
}
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"CommaFeed\"")
.entity("Credentials are required to access this resource.").type(MediaType.TEXT_PLAIN_TYPE).build());
public SecurityCheckUserServiceProvider(UserService userService) {
super(UserService.class, userService);
}
}
@RequiredArgsConstructor
private static class SecurityCheckInjectable<T> extends AbstractHttpContextInjectable<User> {
private static final String PREFIX = "Basic";
private final HttpServletRequest request;
private final UserService userService;
private final Role role;
private final boolean apiKeyAllowed;
@Override
public User getValue(HttpContext c) {
Optional<User> user = cookieSessionLogin();
if (!user.isPresent()) {
user = basicAuthenticationLogin(c);
}
if (!user.isPresent()) {
user = apiKeyLogin(c);
}
if (user.isPresent()) {
return user.get();
} else {
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.entity("Credentials are required to access this resource.").type(MediaType.TEXT_PLAIN_TYPE).build());
}
}
private Optional<User> cookieSessionLogin() {
HttpSession session = request.getSession(false);
if (session != null) {
User user = (User) session.getAttribute(CommaFeedApplication.SESSION_USER);
return Optional.fromNullable(user);
}
return Optional.absent();
}
private Optional<User> basicAuthenticationLogin(HttpContext c) {
String header = c.getRequest().getHeaderValue(HttpHeaders.AUTHORIZATION);
if (header != null) {
int space = header.indexOf(' ');
if (space > 0) {
String method = header.substring(0, space);
if (PREFIX.equalsIgnoreCase(method)) {
String decoded = B64Code.decode(header.substring(space + 1), StringUtil.__ISO_8859_1);
int i = decoded.indexOf(':');
if (i > 0) {
String username = decoded.substring(0, i);
String password = decoded.substring(i + 1);
Optional<User> user = userService.login(username, password);
if (user.isPresent() && user.get().hasRole(role)) {
return user;
}
}
}
}
}
return Optional.absent();
}
private Optional<User> apiKeyLogin(HttpContext c) {
String apiKey = c.getUriInfo().getPathParameters().getFirst("apiKey");
if (apiKey != null && apiKeyAllowed) {
Optional<User> user = userService.login(apiKey);
if (user.isPresent() && user.get().hasRole(role)) {
return user;
}
}
return Optional.absent();
}
}
private HttpServletRequest request;
private UserService userService;
public SecurityCheckProvider(UserService userService) {
public SecurityCheckProvider(@Context HttpServletRequest request, @Context UserService userService) {
this.request = request;
this.userService = userService;
}
@@ -90,6 +122,6 @@ public class SecurityCheckProvider implements InjectableProvider<SecurityCheck,
@Override
public Injectable<?> getInjectable(ComponentContext ic, SecurityCheck sc, Parameter c) {
return new SecurityCheckInjectable<>(userService, sc.value(), sc.apiKeyAllowed());
return new SecurityCheckInjectable<>(request, userService, sc.value(), sc.apiKeyAllowed());
}
}

View File

@@ -0,0 +1,20 @@
package com.commafeed.frontend.model.request;
import java.io.Serializable;
import lombok.Data;
import com.wordnik.swagger.annotations.ApiModel;
import com.wordnik.swagger.annotations.ApiModelProperty;
@SuppressWarnings("serial")
@Data
@ApiModel
public class LoginRequest implements Serializable {
@ApiModelProperty(value = "username", required = true)
private String name;
@ApiModelProperty(value = "password", required = true)
private String password;
}

View File

@@ -1,9 +1,11 @@
package com.commafeed.frontend.resource;
import io.dropwizard.hibernate.UnitOfWork;
import io.dropwizard.jersey.sessions.Session;
import java.util.Arrays;
import javax.servlet.http.HttpSession;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -33,8 +35,10 @@ import com.commafeed.backend.service.UserService;
import com.commafeed.frontend.auth.SecurityCheck;
import com.commafeed.frontend.model.Settings;
import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.LoginRequest;
import com.commafeed.frontend.model.request.ProfileModificationRequest;
import com.commafeed.frontend.model.request.RegistrationRequest;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
@@ -203,7 +207,20 @@ public class UserREST {
} catch (Exception e) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
}
@Path("/login")
@POST
@UnitOfWork
@ApiOperation(value = "Login and create a session")
public Response login(@ApiParam(required = true) LoginRequest req, @Session HttpSession session) {
Optional<User> user = userService.login(req.getName(), req.getPassword());
if (user.isPresent()) {
session.setAttribute(CommaFeedApplication.SESSION_USER, user);
return Response.ok().build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
@Path("/profile/deleteAccount")

View File

@@ -0,0 +1,25 @@
package com.commafeed.frontend.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import com.commafeed.CommaFeedConfiguration;
@SuppressWarnings("serial")
@RequiredArgsConstructor
public class LogoutServlet extends HttpServlet {
private final CommaFeedConfiguration config;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().invalidate();
resp.sendRedirect(resp.encodeRedirectURL(config.getApplicationSettings().getPublicUrl()));
}
}

View File

@@ -24,6 +24,7 @@ import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserSettings.ReadingOrder;
import com.commafeed.backend.service.UserService;
import com.commafeed.frontend.resource.CategoryREST;
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
@SuppressWarnings("serial")
@@ -51,8 +52,8 @@ public class NextUnreadServlet extends HttpServlet {
return;
}
User user = userService.login(apiKey);
if (user == null) {
Optional<User> user = userService.login(apiKey);
if (!user.isPresent()) {
resp.getWriter().write("unknown user or api key not found");
return;
}
@@ -65,14 +66,15 @@ public class NextUnreadServlet extends HttpServlet {
List<FeedEntryStatus> statuses = null;
if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) {
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
statuses = feedEntryStatusDAO.findBySubscriptions(user, subs, true, null, null, 0, 1, order, true, false, null);
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user.get());
statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subs, true, null, null, 0, 1, order, true, false, null);
} else {
FeedCategory category = feedCategoryDAO.findById(user, Long.valueOf(categoryId));
FeedCategory category = feedCategoryDAO.findById(user.get(), Long.valueOf(categoryId));
if (category != null) {
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user, category);
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user, children);
statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, null, null, 0, 1, order, true, false, null);
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user.get(), category);
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user.get(), children);
statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subscriptions, true, null, null, 0, 1, order, true, false,
null);
}
}