001/*
002 * (C) Copyright 2006-2016 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     * NOT PUBLIC, DO NOT CALL. Kept public for compatibility with old code.
119     * <p>
120     * Opens a {@link CoreSession} for the given context.
121     *
122     * @param repositoryName the repository name, or {@code null} for the default repository
123     * @param context the session open context
124     * @return the session
125     */
126    public static CoreSession openCoreSession(String repositoryName, Map<String, Serializable> context) {
127        return openCoreSession(repositoryName, getPrincipal(context));
128    }
129
130    /**
131     * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given principal.
132     * <p>
133     * The session must be closed using {@link CoreSession#close}.
134     *
135     * @param repositoryName the repository name, or {@code null} for the default repository
136     * @param principal the principal
137     * @return the session
138     * @since 5.9.3
139     */
140    public static CoreSession openCoreSession(String repositoryName, Principal principal) {
141        if (principal instanceof NuxeoPrincipal) {
142            return openCoreSession(repositoryName, (NuxeoPrincipal) principal);
143        } else {
144            return openCoreSession(repositoryName, getPrincipal(principal.getName()));
145        }
146    }
147
148    /**
149     * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given principal.
150     * <p>
151     * The session must be closed using {@link CoreSession#close}.
152     *
153     * @param repositoryName the repository name, or {@code null} for the default repository
154     * @param principal the principal
155     * @return the session
156     * @since 5.9.3
157     */
158    public static CoreSession openCoreSession(String repositoryName, NuxeoPrincipal principal) {
159        if (repositoryName == null) {
160            RepositoryManager repositoryManager = Framework.getService(RepositoryManager.class);
161            repositoryName = repositoryManager.getDefaultRepository().getName();
162        }
163        return Framework.getService(CoreSessionService.class).createCoreSession(repositoryName, principal);
164    }
165
166    /**
167     * Gets an existing open session for the given session id.
168     * <p>
169     * The returned CoreSession must not be closed, as it is owned by someone else.
170     *
171     * @param sessionId the session id
172     * @return the session, which must not be closed
173     */
174    public CoreSession getSession(String sessionId) {
175        return Framework.getService(CoreSessionService.class).getCoreSession(sessionId);
176    }
177
178    /**
179     * Use {@link CoreSession#close} instead.
180     *
181     * @since 5.9.3
182     */
183    public static void closeCoreSession(CoreSession session) {
184        Framework.getService(CoreSessionService.class).releaseCoreSession(session);
185    }
186
187    protected static NuxeoPrincipal getPrincipal(Map<String, Serializable> map) {
188        if (map == null) {
189            return getPrincipal((String) null); // logged-in principal
190        }
191        NuxeoPrincipal principal = (NuxeoPrincipal) map.get("principal");
192        if (principal == null) {
193            principal = getPrincipal((String) map.get("username"));
194        }
195        return principal;
196    }
197
198    protected static NuxeoPrincipal getPrincipal(String username) {
199        if (username != null) {
200            if (SYSTEM_USERNAME.equals(username)) {
201                return new SystemPrincipal(null);
202            } else {
203                return new UserPrincipal(username, new ArrayList<>(), false, false);
204            }
205        } else {
206            LoginStack.Entry entry = ClientLoginModule.getCurrentLogin();
207            if (entry != null) {
208                Principal p = entry.getPrincipal();
209                if (p instanceof NuxeoPrincipal) {
210                    return (NuxeoPrincipal) p;
211                } else if (LoginComponent.isSystemLogin(p)) {
212                    return new SystemPrincipal(p.getName());
213                } else {
214                    throw new RuntimeException("Unsupported principal: " + p.getClass());
215                }
216            } else {
217                if (Framework.isTestModeSet()) {
218                    return new SystemPrincipal(null);
219                } else {
220                    throw new NuxeoException(
221                            "Cannot create a CoreSession outside a security context, " + " login() missing.");
222                }
223            }
224        }
225    }
226
227    /**
228     * Gets the number of open sessions.
229     *
230     * @since 5.4.2
231     * @deprecated since 8.4, use {@link CoreSessionService#getNumberOfOpenCoreSessions()} directly
232     */
233    @Deprecated
234    public int getNumberOfSessions() {
235        return Framework.getService(CoreSessionService.class).getNumberOfOpenCoreSessions();
236    }
237
238    /**
239     * Gets the number of open sessions.
240     *
241     * @since 5.4.2
242     * @deprecated since 8.4, use {@link CoreSessionService#getCoreSessionRegistrationInfos()} directly
243     */
244    @Deprecated
245    public Collection<CoreSessionRegistrationInfo> getRegistrationInfos() {
246        return Framework.getService(CoreSessionService.class).getCoreSessionRegistrationInfos();
247    }
248
249    /**
250     * Gets the name of the currently logged-in principal.
251     *
252     * @return the principal name, or {@code null} if there was no login
253     * @since 8.4
254     */
255    protected static String getCurrentPrincipalName() {
256        Principal p = ClientLoginModule.getCurrentPrincipal();
257        return p == null ? null : p.getName();
258    }
259
260    /**
261     * Runs the given {@link Function} with a system {@link CoreSession} while logged in as a system user.
262     *
263     * @param repositoryName the repository name for the {@link CoreSession}
264     * @param function the function taking a system {@link CoreSession} and returning a result of type {@code <R>}
265     * @param <R> the function return type
266     * @return the result of the function
267     * @since 8.4
268     */
269    public static <R> R doPrivileged(String repositoryName, Function<CoreSession, R> function) {
270        MutableObject<R> result = new MutableObject<>();
271        new UnrestrictedSessionRunner(repositoryName, getCurrentPrincipalName()) {
272            @Override
273            public void run() {
274                result.setValue(function.apply(session));
275            }
276        }.runUnrestricted();
277        return result.getValue();
278    }
279
280    /**
281     * Runs the given {@link Function} with a system {@link CoreSession} while logged in as a system user.
282     *
283     * @param session an existing session
284     * @param function the function taking a system {@link CoreSession} and returning a result of type {@code <R>}
285     * @param <R> the function return type
286     * @return the result of the function
287     * @since 8.4
288     */
289    public static <R> R doPrivileged(CoreSession session, Function<CoreSession, R> function) {
290        MutableObject<R> result = new MutableObject<>();
291        new UnrestrictedSessionRunner(session) {
292            @Override
293            public void run() {
294                result.setValue(function.apply(session));
295            }
296        }.runUnrestricted();
297        return result.getValue();
298    }
299
300    /**
301     * Runs the given {@link Consumer} with a system {@link CoreSession} while logged in as a system user.
302     *
303     * @param repositoryName the repository name for the {@link CoreSession}
304     * @param consumer the consumer taking a system {@link CoreSession}
305     * @since 8.4
306     */
307    public static void doPrivileged(String repositoryName, Consumer<CoreSession> consumer) {
308        new UnrestrictedSessionRunner(repositoryName, getCurrentPrincipalName()) {
309            @Override
310            public void run() {
311                consumer.accept(session);
312            }
313        }.runUnrestricted();
314    }
315
316    /**
317     * Runs the given {@link Consumer} with a system {@link CoreSession} while logged in as a system user.
318     *
319     * @param session an existing session
320     * @param consumer the consumer taking a system {@link CoreSession}
321     * @since 8.4
322     */
323    public static void doPrivileged(CoreSession session, Consumer<CoreSession> consumer) {
324        new UnrestrictedSessionRunner(session) {
325            @Override
326            public void run() {
327                consumer.accept(session);
328            }
329        }.runUnrestricted();
330    }
331
332}