001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019
020package org.nuxeo.ecm.core.api;
021
022import javax.security.auth.login.LoginContext;
023import javax.security.auth.login.LoginException;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.nuxeo.runtime.api.Framework;
028
029/**
030 * Helper class to run code with an unrestricted session.
031 * <p>
032 * The caller must implement the {@link #run} method, and call {@link #runUnrestricted}.
033 *
034 * @author Florent Guillaume
035 */
036public abstract class UnrestrictedSessionRunner {
037
038    private static final Log log = LogFactory.getLog(UnrestrictedSessionRunner.class);
039
040    protected String originatingUsername;
041
042    protected CoreSession session;
043
044    protected final boolean sessionIsAlreadyUnrestricted;
045
046    protected final String repositoryName;
047
048    /** True if a call to {@link #runUnrestricted} is in progress. */
049    protected boolean isUnrestricted;
050
051    /**
052     * Constructs a {@link UnrestrictedSessionRunner} given an existing session (which may or may not be already
053     * unrestricted).
054     * <p>
055     * Originating user is taken on given session.
056     *
057     * @param session the available session
058     */
059    protected UnrestrictedSessionRunner(CoreSession session) {
060        this.session = session;
061        sessionIsAlreadyUnrestricted = checkUnrestricted(session);
062        if (sessionIsAlreadyUnrestricted) {
063            repositoryName = null;
064        } else {
065            repositoryName = session.getRepositoryName();
066        }
067        NuxeoPrincipal pal = session.getPrincipal();
068        if (pal != null) {
069            originatingUsername = pal.getName();
070        }
071    }
072
073    /**
074     * Constructs a {@link UnrestrictedSessionRunner} given a repository name.
075     *
076     * @param repositoryName the repository name
077     */
078    protected UnrestrictedSessionRunner(String repositoryName) {
079        session = null;
080        sessionIsAlreadyUnrestricted = false;
081        this.repositoryName = repositoryName;
082    }
083
084    /**
085     * Constructs a {@link UnrestrictedSessionRunner} given a repository name and an originating user name.
086     *
087     * @param repositoryName the repository name
088     * @param originatingUser the user name behind the system user
089     */
090    protected UnrestrictedSessionRunner(String repositoryName, String originatingUser) {
091        session = null;
092        sessionIsAlreadyUnrestricted = false;
093        this.repositoryName = repositoryName;
094        originatingUsername = originatingUser;
095    }
096
097    public String getOriginatingUsername() {
098        return originatingUsername;
099    }
100
101    public void setOriginatingUsername(String originatingUsername) {
102        this.originatingUsername = originatingUsername;
103    }
104
105    protected boolean checkUnrestricted(CoreSession session) {
106        return session.getPrincipal().isAdministrator();
107    }
108
109    /**
110     * Calls the {@link #run()} method with an unrestricted {@link #session}. During this call, {@link #isUnrestricted}
111     * is set to {@code true}.
112     */
113    public void runUnrestricted() {
114        isUnrestricted = true;
115        try {
116            if (sessionIsAlreadyUnrestricted) {
117                run();
118                return;
119            }
120
121            LoginContext loginContext;
122            try {
123                loginContext = Framework.loginAs(originatingUsername);
124            } catch (LoginException e) {
125                throw new NuxeoException(e);
126            }
127            try {
128                CoreSession baseSession = session;
129                try (CloseableCoreSession closeableCoreSession = CoreInstance.openCoreSession(repositoryName)) {
130                    session = closeableCoreSession;
131                    run();
132                } finally {
133                    session = baseSession;
134                }
135            } finally {
136                try {
137                    // loginContext may be null in tests
138                    if (loginContext != null) {
139                        loginContext.logout();
140                    }
141                } catch (LoginException e) {
142                    log.error(e); // don't rethrow inside finally
143                }
144            }
145        } finally {
146            isUnrestricted = false;
147        }
148    }
149
150    /**
151     * This method will be called by {@link #runUnrestricted()} with {@link #session} available as an unrestricted
152     * session.
153     * <p>
154     * It can also be called directly in which case the {@link #session} available will be the one passed to
155     * {@code #UnrestrictedSessionRunner(CoreSession)}.
156     */
157    public abstract void run();
158
159}