
org.structr.web.auth.UiAuthenticator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of structr-ui Show documentation
Show all versions of structr-ui Show documentation
Structr is an open source framework based on the popular Neo4j graph database.
The newest version!
/**
* Copyright (C) 2010-2016 Structr GmbH
*
* This file is part of Structr .
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Structr. If not, see .
*/
package org.structr.web.auth;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.server.session.HashSessionManager;
import org.structr.common.AccessMode;
import org.structr.common.PathHelper;
import org.structr.common.SecurityContext;
import org.structr.common.error.FrameworkException;
import org.structr.core.Services;
import org.structr.core.app.StructrApp;
import org.structr.core.auth.Authenticator;
import org.structr.core.auth.exception.AuthenticationException;
import org.structr.core.auth.exception.UnauthorizedException;
import org.structr.core.entity.AbstractNode;
import org.structr.core.entity.Person;
import org.structr.core.entity.Principal;
import org.structr.core.entity.ResourceAccess;
import org.structr.core.entity.SuperUser;
import org.structr.core.property.PropertyKey;
import org.structr.rest.auth.AuthHelper;
import org.structr.rest.auth.SessionHelper;
import org.structr.rest.service.HttpService;
import org.structr.web.entity.User;
import org.structr.web.resource.RegistrationResource;
import org.structr.web.servlet.HtmlServlet;
//~--- classes ----------------------------------------------------------------
/**
*
*
*/
public class UiAuthenticator implements Authenticator {
private static final Logger logger = Logger.getLogger(UiAuthenticator.class.getName());
protected boolean examined = false;
protected static boolean userAutoCreate;
protected static boolean userAutoLogin;
private static Class userClass;
private enum Method { GET, PUT, POST, DELETE, HEAD, OPTIONS }
private static final Map methods = new LinkedHashMap();
// HTTP methods
static {
methods.put("GET", Method.GET);
methods.put("PUT", Method.PUT);
methods.put("POST", Method.POST);
methods.put("HEAD", Method.HEAD);
methods.put("DELETE", Method.DELETE);
methods.put("OPTIONS", Method.OPTIONS);
}
// access flags
public static final long FORBIDDEN = 0;
public static final long AUTH_USER_GET = 1;
public static final long AUTH_USER_PUT = 2;
public static final long AUTH_USER_POST = 4;
public static final long AUTH_USER_DELETE = 8;
public static final long NON_AUTH_USER_GET = 16;
public static final long NON_AUTH_USER_PUT = 32;
public static final long NON_AUTH_USER_POST = 64;
public static final long NON_AUTH_USER_DELETE = 128;
public static final long AUTH_USER_OPTIONS = 256;
public static final long NON_AUTH_USER_OPTIONS = 512;
public static final long AUTH_USER_HEAD = 1024;
public static final long NON_AUTH_USER_HEAD = 2048;
//~--- methods --------------------------------------------------------
/**
* Examine request and try to find a user.
*
* First, check session id, then try external (OAuth) authentication,
* finally, check standard login by credentials.
*
* @param request
* @param response
* @return security context
* @throws FrameworkException
*/
@Override
public SecurityContext initializeAndExamineRequest(final HttpServletRequest request, final HttpServletResponse response) throws FrameworkException {
// Initialize custom user class
getUserClass();
SecurityContext securityContext;
Principal user = SessionHelper.checkSessionAuthentication(request);
if (user == null) {
user = checkExternalAuthentication(request, response);
}
if (user == null) {
user = getUser(request, true);
}
if (user == null) {
// If no user could be determined, assume frontend access
securityContext = SecurityContext.getInstance(user, request, AccessMode.Frontend);
} else {
if (user instanceof SuperUser) {
securityContext = SecurityContext.getSuperUserInstance(request);
} else {
securityContext = SecurityContext.getInstance(user, request, AccessMode.Backend);
}
}
securityContext.setAuthenticator(this);
// Check CORS settings (Cross-origin resource sharing, see http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
final String origin = request.getHeader("Origin");
if (!StringUtils.isBlank(origin)) {
final Services services = Services.getInstance();
response.setHeader("Access-Control-Allow-Origin", origin);
// allow cross site resource sharing (read only)
final String maxAge = services.getConfigurationValue(Services.ACCESS_CONTROL_MAX_AGE);
if (StringUtils.isNotBlank(maxAge)) {
response.setHeader("Access-Control-MaxAge", maxAge);
}
final String allowMethods = services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_METHODS);
if (StringUtils.isNotBlank(allowMethods)) {
response.setHeader("Access-Control-Allow-Methods", allowMethods);
}
final String allowHeaders = services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_HEADERS);
if (StringUtils.isNotBlank(allowHeaders)) {
response.setHeader("Access-Control-Allow-Headers", allowHeaders);
}
final String allowCredentials = services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_CREDENTIALS);
if (StringUtils.isNotBlank(allowCredentials)) {
response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
}
final String exposeHeaders = services.getConfigurationValue(Services.ACCESS_CONTROL_EXPOSE_HEADERS);
if (StringUtils.isNotBlank(exposeHeaders)) {
response.setHeader("Access-Control-Expose-Headers", exposeHeaders);
}
}
examined = true;
// store a reference of the response object in SecurityContext
// to be able to stream data directly from builtin functions
securityContext.setResponse(response);
return securityContext;
}
@Override
public boolean hasExaminedRequest() {
return examined;
}
@Override
public void setUserAutoCreate(final boolean userAutoCreate) {
UiAuthenticator.userAutoCreate = userAutoCreate;
}
@Override
public void setUserAutoLogin(boolean userAutoLogin) {
UiAuthenticator.userAutoLogin = userAutoLogin;
}
@Override
public void checkResourceAccess(final SecurityContext securityContext, final HttpServletRequest request, final String rawResourceSignature, final String propertyView)
throws FrameworkException {
final ResourceAccess resourceAccess = ResourceAccess.findGrant(securityContext, rawResourceSignature);
final Method method = methods.get(request.getMethod());
final Principal user = securityContext.getUser(false);
final boolean validUser = (user != null);
// super user is always authenticated
if (validUser && (user instanceof SuperUser || user.getProperty(Principal.isAdmin))) {
return;
}
// no grants => no access rights
if (resourceAccess == null) {
logger.log(Level.INFO, "No resource access grant found for signature {0}. (URI: {1})", new Object[] { rawResourceSignature, securityContext.getCompoundRequestURI() } );
throw new UnauthorizedException("Forbidden");
} else {
switch (method) {
case GET :
if (!validUser && resourceAccess.hasFlag(NON_AUTH_USER_GET)) {
return;
}
if (validUser && resourceAccess.hasFlag(AUTH_USER_GET)) {
return;
}
break;
case PUT :
if (!validUser && resourceAccess.hasFlag(NON_AUTH_USER_PUT)) {
return;
}
if (validUser && resourceAccess.hasFlag(AUTH_USER_PUT)) {
return;
}
break;
case POST :
if (!validUser && resourceAccess.hasFlag(NON_AUTH_USER_POST)) {
return;
}
if (validUser && resourceAccess.hasFlag(AUTH_USER_POST)) {
return;
}
break;
case DELETE :
if (!validUser && resourceAccess.hasFlag(NON_AUTH_USER_DELETE)) {
return;
}
if (validUser && resourceAccess.hasFlag(AUTH_USER_DELETE)) {
return;
}
break;
case OPTIONS :
if (!validUser && resourceAccess.hasFlag(NON_AUTH_USER_OPTIONS)) {
return;
}
if (validUser && resourceAccess.hasFlag(AUTH_USER_OPTIONS)) {
return;
}
break;
case HEAD :
if (!validUser && resourceAccess.hasFlag(NON_AUTH_USER_HEAD)) {
return;
}
if (validUser && resourceAccess.hasFlag(AUTH_USER_HEAD)) {
return;
}
break;
}
}
logger.log(Level.INFO, "Resource access grant found for signature {0}, but method {1} not allowed for {2}.", new Object[] { rawResourceSignature, method, validUser ? "authenticated users" : "public users" } );
throw new UnauthorizedException("Forbidden");
}
@Override
public Principal doLogin(final HttpServletRequest request, final String emailOrUsername, final String password) throws AuthenticationException, FrameworkException {
final Principal user = AuthHelper.getPrincipalForPassword(Person.eMail, emailOrUsername, password);
if (user != null) {
final String allowLoginBeforeConfirmation = Services.getInstance().getConfigurationValue(RegistrationResource.ALLOW_LOGIN_BEFORE_CONFIRMATION);
if (user.getProperty(User.confirmationKey) != null && Boolean.FALSE.equals(Boolean.parseBoolean(allowLoginBeforeConfirmation))) {
logger.log(Level.WARNING, "Login as {0} not allowed before confirmation.", user);
throw new AuthenticationException(AuthHelper.STANDARD_ERROR_MSG);
}
AuthHelper.doLogin(request, user);
}
return user;
}
@Override
public void doLogout(final HttpServletRequest request) {
try {
final Principal user = getUser(request, false);
if (user != null) {
AuthHelper.doLogout(request, user);
}
final HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
} catch (IllegalStateException | FrameworkException ex) {
logger.log(Level.WARNING, "Error while logging out user", ex);
}
}
/**
* This method checks all configured external authentication services.
*
* @param request
* @param response
* @return user
*/
protected static Principal checkExternalAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws FrameworkException {
final String path = PathHelper.clean(request.getPathInfo());
final String[] uriParts = PathHelper.getParts(path);
logger.log(Level.FINE, "Checking external authentication ...");
if (uriParts == null || uriParts.length != 3 || !("oauth".equals(uriParts[0]))) {
logger.log(Level.FINE, "Incorrect URI parts for OAuth process, need /oauth//");
return null;
}
final String name = uriParts[1];
final String action = uriParts[2];
// Try to getValue an OAuth2 server for the given name
final StructrOAuthClient oauthServer = StructrOAuthClient.getServer(name);
if (oauthServer == null) {
logger.log(Level.FINE, "No OAuth2 authentication server configured for {0}", path);
return null;
}
if ("login".equals(action)) {
try {
response.sendRedirect(oauthServer.getEndUserAuthorizationRequestUri(request));
return null;
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not send redirect to authorization server", ex);
}
} else if ("auth".equals(action)) {
final String accessToken = oauthServer.getAccessToken(request);
final SecurityContext superUserContext = SecurityContext.getSuperUserInstance();
if (accessToken != null) {
logger.log(Level.FINE, "Got access token {0}", accessToken);
//securityContext.setAttribute("OAuthAccessToken", accessToken);
String value = oauthServer.getCredential(request);
logger.log(Level.FINE, "Got credential value: {0}", new Object[] { value });
if (value != null) {
PropertyKey credentialKey = oauthServer.getCredentialKey();
Principal user = AuthHelper.getPrincipalForCredential(credentialKey, value);
if (user == null && userAutoCreate) {
user = RegistrationResource.createUser(superUserContext, credentialKey, value, true, userClass);
}
if (user != null) {
AuthHelper.doLogin(request, user);
HtmlServlet.setNoCacheHeaders(response);
try {
logger.log(Level.FINE, "Response status: {0}", response.getStatus());
response.sendRedirect(oauthServer.getReturnUri());
} catch (IOException ex) {
logger.log(Level.SEVERE, "Could not redirect to {0}: {1}", new Object[]{oauthServer.getReturnUri(), ex});
}
return user;
}
}
}
}
try {
response.sendRedirect(oauthServer.getErrorUri());
} catch (IOException ex) {
logger.log(Level.SEVERE, "Could not redirect to {0}: {1}", new Object[]{ oauthServer.getReturnUri(), ex });
}
return null;
}
public static void writeUnauthorized(final HttpServletResponse response) throws IOException {
response.setHeader("WWW-Authenticate", "BASIC realm=\"Restricted Access\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
public static void writeNotFound(final HttpServletResponse response) throws IOException {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
public static void writeInternalServerError(final HttpServletResponse response) {
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (IOException ignore) {}
}
@Override
public boolean getUserAutoCreate() {
return userAutoCreate;
}
@Override
public boolean getUserAutoLogin() {
return userAutoLogin;
}
@Override
public Class getUserClass() {
if (userClass == null) {
String configuredCustomClassName = StructrApp.getConfigurationValue("Registration.customUserClass");
if (StringUtils.isEmpty(configuredCustomClassName)) {
configuredCustomClassName = User.class.getSimpleName();
}
userClass = StructrApp.getConfiguration().getNodeEntityClass(configuredCustomClassName);
}
return userClass;
}
@Override
public Principal getUser(final HttpServletRequest request, final boolean tryLogin) throws FrameworkException {
Principal user = null;
if (request.getAttribute(SessionHelper.SESSION_IS_NEW) != null) {
// First, check session (JSESSIONID cookie)
final HttpSession session = request.getSession(false);
if (session != null) {
user = AuthHelper.getPrincipalForSessionId(session.getId());
}
}
if (user == null) {
// Second, check X-Headers
String userName = request.getHeader("X-User");
String password = request.getHeader("X-Password");
String token = request.getHeader("X-StructrSessionToken");
// Try to authorize with a session token first
if (token != null) {
user = AuthHelper.getPrincipalForSessionId(token);
} else if ((userName != null) && (password != null)) {
if (tryLogin) {
user = AuthHelper.getPrincipalForPassword(AbstractNode.name, userName, password);
}
}
}
return user;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy