001/*
002 * (C) Copyright 2019 Nuxeo (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 */
019package org.nuxeo.runtime.api.login;
020
021import java.lang.reflect.Constructor;
022import java.security.Principal;
023import java.util.Collections;
024
025import javax.security.auth.Subject;
026import javax.security.auth.login.LoginContext;
027import javax.security.auth.login.LoginException;
028
029import sun.reflect.ReflectionFactory;
030
031/**
032 * An implementation of {@link LoginContext} just holding a principal.
033 * <p>
034 * Construction is done through the static {@link #create} method, which takes a {@link Principal}. The caller must push
035 * the principal on the Nuxeo principal stack using {@link #login}, and finally do {@link #logout} or {@link #close} to
036 * remove it.
037 * <p>
038 * This is used for compatibility with previous code that expected to receive a {@link LoginContext} and then call
039 * {@link #logout} on it.
040 *
041 * @since 11.1
042 */
043public class NuxeoLoginContext extends LoginContext implements AutoCloseable {
044
045    protected Principal principal;
046
047    protected Subject subject;
048
049    protected boolean loggedIn;
050
051    /**
052     * This constructor cannot be used, use the static method {@link #create} instead.
053     */
054    public NuxeoLoginContext() throws LoginException {
055        super(error());
056    }
057
058    private static String error() {
059        throw new UnsupportedOperationException("must use create() method");
060    }
061
062    // constructs the class without calling its declared constructor or its super's constructor
063    // we need to do this because LoginContext construction is very costly
064    private static final Constructor<?> BARE_CONSTRUCTOR;
065    static {
066        try {
067            Class<?> klass = NuxeoLoginContext.class;
068            BARE_CONSTRUCTOR = ReflectionFactory.getReflectionFactory()
069                                                .newConstructorForSerialization(klass,
070                                                        Object.class.getDeclaredConstructor());
071        } catch (ReflectiveOperationException e) {
072            throw new ExceptionInInitializerError(e);
073        }
074    }
075
076    /**
077     * Creates a {@link NuxeoLoginContext} for the given principal.
078     *
079     * @param principal the principal
080     * @return the login context
081     */
082    public static NuxeoLoginContext create(Principal principal) {
083        NuxeoLoginContext loginContext;
084        try {
085            loginContext = (NuxeoLoginContext) BARE_CONSTRUCTOR.newInstance();
086        } catch (ReflectiveOperationException | IllegalArgumentException e) {
087            throw new RuntimeException(e);
088        }
089        loginContext.setPrincipal(principal);
090        return loginContext;
091    }
092
093    protected void setPrincipal(Principal principal) {
094        this.principal = principal;
095        this.subject = new Subject(true, Collections.singleton(principal), Collections.emptySet(),
096                Collections.emptySet());
097    }
098
099    @Override
100    public Subject getSubject() {
101        return subject;
102    }
103
104    @Override
105    public void login() {
106        if (!loggedIn) {
107            LoginComponent.pushPrincipal(principal);
108            loggedIn = true;
109        }
110    }
111
112    @Override
113    public void logout() {
114        close();
115    }
116
117    @Override
118    public void close() {
119        if (loggedIn) {
120            LoginComponent.popPrincipal();
121            loggedIn = false;
122        }
123    }
124
125}