[Major] cleaned up stupid use of forms for POST and added proper sec

This commit is contained in:
Robert von Burg 2014-08-04 00:24:57 +02:00
parent 22ff59c384
commit b6828be09b
9 changed files with 185 additions and 89 deletions

View File

@ -15,6 +15,7 @@
*/
package li.strolch.rest;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
@ -31,10 +32,11 @@ import ch.eitchnet.utils.dbc.DBC;
*/
public class DefaultStrolchSessionHandler extends StrolchComponent implements StrolchSessionHandler {
private static final String PROP_REMEMBER_USER = "rememberUser";
private static final String SESSION_ORIGIN = "session.origin";
private static final String PROP_VALIDATE_ORIGIN = "validateOrigin";
private StrolchPrivilegeHandler privilegeHandler;
private Map<String, Certificate> certificateMap;
private boolean rememberUser;
private boolean validateOrigin;
/**
* @param container
@ -46,7 +48,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
@Override
public void initialize(ComponentConfiguration configuration) {
this.rememberUser = configuration.getBoolean(PROP_REMEMBER_USER, false);
this.validateOrigin = configuration.getBoolean(PROP_VALIDATE_ORIGIN, false);
super.initialize(configuration);
}
@ -81,63 +83,47 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
DBC.PRE.assertNotEmpty("Username must be set!", username);
DBC.PRE.assertNotNull("Passwort must be set", password);
String userId = getUserId(origin, username);
Certificate certificate;
if (this.rememberUser) {
certificate = this.certificateMap.get(userId);
if (certificate != null) {
this.privilegeHandler.isCertificateValid(certificate);
logger.info("Re-using session for user " + userId + " and sessionId " + certificate.getSessionId());
return certificate;
}
}
certificate = this.privilegeHandler.authenticate(username, password);
if (this.rememberUser)
this.certificateMap.put(userId, certificate);
else
this.certificateMap.put(certificate.getAuthToken(), certificate);
Certificate certificate = this.privilegeHandler.authenticate(username, password);
certificate.getSessionDataMap().put(SESSION_ORIGIN, origin);
this.certificateMap.put(certificate.getAuthToken(), certificate);
return certificate;
}
@Override
public Certificate validate(String origin, String username, String sessionId) {
DBC.PRE.assertNotEmpty("Origin must be set!", username);
DBC.PRE.assertNotEmpty("Username must be set!", username);
Certificate certificate;
if (this.rememberUser)
certificate = this.certificateMap.get(getUserId(origin, username));
else
certificate = this.certificateMap.get(sessionId);
public Certificate validate(String origin, String authToken) {
DBC.PRE.assertNotEmpty("Origin must be set!", origin);
DBC.PRE.assertNotEmpty("SessionId must be set!", authToken);
Certificate certificate = this.certificateMap.get(authToken);
if (certificate == null)
throw new StrolchException("No certificate exists for sessionId " + sessionId);
if (!certificate.getUsername().equals(username) || !certificate.getAuthToken().equals(sessionId)) {
throw new StrolchException("Illegal request for username " + username + " and sessionId " + sessionId);
}
throw new StrolchException(MessageFormat.format("No certificate exists for sessionId {0}", authToken));
this.privilegeHandler.isCertificateValid(certificate);
if (this.validateOrigin && !origin.equals(certificate.getSessionDataMap().get(SESSION_ORIGIN))) {
String msg = MessageFormat.format("Illegal request for origin {0} and sessionId {1}", origin, authToken);
throw new StrolchException(msg);
}
return certificate;
}
@Override
public void invalidateSession(String origin, Certificate certificate) {
if (this.rememberUser)
this.certificateMap.remove(getUserId(origin, certificate.getUsername()));
else
this.certificateMap.remove(certificate.getSessionId());
DBC.PRE.assertNotEmpty("Origin must be set!", origin);
DBC.PRE.assertNotNull("Certificate must bet given!", certificate);
if (this.validateOrigin && !origin.equals(certificate.getSessionDataMap().get(SESSION_ORIGIN))) {
String msg = MessageFormat.format("Illegal request for origin {0} and sessionId {1}", origin,
certificate.getAuthToken());
throw new StrolchException(msg);
}
Certificate removedCert = this.certificateMap.remove(certificate.getAuthToken());
if (removedCert == null)
logger.error("No session was registered with token " + certificate.getAuthToken());
this.privilegeHandler.invalidateSession(certificate);
}
/**
* @param origin
* @param username
* @return
*/
private String getUserId(String origin, String username) {
return origin + "_" + username;
}
}

