001/*
002 * (C) Copyright 2006-2019 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 *     Thierry Delprat
019 *     Florent Guillaume
020 */
021
022package org.nuxeo.runtime.api.login;
023
024import java.io.Serializable;
025import java.security.Principal;
026import java.util.Deque;
027import java.util.LinkedList;
028import java.util.Objects;
029
030import javax.security.auth.login.LoginException;
031
032import org.nuxeo.runtime.api.Framework;
033import org.nuxeo.runtime.model.DefaultComponent;
034
035/**
036 * Component holding the stack of logged in principals.
037 */
038public class LoginComponent extends DefaultComponent implements LoginService {
039
040    public static final String SYSTEM_USERNAME = "system";
041
042    /**
043     * The thread-local principal stack. The top of the stack (last element) contains the current principal.
044     *
045     * @since 11.1
046     */
047    protected static final ThreadLocal<Deque<Principal>> PRINCIPAL_STACK = ThreadLocal.withInitial(
048            () -> new LinkedList<>());
049
050    @Override
051    @SuppressWarnings("unchecked")
052    public <T> T getAdapter(Class<T> adapter) {
053        if (LoginService.class.isAssignableFrom(adapter)) {
054            return (T) this;
055        }
056        return null;
057    }
058
059    protected NuxeoLoginContext systemLogin(String originatingUser) {
060        Principal principal = new SystemID(originatingUser);
061        NuxeoLoginContext loginContext = NuxeoLoginContext.create(principal);
062        loginContext.login();
063        return loginContext;
064    }
065
066    @Override
067    public NuxeoLoginContext login() {
068        return systemLogin(null);
069    }
070
071    @Override
072    public NuxeoLoginContext loginAs(String username) {
073        return systemLogin(username);
074    }
075
076    @Deprecated
077    @Override
078    public NuxeoLoginContext login(String username, Object credentials) throws LoginException {
079        return Framework.loginUser(username);
080    }
081
082    @Override
083    public boolean isSystemId(Principal principal) {
084        return isSystemLogin(principal);
085    }
086
087    public static boolean isSystemLogin(Object principal) {
088        return principal instanceof SystemID;
089    }
090
091    public static class SystemID implements Principal, Serializable {
092
093        private static final long serialVersionUID = 2758247997191809993L;
094
095        private final String userName;
096
097        public SystemID() {
098            userName = null;
099        }
100
101        public SystemID(String origUser) {
102            userName = origUser == null ? SYSTEM_USERNAME : origUser;
103        }
104
105        @Override
106        public String getName() {
107            return userName;
108        }
109
110        @Override
111        public boolean equals(Object object) {
112            if (object instanceof SystemID) {
113                SystemID other = (SystemID) object;
114                return Objects.equals(userName, other.userName);
115            }
116            return false;
117        }
118
119        @Override
120        public int hashCode() {
121            return userName == null ? 0 : userName.hashCode();
122        }
123
124    }
125
126    /**
127     * INTERNAL.
128     *
129     * @since 11.1
130     */
131    public static Deque<Principal> getPrincipalStack() {
132        return PRINCIPAL_STACK.get();
133    }
134
135    /**
136     * INTERNAL.
137     *
138     * @since 11.1
139     */
140    public static void clearPrincipalStack() {
141        // removes the whole thread-local, so original stack object is untouched
142        PRINCIPAL_STACK.remove();
143    }
144
145    /**
146     * Pushes the principal to the current principal stack.
147     *
148     * @param principal the principal
149     * @since 11.1
150     */
151    public static void pushPrincipal(Principal principal) {
152        PRINCIPAL_STACK.get().addLast(principal);
153    }
154
155    /**
156     * Pops the last principal from the current principal stack.
157     *
158     * @return the last principal, or {@code null} if the stack is empty
159     * @since 11.1
160     */
161    public static Principal popPrincipal() {
162        return PRINCIPAL_STACK.get().pollLast();
163    }
164
165    /**
166     * Returns the last principal from the current principal stack.
167     *
168     * @return the last principal, or {@code null} if the stack is empty
169     * @since 11.1
170     */
171    public static Principal getCurrentPrincipal() {
172        return PRINCIPAL_STACK.get().peekLast();
173    }
174
175}