001/*
002 * (C) Copyright 2006-2014 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 *     Bogdan Stefanescu
018 *     Florent Guillaume
019 */
020package org.nuxeo.ecm.core.api;
021
022import static org.nuxeo.ecm.core.api.security.SecurityConstants.SYSTEM_USERNAME;
023
024import java.io.Serializable;
025import java.security.Principal;
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.Map;
029import java.util.function.Consumer;
030import java.util.function.Function;
031
032import org.apache.commons.lang3.mutable.MutableObject;
033import org.nuxeo.ecm.core.api.CoreSessionService.CoreSessionRegistrationInfo;
034import org.nuxeo.ecm.core.api.impl.UserPrincipal;
035import org.nuxeo.ecm.core.api.local.ClientLoginModule;
036import org.nuxeo.ecm.core.api.local.LoginStack;
037import org.nuxeo.ecm.core.api.repository.RepositoryManager;
038import org.nuxeo.ecm.core.api.security.SecurityConstants;
039import org.nuxeo.runtime.api.Framework;
040import org.nuxeo.runtime.api.login.LoginComponent;
041
042/**
043 * The CoreInstance is the main access point to a CoreSession.
044 */
045public class CoreInstance {
046
047    private static final CoreInstance INSTANCE = new CoreInstance();
048
049    private CoreInstance() {
050    }
051
052    /**
053     * Gets the CoreInstance singleton.
054     *
055     * @deprecated since 8.4, use {@link CoreSessionService} directly.
056     */
057    public static CoreInstance getInstance() {
058        return INSTANCE;
059    }
060
061    /**
062     * Opens a {@link CoreSession} for the currently logged-in user.
063     * <p>
064     * The session must be closed using {@link CoreSession#close}.
065     *
066     * @param repositoryName the repository name, or {@code null} for the default repository
067     * @return the session
068     * @since 5.9.3
069     */
070    public static CoreSession openCoreSession(String repositoryName) {
071        return openCoreSession(repositoryName, getPrincipal((String) null));
072    }
073
074    /**
075     * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given user.
076     * <p>
077     * The session must be closed using {@link CoreSession#close}.
078     *
079     * @param repositoryName the repository name, or {@code null} for the default repository
080     * @param username the user name
081     * @return the session
082     * @since 5.9.3
083     */
084    public static CoreSession openCoreSession(String repositoryName, String username) {
085        return openCoreSession(repositoryName, getPrincipal(username));
086    }
087
088    /**
089     * Opens a {@link CoreSession} for a system user.
090     * <p>
091     * The session must be closed using {@link CoreSession#close}.
092     *
093     * @param repositoryName the repository name, or {@code null} for the default repository
094     * @return the session
095     * @since 5.9.3
096     */
097    public static CoreSession openCoreSessionSystem(String repositoryName) {
098        return openCoreSession(repositoryName, getPrincipal((SecurityConstants.SYSTEM_USERNAME)));
099    }
100
101    /**
102     * Opens a {@link CoreSession} for a system user with an optional originating username.
103     * <p>
104     * The session must be closed using {@link CoreSession#close}.
105     *
106     * @param repositoryName the repository name, or {@code null} for the default repository
107     * @param originatingUsername the originating username to set on the SystemPrincipal
108     * @return the session
109     * @since 8.1
110     */
111    public static CoreSession openCoreSessionSystem(String repositoryName, String originatingUsername) {
112        NuxeoPrincipal principal = getPrincipal((SecurityConstants.SYSTEM_USERNAME));
113        principal.setOriginatingUser(originatingUsername);
114        return openCoreSession(repositoryName, principal);
115    }
116
117    /**
118     * @deprecated since 5.9.3, use {@link #openCoreSession} instead.
119     */
120    @Deprecated
121    public CoreSession open(String repositoryName, Map<String, Serializable> context) {
122        return openCoreSession(repositoryName, getPrincipal(context));
123    }
124
125    /**
126     * NOT PUBLIC, DO NOT CALL. Kept public for compatibility with old code.
127     * <p>
128     * Opens a {@link CoreSession} for the given context.
129     *
130     * @param repositoryName the repository name, or {@code null} for the default repository
131     * @param context the session open context
132     * @return the session
133     */
134    public static CoreSession openCoreSession(String repositoryName, Map<String, Serializable> context) {
135        return openCoreSession(repositoryName, getPrincipal(context));
136    }
137
138    /**
139     * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given principal.
140     * <p>
141     * The session must be closed using {@link CoreSession#close}.
142     *
143     * @param repositoryName the repository name, or {@code null} for the default repository
144     * @param principal the principal
145     * @return the session
146     * @since 5.9.3
147     */
148    public static CoreSession openCoreSession(String repositoryName, Principal principal) {
149        if (principal instanceof NuxeoPrincipal) {
150            return openCoreSession(repositoryName, (NuxeoPrincipal) principal);
151        } else {
152            return openCoreSession(repositoryName, getPrincipal(principal.getName()));
153        }
154    }
155
156    /**
157     * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given principal.
158     * <p>
159     * The session must be closed using {@link CoreSession#close}.
160     *
161     * @param repositoryName the repository name, or {@code null} for the default repository
162     * @param principal the principal
163     * @return the session
164     * @since 5.9.3
165     */
166    public static CoreSession openCoreSession(String repositoryName, NuxeoPrincipal principal) {
167        if (repositoryName == null) {
168            RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
169            repositoryName = repositoryManager.getDefaultRepository().getName();
170        }
171        return Framework.getService(CoreSessionService.class).createCoreSession(repositoryName, principal);
172    }
173
174    /**
175     * Gets an existing open session for the given session id.
176     * <p>
177     * The returned CoreSession must not be closed, as it is owned by someone else.
178     *
179     * @param sessionId the session id
180     * @return the session, which must not be closed
181     */
182    public CoreSession getSession(String sessionId) {
183        return Framework.getService(CoreSessionService.class).getCoreSession(sessionId);
184    }
185
186    /**
187     * Use {@link CoreSession#close} instead.
188     *
189     * @since 5.9.3
190     */
191    public static void closeCoreSession(CoreSession session) {
192        Framework.getService(CoreSessionService.class).releaseCoreSession(session);
193    }
194
195    protected static NuxeoPrincipal getPrincipal(Map<String, Serializable> map) {
196        if (map == null) {
197            return getPrincipal((String) null); // logged-in principal
198        }
199        NuxeoPrincipal principal = (NuxeoPrincipal) map.get("principal");
200        if (principal == null) {
201            principal = getPrincipal((String) map.get("username"));
202        }
203        return principal;
204    }
205
206    protected static NuxeoPrincipal getPrincipal(String username) {
207        if (username != null) {
208            if (SYSTEM_USERNAME.equals(username)) {
209                return new SystemPrincipal(null);
210            } else {
211                return new UserPrincipal(username, new ArrayList<String>(), false, false);
212            }
213        } else {
214            LoginStack.Entry entry = ClientLoginModule.getCurrentLogin();
215            if (entry != null) {
216                Principal p = entry.getPrincipal();
217                if (p instanceof NuxeoPrincipal) {
218                    return (NuxeoPrincipal) p;
219                } else if (LoginComponent.isSystemLogin(p)) {
220                    return new SystemPrincipal(p.getName());
221                } else {
222                    throw new RuntimeException("Unsupported principal: " + p.getClass());
223                }
224            } else {
225                if (Framework.isTestModeSet()) {
226                    return new SystemPrincipal(null);
227                } else {
228                    throw new NuxeoException("Cannot create a CoreSession outside a security context, "
229                            + " login() missing.");
230                }
231            }
232        }
233    }
234
235    /**
236     * @deprecated since 5.9.3, use {@link CoreSession#close} instead.
237     */
238    @Deprecated
239    public void close(CoreSession session) {
240        session.close(); // calls back closeCoreSession
241    }
242
243    /**
244     * Gets the number of open sessions.
245     *
246     * @since 5.4.2
247     * @deprecated since 8.4, use {@link CoreSessionService#getNumberOfOpenSessions} directly
248     */
249    public int getNumberOfSessions() {
250        return Framework.getService(CoreSessionService.class).getNumberOfOpenCoreSessions();
251    }
252
253    /**
254     * Gets the number of open sessions.
255     *
256     * @since 5.4.2
257     * @deprecated since 8.4, use {@link CoreSessionService#getRegistrationInfos} directly
258     */
259    public Collection<CoreSessionRegistrationInfo> getRegistrationInfos() {
260        return Framework.getService(CoreSessionService.class).getCoreSessionRegistrationInfos();
261    }
262
263    /**
264     * Gets the name of the currently logged-in principal.
265     *
266     * @return the principal name, or {@code null} if there was no login
267     * @since 8.4
268     */
269    protected static String getCurrentPrincipalName() {
270        Principal p = ClientLoginModule.getCurrentPrincipal();
271        return p == null ? null : p.getName();
272    }
273
274    /**
275     * Runs the given {@link Function} with a system {@link CoreSession} while logged in as a system user.
276     *
277     * @param repositoryName the repository name for the {@link CoreSession}
278     * @param function the function taking a system {@link CoreSession} and returning a result of type {@code <R>}
279     * @param <R> the function return type
280     * @return the result of the function
281     * @since 8.4
282     */
283    public static <R> R doPrivileged(String repositoryName, Function<CoreSession, R> function) {
284        MutableObject<R> result = new MutableObject<R>();
285        new UnrestrictedSessionRunner(repositoryName, getCurrentPrincipalName()) {
286            @Override
287            public void run() {
288                result.setValue(function.apply(session));
289            }
290        }.runUnrestricted();
291        return result.getValue();
292    }
293
294    /**
295     * Runs the given {@link Function} with a system {@link CoreSession} while logged in as a system user.
296     *
297     * @param session an existing session
298     * @param function the function taking a system {@link CoreSession} and returning a result of type {@code <R>}
299     * @param <R> the function return type
300     * @return the result of the function
301     * @since 8.4
302     */
303    public static <R> R doPrivileged(CoreSession session, Function<CoreSession, R> function) {
304        MutableObject<R> result = new MutableObject<R>();
305        new UnrestrictedSessionRunner(session) {
306            @Override
307            public void run() {
308                result.setValue(function.apply(session));
309            }
310        }.runUnrestricted();
311        return result.getValue();
312    }
313
314    /**
315     * Runs the given {@link Consumer} with a system {@link CoreSession} while logged in as a system user.
316     *
317     * @param repositoryName the repository name for the {@link CoreSession}
318     * @param consumer the consumer taking a system {@link CoreSession}
319     * @since 8.4
320     */
321    public static void doPrivileged(String repositoryName, Consumer<CoreSession> consumer) {
322        new UnrestrictedSessionRunner(repositoryName, getCurrentPrincipalName()) {
323            @Override
324            public void run() {
325                consumer.accept(session);
326            }
327        }.runUnrestricted();
328    }
329
330    /**
331     * Runs the given {@link Consumer} with a system {@link CoreSession} while logged in as a system user.
332     *
333     * @param session an existing session
334     * @param consumer the consumer taking a system {@link CoreSession}
335     * @since 8.4
336     */
337    public static void doPrivileged(CoreSession session, Consumer<CoreSession> consumer) {
338        new UnrestrictedSessionRunner(session) {
339            @Override
340            public void run() {
341                consumer.accept(session);
342            }
343        }.runUnrestricted();
344    }
345
346}