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 java.security.Principal;
023
024import javax.security.auth.login.LoginContext;
025import javax.security.auth.login.LoginException;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.security.SecurityConstants;
030import org.nuxeo.runtime.api.Framework;
031
032/**
033 * Helper class to run code with an unrestricted session.
034 * <p>
035 * The caller must implement the {@link #run} method, and call {@link #runUnrestricted}.
036 *
037 * @author Florent Guillaume
038 */
039public abstract class UnrestrictedSessionRunner {
040
041    private static final Log log = LogFactory.getLog(UnrestrictedSessionRunner.class);
042
043    protected String originatingUsername;
044
045    protected CoreSession session;
046
047    protected final boolean sessionIsAlreadyUnrestricted;
048
049    protected final String repositoryName;
050
051    /** True if a call to {@link #runUnrestricted} is in progress. */
052    protected boolean isUnrestricted;
053
054    /**
055     * Constructs a {@link UnrestrictedSessionRunner} given an existing session (which may or may not be already
056     * unrestricted).
057     * <p>
058     * Originating user is taken on given session.
059     *
060     * @param session the available session
061     */
062    protected UnrestrictedSessionRunner(CoreSession session) {
063        this.session = session;
064        sessionIsAlreadyUnrestricted = checkUnrestricted(session);
065        if (sessionIsAlreadyUnrestricted) {
066            repositoryName = null;
067        } else {
068            repositoryName = session.getRepositoryName();
069        }
070        Principal pal = session.getPrincipal();
071        if (pal != null) {
072            originatingUsername = pal.getName();
073        }
074    }
075
076    /**
077     * Constructs a {@link UnrestrictedSessionRunner} given a repository name.
078     *
079     * @param repositoryName the repository name
080     */
081    protected UnrestrictedSessionRunner(String repositoryName) {
082        session = null;
083        sessionIsAlreadyUnrestricted = false;
084        this.repositoryName = repositoryName;
085    }
086
087    /**
088     * Constructs a {@link UnrestrictedSessionRunner} given a repository name and an originating user name.
089     *
090     * @param repositoryName the repository name
091     * @param originatingUser the user name behind the system user
092     */
093    protected UnrestrictedSessionRunner(String repositoryName, String originatingUser) {
094        session = null;
095        sessionIsAlreadyUnrestricted = false;
096        this.repositoryName = repositoryName;
097        originatingUsername = originatingUser;
098    }
099
100    public String getOriginatingUsername() {
101        return originatingUsername;
102    }
103
104    public void setOriginatingUsername(String originatingUsername) {
105        this.originatingUsername = originatingUsername;
106    }
107
108    protected boolean checkUnrestricted(CoreSession session) {
109        return SecurityConstants.SYSTEM_USERNAME.equals(session.getPrincipal().getName())
110                || (session.getPrincipal() instanceof NuxeoPrincipal && ((NuxeoPrincipal) session.getPrincipal()).isAdministrator());
111    }
112
113    /**
114     * Calls the {@link #run()} method with an unrestricted {@link #session}. During this call, {@link #isUnrestricted}
115     * is set to {@code true}.
116     */
117    public void runUnrestricted() {
118        isUnrestricted = true;
119        try {
120            if (sessionIsAlreadyUnrestricted) {
121                run();
122                return;
123            }
124
125            LoginContext loginContext;
126            try {
127                loginContext = Framework.loginAs(originatingUsername);
128            } catch (LoginException e) {
129                throw new NuxeoException(e);
130            }
131            try {
132                CoreSession baseSession = session;
133                try (CloseableCoreSession closeableCoreSession = CoreInstance.openCoreSession(repositoryName)) {
134                    session = closeableCoreSession;
135                    run();
136                } finally {
137                    session = baseSession;
138                }
139            } finally {
140                try {
141                    // loginContext may be null in tests
142                    if (loginContext != null) {
143                        loginContext.logout();
144                    }
145                } catch (LoginException e) {
146                    log.error(e); // don't rethrow inside finally
147                }
148            }
149        } finally {
150            isUnrestricted = false;
151        }
152    }
153
154    /**
155     * This method will be called by {@link #runUnrestricted()} with {@link #session} available as an unrestricted
156     * session.
157     * <p>
158     * It can also be called directly in which case the {@link #session} available will be the one passed to
159     * {@code #UnrestrictedSessionRunner(CoreSession)}.
160     */
161    public abstract void run();
162
163}