org.hippoecm.frontend.model.JcrSessionModel.java Source code

Java tutorial

Introduction

Here is the source code for org.hippoecm.frontend.model.JcrSessionModel.java

Source

/*
 *  Copyright 2008-2013 Hippo B.V. (http://www.onehippo.com)
 * 
 *  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 org.hippoecm.frontend.model;

import javax.jcr.LoginException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;

import org.apache.wicket.Application;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.flow.AbortWithHttpErrorCodeException;
import org.hippoecm.frontend.Main;
import org.hippoecm.hst.diagnosis.HDC;
import org.hippoecm.hst.diagnosis.Task;
import org.hippoecm.repository.HippoRepository;
import org.hippoecm.repository.api.HippoNodeType;
import org.hippoecm.repository.api.NodeNameCodec;
import org.onehippo.cms7.event.HippoEvent;
import org.onehippo.cms7.event.HippoEventConstants;
import org.onehippo.cms7.event.HippoSecurityEvent;
import org.onehippo.cms7.services.HippoServiceRegistry;
import org.onehippo.cms7.services.eventbus.HippoEventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Session model that uses the Main application to construct a JCR session.  When the model is attached, pending
 * changes are persisted.
 * <p/>
 * Plugins can subclass this model to refine the way the session is obtained.
 */
public class JcrSessionModel extends LoadableDetachableModel<Session> {

    // The jcr session is wrapped in a LoadableDetachableModel because it can't be serialized
    // and therefore cannot be a direct field of the wicket session. Wrapping the jcr session
    // like this has the added bonus of being a very simple reconnect mechanism.
    private static final long serialVersionUID = 1L;

    private static final Logger log = LoggerFactory.getLogger(JcrSessionModel.class);

    private UserCredentials credentials;
    private String remoteAddress;
    private boolean saveOnExit = true;
    private LoginException lastThrownLoginException;

    // although only written to, this instance variable is important : It serves to make sure
    // the transientModelObject jcr session gets logged out during serialization
    private TransientJcrSessionWrapper transientJcrSessionWrapper;

    public JcrSessionModel(UserCredentials credentials) {
        this.credentials = credentials;
        this.remoteAddress = ((ServletWebRequest) RequestCycle.get().getRequest()).getContainerRequest()
                .getRemoteAddr();
    }

    protected void flush() {
        Task flushTask = null;

        try {
            if (HDC.isStarted()) {
                flushTask = HDC.getCurrentTask().startSubtask("JcrSessionModel.flush");
            }

            Session session = getObject();
            if (session != null) {
                if (session.isLive()) {
                    if (saveOnExit) {
                        try {
                            session.save();
                        } catch (RepositoryException e) {
                            log.error("Failed to save session before logging out", e);
                        }
                    }
                    session.logout();
                    logHippoEvent(false, session.getUserID(), "logout", true);
                }
                transientJcrSessionWrapper = null;
                super.detach();
            }
        } finally {
            if (flushTask != null) {
                flushTask.stop();
            }
        }
    }

    public Session getSession() {
        try {
            Session session = getObject();
            if (session == null) {
                return null;
            }
            if (!session.isLive()) {
                detach();
            }
        } catch (Exception e) {
            detach();
        }
        // this will call load() only if detached
        return getObject();
    }

    @Override
    public void detach() {
        if (isAttached()) {
            flush();
        }
        super.detach();
    }

    @Override
    protected Session load() {
        Session session = null;
        boolean fatalError = false;
        try {
            if (credentials == null) {
                return null;
            }
            session = login(credentials);
            if (isSystemUser(session)) {
                logHippoEvent(true, credentials.getUsername(), "system user", false);
                return null;
            }
            logHippoEvent(true, credentials.getUsername(), "login successful", true);
        } catch (LoginException e) {
            logHippoEvent(true, credentials.getUsername(), "invalid credentials", false);
            lastThrownLoginException = e;
        } catch (RepositoryException e) {
            fatalError = true;
            log.error("Unable to obtain repository instance, aborting.", e);
        }

        if (fatalError) {
            // there's no sense in continuing
            throw new AbortWithHttpErrorCodeException(503, "Unable to load session");
        }
        transientJcrSessionWrapper = new TransientJcrSessionWrapper(session);
        return session;
    }

    public Session getSessionObject() throws LoginException {
        final Session session = getObject();

        try {
            if (lastThrownLoginException != null) {
                throw lastThrownLoginException;
            }
        } finally {
            // Clear the login exception for subsequent calls
            lastThrownLoginException = null;
        }

        return session;
    }

    public static Session login(UserCredentials credentials) throws LoginException, RepositoryException {
        Main main = (Main) Application.get();
        HippoRepository repository = main.getRepository();
        return repository.getRepository().login(credentials.getJcrCredentials());
    }

    private void logHippoEvent(final boolean login, final String user, final String message, boolean success) {
        final HippoEventBus eventBus = HippoServiceRegistry.getService(HippoEventBus.class);

        if (eventBus != null) {
            Task logEventTask = null;

            try {
                if (HDC.isStarted()) {
                    logEventTask = HDC.getCurrentTask().startSubtask("JcrSessionModel.logHippoEvent");
                }

                final String action = login ? "login" : "logout";
                final HippoEvent event = new HippoSecurityEvent("cms").success(success).action(action)
                        .category(HippoEventConstants.CATEGORY_SECURITY).user(user)
                        .set("remoteAddress", getRemoteAddr()).message(message);
                event.sealEvent();
                eventBus.post(event);
            } finally {
                if (logEventTask != null) {
                    logEventTask.stop();
                }
            }
        }
    }

    protected boolean isSystemUser(Session session) {
        try {
            Node userNode = getUserNode(session);
            if (userNode != null && userNode.hasProperty("hipposys:system")) {
                return userNode.getProperty("hipposys:system").getBoolean();
            }
        } catch (RepositoryException e) {
            log.warn("Unable to determine if user is a system user: {}", e.getMessage());
            log.debug("Error while determining system user status:", e);
        }
        return false;
    }

    protected Node getUserNode(Session session) throws RepositoryException {
        String userId = session.getUserID();
        StringBuilder statement = new StringBuilder();
        statement.append("//element");
        statement.append("(*, ").append(HippoNodeType.NT_USER).append(")");
        statement.append('[').append("fn:name() = ").append("'").append(NodeNameCodec.encode(userId, true))
                .append("'").append(']');
        Query q;
        q = session.getWorkspace().getQueryManager().createQuery(statement.toString(), Query.XPATH);
        QueryResult result = q.execute();
        if (result.getNodes().hasNext()) {
            return result.getNodes().nextNode();
        }

        return null;
    }

    /**
     * Helper method for logging
     *
     * @return ip address of client
     */
    private String getRemoteAddr() {
        return remoteAddress;
    }

    public void setSaveOnExit(final boolean saveOnExit) {
        this.saveOnExit = saveOnExit;
    }
}