[Major] cleaned up stupid use of forms for POST and added proper sec
This commit is contained in:
parent
22ff59c384
commit
b6828be09b
|
@ -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
|
||||
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());
|
||||
this.privilegeHandler.invalidateSession(certificate);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param origin
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
private String getUserId(String origin, String username) {
|
||||
return origin + "_" + username;
|
||||
Certificate removedCert = this.certificateMap.remove(certificate.getAuthToken());
|
||||
if (removedCert == null)
|
||||
logger.error("No session was registered with token " + certificate.getAuthToken());
|
||||
|
||||
this.privilegeHandler.invalidateSession(certificate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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$
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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$
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue