/* * eXist Open Source Native XML Database * Copyright (C) 2001-2010 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ */ package org.exist.xquery.functions.xmldb; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.exist.EXistException; import org.exist.dom.QName; import org.exist.http.servlets.RequestWrapper; import org.exist.http.servlets.SessionWrapper; import org.exist.security.AuthenticationException; import org.exist.security.SecurityManager; import org.exist.security.Subject; import org.exist.storage.BrokerPool; import org.exist.xmldb.XmldbURI; import org.exist.xquery.*; import org.exist.xquery.functions.request.RequestModule; import org.exist.xquery.functions.session.SessionModule; import org.exist.xquery.value.BooleanValue; import org.exist.xquery.value.FunctionReturnSequenceType; import org.exist.xquery.value.FunctionParameterSequenceType; import org.exist.xquery.value.JavaObjectValue; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.SequenceType; import org.exist.xquery.value.Type; import org.xmldb.api.DatabaseManager; import org.xmldb.api.base.Collection; import org.xmldb.api.base.XMLDBException; /** * @author Wolfgang Meier (wolfgang@exist-db.org) * @author Andrzej Taramina (andrzej@chaeron.com) * @author ljo */ public class XMLDBAuthenticate extends UserSwitchingBasicFunction { private static final Logger logger = LogManager.getLogger(XMLDBAuthenticate.class); public final static FunctionSignature authenticateSignature = new FunctionSignature( new QName("authenticate", XMLDBModule.NAMESPACE_URI, XMLDBModule.PREFIX), "Check if the user, $user-id, can authenticate against the database collection $collection-uri. The function simply tries to " + "read the collection $collection-uri, using the credentials " + "$user-id and $password. " + XMLDBModule.COLLECTION_URI + " " + "It returns true if the authentication succeeds, false otherwise.", new SequenceType[]{ new FunctionParameterSequenceType("collection-uri", Type.STRING, Cardinality.EXACTLY_ONE, "The collection URI"), new FunctionParameterSequenceType("user-id", Type.STRING, Cardinality.ZERO_OR_ONE, "The user-id"), new FunctionParameterSequenceType("password", Type.STRING, Cardinality.ZERO_OR_ONE, "The password") }, new FunctionReturnSequenceType(Type.BOOLEAN, Cardinality.EXACTLY_ONE, "true() on successful authentication, false() otherwise") ); public final static FunctionSignature loginSignatures[] = { new FunctionSignature( new QName("login", XMLDBModule.NAMESPACE_URI, XMLDBModule.PREFIX), "Login the user, $user-id, and set it as the owner " + "of the currently executing XQuery. " + XMLDBModule.COLLECTION_URI + " " + "It returns true if the authentication succeeds, false otherwise. " + "If called from a HTTP context the login is cached for the " + "lifetime of the HTTP session and may be used for any XQuery " + "run in that session. " + "If an HTTP session does not already exist, none will be created.", new SequenceType[]{ new FunctionParameterSequenceType("collection-uri", Type.STRING, Cardinality.EXACTLY_ONE, "The collection URI"), new FunctionParameterSequenceType("user-id", Type.STRING, Cardinality.ZERO_OR_ONE, "The user-id"), new FunctionParameterSequenceType("password", Type.STRING, Cardinality.ZERO_OR_ONE, "The password") }, new FunctionReturnSequenceType(Type.BOOLEAN, Cardinality.EXACTLY_ONE, "true() on successful authentication and owner elevation, false() otherwise") ), new FunctionSignature( new QName("login", XMLDBModule.NAMESPACE_URI, XMLDBModule.PREFIX), "Login the user, $user-id, and set it as the owner " + "of the currently executing XQuery. " + XMLDBModule.COLLECTION_URI + " " + "It returns true() if the authentication succeeds, " + "false() otherwise. " + "If called from a HTTP context the login is cached for the " + "lifetime of the HTTP session and may be used for any XQuery" + "run in that session. " + "$create-session specifies whether to create an HTTP session on " + "successful authentication or not. " + "If $create-session is false() or the empty sequence no session " + "will be created if one does not already exist.", new SequenceType[]{ new FunctionParameterSequenceType("collection-uri", Type.STRING, Cardinality.EXACTLY_ONE, "The collection URI"), new FunctionParameterSequenceType("user-id", Type.STRING, Cardinality.ZERO_OR_ONE, "The user-id"), new FunctionParameterSequenceType("password", Type.STRING, Cardinality.ZERO_OR_ONE, "The password"), new FunctionParameterSequenceType("create-session", Type.BOOLEAN, Cardinality.ZERO_OR_ONE, "whether to create the session or not on successful authentication, default false()") }, new FunctionReturnSequenceType(Type.BOOLEAN, Cardinality.EXACTLY_ONE, "true() on successful authentication and owner elevation, false() otherwise") ) }; public XMLDBAuthenticate(final XQueryContext context, final FunctionSignature signature) { super(context, signature); } @Override public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException { if (args[1].isEmpty()) { return BooleanValue.FALSE; } final String uri = args[0].getStringValue(); final String userName = args[1].getStringValue(); if (userName == null) { logger.error("Unable to authenticate username == NULL"); return BooleanValue.FALSE; } final String password = args[2].getStringValue(); final boolean createSession = args.length > 3 && args[3].effectiveBooleanValue(); final XmldbURI targetColl; if (!uri.startsWith(XmldbURI.XMLDB_SCHEME + ':')) { targetColl = XmldbURI.EMBEDDED_SERVER_URI.resolveCollectionPath(XmldbURI.create(uri)); } else { targetColl = XmldbURI.create(uri); } try { final Subject user; try { final SecurityManager sm = BrokerPool.getInstance().getSecurityManager(); user = sm.authenticate(userName, password); } catch (final AuthenticationException | EXistException e) { logger.error("Unable to authenticate user: " + userName + " " + getLocation(), e); return BooleanValue.FALSE; } final Collection root = DatabaseManager.getCollection(targetColl.toString(), userName, password); if (root == null) { logger.error("Unable to authenticate user: target collection " + targetColl + " does not exist " + getLocation()); return BooleanValue.FALSE; } if (isCalledAs("login")) { //switch the user of the current broker switchUser(user); //if there is a http session cache the user in the http session cacheUserInHttpSession(user, createSession); } return BooleanValue.TRUE; } catch (final XMLDBException e) { logger.error(getLocation() + " : " + e.getMessage(), e); return BooleanValue.FALSE; } } private String getLocation() { return "@ " + getContext().getSource().path() + " [" + getLine() + ":" + getColumn() + "]"; } /** * If there is a HTTP Session, then this will store the user object in the session under the key * defined by XQueryContext.HTTP_SESSIONVAR_XMLDB_USER * * @param user The User to cache in the session * @param createSession Create session? */ private void cacheUserInHttpSession(final Subject user, final boolean createSession) throws XPathException { final Variable var = getSessionVar(createSession); if (var != null && var.getValue() != null) { if (var.getValue().getItemType() == Type.JAVA_OBJECT) { final JavaObjectValue session = (JavaObjectValue) var.getValue().itemAt(0); if (session.getObject() instanceof SessionWrapper) { ((SessionWrapper) session.getObject()).setAttribute(XQueryContext.HTTP_SESSIONVAR_XMLDB_USER, user); } } } } /** * Get the HTTP Session variable. Create it if requested and it doesn't exist. * * @param createSession Create session? */ private Variable getSessionVar(final boolean createSession) throws XPathException { final SessionModule sessionModule = (SessionModule) context.getModule(SessionModule.NAMESPACE_URI); Variable var = sessionModule.resolveVariable(SessionModule.SESSION_VAR); if (createSession && (var == null || var.getValue() == null)) { final RequestModule reqModule = (RequestModule) context.getModule(RequestModule.NAMESPACE_URI); // request object is read from global variable $request final Variable reqVar = reqModule.resolveVariable(RequestModule.REQUEST_VAR); if (reqVar == null || reqVar.getValue() == null) { logger.error("No request object found in the current XQuery context."); throw new XPathException(this, ErrorCodes.XPDY0002, "No request object found in the current XQuery context."); } if (reqVar.getValue().getItemType() != Type.JAVA_OBJECT) { logger.error("Variable $request is not bound to an Java object."); throw new XPathException(this, ErrorCodes.XPDY0002, "Variable $request is not bound to an Java object."); } final JavaObjectValue reqValue = (JavaObjectValue) reqVar.getValue().itemAt(0); if (reqValue.getObject() instanceof RequestWrapper) { final SessionWrapper session = ((RequestWrapper) reqValue.getObject()).getSession(true); var = sessionModule.declareVariable(SessionModule.SESSION_VAR, session); } } return var; } }