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.nuxeo.ecm.core.api.security.SecurityConstants;
028import org.nuxeo.runtime.api.Framework;
029
030/**
031 * Helper class to run code with an unrestricted session.
032 * <p>
033 * The caller must implement the {@link #run} method, and call {@link #runUnrestricted}.
034 *
035 * @author Florent Guillaume
036 */
037public abstract class UnrestrictedSessionRunner {
038
039    protected String originatingUsername;
040
041    protected CoreSession session;
042
043    protected final boolean sessionIsAlreadyUnrestricted;
044
045    protected final String repositoryName;
046
047    /** True if a call to {@link #runUnrestricted} is in progress. */
048    public boolean isUnrestricted;
049
050    /**
051     * Constructs a {@link UnrestrictedSessionRunner} given an existing session (which may or may not be already
052     * unrestricted).
053     * <p>
054     * Originating user is taken on given session.
055     *
056     * @param session the available session
057     */
058    protected UnrestrictedSessionRunner(CoreSession session) {
059        this.session = session;
060        sessionIsAlreadyUnrestricted = isUnrestricted(session);
061        if (sessionIsAlreadyUnrestricted) {
062            repositoryName = null;
063        } else {
064            repositoryName = session.getRepositoryName();
065        }
066        Principal pal = session.getPrincipal();
067        if (pal != null) {
068            originatingUsername = pal.getName();
069        }
070    }
071
072    /**
073     * Constructs a {@link UnrestrictedSessionRunner} given a repository name.
074     *
075     * @param repositoryName the repository name
076     */
077    protected UnrestrictedSessionRunner(String repositoryName) {
078        session = null;
079        sessionIsAlreadyUnrestricted = false;
080        this.repositoryName = repositoryName;
081    }
082
083    /**
084     * Constructs a {@link UnrestrictedSessionRunner} given a repository name and an originating user name.
085     *
086     * @param repositoryName the repository name
087     * @param originatingUser the user name behind the system user
088     */
089    protected UnrestrictedSessionRunner(String repositoryName, String originatingUser) {
090        session = null;
091        sessionIsAlreadyUnrestricted = false;
092        this.repositoryName = repositoryName;
093        this.originatingUsername = originatingUser;
094    }
095
096    public String getOriginatingUsername() {
097        return originatingUsername;
098    }
099
100    public void setOriginatingUsername(String originatingUsername) {
101        this.originatingUsername = originatingUsername;
102    }
103
104    protected boolean isUnrestricted(CoreSession session) {
105        return SecurityConstants.SYSTEM_USERNAME.equals(session.getPrincipal().getName())
106                || (session.getPrincipal() instanceof NuxeoPrincipal && ((NuxeoPrincipal) 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                if (baseSession != null && !baseSession.isStateSharedByAllThreadSessions()) {
130                    // save base session state for unrestricted one
131                    baseSession.save();
132                }
133                session = CoreInstance.openCoreSession(repositoryName);
134                if (loginContext == null && Framework.isTestModeSet()) {
135                    NuxeoPrincipal principal = (NuxeoPrincipal) session.getPrincipal();
136                    if (principal instanceof SystemPrincipal) {
137                        // we are in a test that is not using authentication
138                        // =>
139                        // we're not stacking the originating user in the
140                        // authentication stack
141                        // so we're setting manually now
142                        principal.setOriginatingUser(originatingUsername);
143                    }
144                }
145                try {
146                    run();
147                } finally {
148                    try {
149                        if (!session.isStateSharedByAllThreadSessions()) {
150                            // save unrestricted state for base session
151                            session.save();
152                        }
153                        session.close();
154                    } finally {
155                        if (baseSession != null && !baseSession.isStateSharedByAllThreadSessions()) {
156                            // process invalidations from unrestricted session
157                            baseSession.save();
158                        }
159                        session = baseSession;
160                    }
161                }
162            } finally {
163                try {
164                    // loginContext may be null in tests
165                    if (loginContext != null) {
166                        loginContext.logout();
167                    }
168                } catch (LoginException e) {
169                    throw new NuxeoException(e);
170                }
171            }
172        } finally {
173            isUnrestricted = false;
174            if (Framework.isTestModeSet() && sessionIsAlreadyUnrestricted) {
175                session.save();
176            }
177        }
178    }
179
180    /**
181     * This method will be called by {@link #runUnrestricted()} with {@link #session} available as an unrestricted
182     * session.
183     * <p>
184     * It can also be called directly in which case the {@link #session} available will be the one passed to
185     * {@code #UnrestrictedSessionRunner(CoreSession)}.
186     */
187    public abstract void run();
188
189}