View File

@ -24,6 +24,8 @@ import li.strolch.rest.endpoint.EnumQuery;
import li.strolch.rest.endpoint.Inspector;
import li.strolch.rest.endpoint.VersionQuery;
import li.strolch.rest.filters.AccessControlResponseFilter;
import li.strolch.rest.filters.AuthenicationRequestFilter;
import li.strolch.rest.filters.AuthenicationResponseFilter;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
@ -43,14 +45,13 @@ public class StrolchRestfulClasses {
Set<Class<?>> providerClasses = new HashSet<>();
providerClasses.add(StrolchRestfulExceptionMapper.class);
providerClasses.add(AccessControlResponseFilter.class);
providerClasses.add(AuthenicationRequestFilter.class);
providerClasses.add(AuthenicationResponseFilter.class);
StrolchRestfulClasses.restfulClasses = Collections.unmodifiableSet(restfulClasses);
StrolchRestfulClasses.providerClasses = Collections.unmodifiableSet(providerClasses);
}
/**
* @return the classes
*/
public static Set<Class<?>> getRestfulClasses() {
return restfulClasses;
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.rest;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class StrolchRestfulConstants {
public static final String STROLCH_CERTIFICATE = "strolch.certificate"; //$NON-NLS-1$
}

View File

@ -24,7 +24,7 @@ public interface StrolchSessionHandler {
public Certificate authenticate(String origin, String username, byte[] password);
public Certificate validate(String origin, String username, String sessionId);
public Certificate validate(String origin, String authToken);
public void invalidateSession(String origin, Certificate certificate);
}

View File

@ -16,8 +16,8 @@
package li.strolch.rest.endpoint;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@ -30,9 +30,9 @@ import javax.ws.rs.core.Response.Status;
import li.strolch.exception.StrolchException;
import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.rest.StrolchSessionHandler;
import li.strolch.rest.form.LoginForm;
import li.strolch.rest.form.LogoutForm;
import li.strolch.rest.model.Login;
import li.strolch.rest.model.LoginResult;
import li.strolch.rest.model.Logout;
import li.strolch.rest.model.LogoutResult;
import org.slf4j.Logger;
@ -51,24 +51,24 @@ public class AuthenticationService {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
@Context
HttpServletRequest servletRequest;
HttpServletRequest request;
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("login")
public Response login(@BeanParam LoginForm loginForm) {
public Response login(Login login) {
LoginResult loginResult = new LoginResult();
GenericEntity<LoginResult> entity = new GenericEntity<LoginResult>(loginResult, LoginResult.class) {
};
try {
StringBuilder sb = new StringBuilder();
if (StringHelper.isEmpty(loginForm.getUsername())) {
if (StringHelper.isEmpty(login.getUsername())) {
sb.append("Username was not given. ");
}
if (StringHelper.isEmpty(loginForm.getPassword())) {
if (StringHelper.isEmpty(login.getPassword())) {
sb.append("Password was not given.");
}
@ -79,9 +79,9 @@ public class AuthenticationService {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getComponent(
StrolchSessionHandler.class);
String origin = getOrigin();
Certificate certificate = sessionHandler.authenticate(origin, loginForm.getUsername(), loginForm
.getPassword().getBytes());
String origin = request.getRemoteAddr();
Certificate certificate = sessionHandler.authenticate(origin, login.getUsername(), login.getPassword()
.getBytes());
loginResult.setSessionId(certificate.getAuthToken());
loginResult.setUsername(certificate.getUsername());
@ -102,17 +102,10 @@ public class AuthenticationService {
}
}
private String getOrigin() {
if (servletRequest == null)
return "test";
return servletRequest.getRequestedSessionId();
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("logout")
public Response logout(@BeanParam LogoutForm logoutForm) {
public Response logout(Logout logout) {
LogoutResult logoutResult = new LogoutResult();
@ -121,10 +114,10 @@ public class AuthenticationService {
try {
StringBuilder sb = new StringBuilder();
if (StringHelper.isEmpty(logoutForm.getUsername())) {
if (StringHelper.isEmpty(logout.getUsername())) {
sb.append("Username was not given.");
}
if (StringHelper.isEmpty(logoutForm.getSessionId())) {
if (StringHelper.isEmpty(logout.getSessionId())) {
sb.append("SessionId was not given.");
}
if (sb.length() != 0) {
@ -134,9 +127,8 @@ public class AuthenticationService {
StrolchSessionHandler sessionHandlerHandler = RestfulStrolchComponent.getInstance().getComponent(
StrolchSessionHandler.class);
String origin = getOrigin();
Certificate certificate = sessionHandlerHandler.validate(origin, logoutForm.getUsername(),
logoutForm.getSessionId());
String origin = request.getRemoteAddr();
Certificate certificate = sessionHandlerHandler.validate(origin, logout.getSessionId());
sessionHandlerHandler.invalidateSession(origin, certificate);
return Response.ok().entity(entity).build();

View File

@ -0,0 +1,49 @@
/**
*
*/
package li.strolch.rest.filters;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.rest.StrolchSessionHandler;
import ch.eitchnet.privilege.model.Certificate;
/**
* @author Reto Breitenmoser <reto.breitenmoser@4trees.ch>
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@Provider
public class AuthenicationRequestFilter implements ContainerRequestFilter {
@Context
HttpServletRequest request;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String sessionId = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if (sessionId != null) {
try {
String origin = request.getRemoteAddr();
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getComponent(
StrolchSessionHandler.class);
Certificate certificate = sessionHandler.validate(origin, sessionId);
requestContext.setProperty(STROLCH_CERTIFICATE, certificate);
} catch (Exception e) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("User cannot access the resource.").build()); //$NON-NLS-1$
}
}
}
}

View File

@ -0,0 +1,34 @@
/**
*
*/
package li.strolch.rest.filters;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.ext.Provider;
import ch.eitchnet.privilege.model.Certificate;
/**
* @author Reto Breitenmoser <reto.breitenmoser@4trees.ch>
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@Provider
public class AuthenicationResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
Certificate cert = (Certificate) requestContext.getProperty(STROLCH_CERTIFICATE);
if (cert != null) {
responseContext.getHeaders().add(HttpHeaders.AUTHORIZATION, cert.getAuthToken());
}
}
}

View File

@ -13,21 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.rest.form;
package li.strolch.rest.model;
import javax.ws.rs.FormParam;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class LoginForm {
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "Login")
public class Login {
@FormParam("username")
@XmlAttribute
private String username;
@FormParam("password")
@XmlAttribute
private String password;
public LoginForm() {
public Login() {
// no-arg constructor for JAXB
}

View File

@ -13,21 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.rest.form;
package li.strolch.rest.model;
import javax.ws.rs.FormParam;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class LogoutForm {
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "Logout")
public class Logout {
@FormParam("username")
@XmlAttribute
private String username;
@FormParam("sessionId")
@XmlAttribute
private String sessionId;
public LogoutForm() {
public Logout() {
// no-arg constructor for JAXB
}