/* * Copyright 2015 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.endpoint; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.HEAD; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.JsonObject; import li.strolch.exception.StrolchException; import li.strolch.privilege.base.AccessDeniedException; import li.strolch.privilege.base.InvalidCredentialsException; import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.PrivilegeContext; import li.strolch.rest.RestfulStrolchComponent; import li.strolch.rest.StrolchRestfulConstants; import li.strolch.rest.StrolchSessionHandler; import li.strolch.rest.model.Login; import li.strolch.rest.model.LoginResult; import li.strolch.rest.model.LogoutResult; import li.strolch.runtime.privilege.PrivilegeHandler; import li.strolch.utils.helper.StringHelper; /** * @author Robert von Burg <eitch@eitchnet.ch> */ @Path("strolch/authentication") public class AuthenticationService { private static final Logger logger = LoggerFactory.getLogger(AuthenticationService.class); @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response login(Login login, @Context HttpServletRequest request, @Context HttpHeaders headers) { LoginResult loginResult = new LoginResult(); try { StringBuilder sb = new StringBuilder(); if (StringHelper.isEmpty(login.getUsername()) || login.getUsername().length() < 2) { sb.append("Username was not given or is too short!"); //$NON-NLS-1$ } if (login.getPassword() == null || login.getPassword().length < 3) { if (sb.length() > 0) sb.append("\n"); sb.append("Password was not given or was too short!"); //$NON-NLS-1$ } if (sb.length() != 0) { loginResult.setMsg(MessageFormat.format("Could not log in due to: {0}", sb.toString())); //$NON-NLS-1$ return Response.status(Status.BAD_REQUEST).entity(loginResult).build(); } RestfulStrolchComponent restfulStrolchComponent = RestfulStrolchComponent.getInstance(); StrolchSessionHandler sessionHandler = restfulStrolchComponent.getComponent(StrolchSessionHandler.class); Certificate certificate = sessionHandler.authenticate(login.getUsername(), login.getPassword()); PrivilegeHandler privilegeHandler = restfulStrolchComponent.getContainer().getPrivilegeHandler(); PrivilegeContext privilegeContext = privilegeHandler.getPrivilegeContext(certificate); loginResult.setSessionId(certificate.getSessionId()); loginResult.setAuthToken(certificate.getAuthToken()); loginResult.setUsername(certificate.getUsername()); loginResult.setFirstname(certificate.getFirstname()); loginResult.setLastname(certificate.getLastname()); loginResult.setLocale(certificate.getLocale()); loginResult.setParameters(certificate.getPropertyMap()); loginResult.setRoles(new ArrayList<>(certificate.getUserRoles())); List<LoginResult.Privilege> privileges = new ArrayList<>(); for (String name : privilegeContext.getPrivilegeNames()) { IPrivilege privilege = privilegeContext.getPrivilege(name); Set<String> allowSet = privilege.getAllowList(); ArrayList<String> allowList = null; if (!allowSet.isEmpty()) allowList = new ArrayList<>(allowSet); privileges.add(new LoginResult.Privilege(name, privilege.isAllAllowed(), allowList)); } loginResult.setPrivileges(privileges); boolean secureCookie = restfulStrolchComponent.isSecureCookie(); if (secureCookie && !request.getScheme().equals("https")) { logger.warn( "Authorization cookie is secure, but connection is not secure! Cookie won't be passed to client!"); } NewCookie cookie = new NewCookie(StrolchRestfulConstants.STROLCH_AUTHORIZATION, certificate.getAuthToken(), "/", null, "Authorization header", (int) TimeUnit.DAYS.toSeconds(1), secureCookie); return Response.ok().entity(loginResult)// .header(HttpHeaders.AUTHORIZATION, certificate.getAuthToken()).cookie(cookie).build(); } catch (InvalidCredentialsException e) { logger.error(e.getMessage(), e); loginResult.setMsg("Could not log in as the given credentials are invalid"); //$NON-NLS-1$ return Response.status(Status.UNAUTHORIZED).entity(loginResult).build(); } catch (AccessDeniedException e) { logger.error(e.getMessage(), e); loginResult.setMsg(MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-1$ return Response.status(Status.UNAUTHORIZED).entity(loginResult).build(); } catch (StrolchException | PrivilegeException e) { logger.error(e.getMessage(), e); loginResult.setMsg(MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-1$ return Response.status(Status.FORBIDDEN).entity(loginResult).build(); } catch (Exception e) { logger.error(e.getMessage(), e); String msg = e.getMessage(); loginResult.setMsg(MessageFormat.format("{0}: {1}", e.getClass().getName(), msg)); //$NON-NLS-1$ return Response.serverError().entity(loginResult).build(); } } @DELETE @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("{authToken}") public Response logout(@PathParam("authToken") String authToken) { LogoutResult logoutResult = new LogoutResult(); try { StrolchSessionHandler sessionHandlerHandler = RestfulStrolchComponent.getInstance() .getComponent(StrolchSessionHandler.class); Certificate certificate = sessionHandlerHandler.validate(authToken); sessionHandlerHandler.invalidate(certificate); logoutResult.setUsername(certificate.getUsername()); logoutResult.setAuthToken(authToken); logoutResult.setMsg(MessageFormat.format("{0} has been logged out.", certificate.getUsername())); //$NON-NLS-1$ return Response.ok().entity(logoutResult).build(); } catch (StrolchException | PrivilegeException e) { logger.error(e.getMessage(), e); logoutResult.setMsg(MessageFormat.format("Could not logout due to: {0}", e.getMessage())); //$NON-NLS-1$ return Response.status(Status.UNAUTHORIZED).entity(logoutResult).build(); } catch (Exception e) { logger.error(e.getMessage(), e); String msg = e.getMessage(); logoutResult.setMsg(MessageFormat.format("{0}: {1}", e.getClass().getName(), msg)); //$NON-NLS-1$ return Response.serverError().entity(logoutResult).build(); } } @HEAD @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("{authToken}") public Response validateSessions(@PathParam("authToken") String authToken) { try { StrolchSessionHandler sessionHandlerHandler = RestfulStrolchComponent.getInstance() .getComponent(StrolchSessionHandler.class); sessionHandlerHandler.validate(authToken); return Response.ok().build(); } catch (StrolchException | PrivilegeException e) { logger.error(e.getMessage(), e); JsonObject root = new JsonObject(); root.addProperty("msg", MessageFormat.format("Session invalid: {0}", e.getMessage())); String json = new Gson().toJson(root); return Response.status(Status.UNAUTHORIZED).entity(json).build(); } catch (Exception e) { logger.error(e.getMessage(), e); String msg = e.getMessage(); JsonObject root = new JsonObject(); root.addProperty("msg", MessageFormat.format("Session invalid: {0}: {1}", e.getClass().getName(), msg)); String json = new Gson().toJson(root); return Response.serverError().entity(json).build(); } } }