001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     bstefanescu
011 *     vpasquier
012 *     slacoin
013 */
014package org.nuxeo.ecm.automation.server;
015
016import java.security.Principal;
017import java.util.ArrayList;
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021
022import javax.servlet.http.HttpServletRequest;
023import javax.ws.rs.ext.MessageBodyReader;
024import javax.ws.rs.ext.MessageBodyWriter;
025
026import org.codehaus.jackson.JsonFactory;
027import org.nuxeo.ecm.automation.core.Constants;
028import org.nuxeo.ecm.automation.io.services.IOComponent;
029import org.nuxeo.ecm.core.api.NuxeoPrincipal;
030import org.nuxeo.ecm.webengine.JsonFactoryManager;
031import org.nuxeo.runtime.api.Framework;
032import org.nuxeo.runtime.model.ComponentContext;
033import org.nuxeo.runtime.model.ComponentInstance;
034import org.nuxeo.runtime.model.DefaultComponent;
035
036/**
037 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
038 */
039public class AutomationServerComponent extends DefaultComponent implements AutomationServer {
040
041    /**
042     * Was used to get the JsonFactory, but since 5.7.3 use either: *
043     * <code>{@link JsonFactoryManager#getJsonFactory()}</code> * Context annotation in JAX-RS object (providers or
044     * resources)
045     */
046    @Deprecated
047    public static AutomationServerComponent me;
048
049    protected static final String XP_BINDINGS = "bindings";
050
051    protected static final String IOCOMPONENT_NAME = "org.nuxeo.ecm.automation.io.services.IOComponent";
052
053    protected IOComponent ioComponent;
054
055    private static final String XP_MARSHALLER = "marshallers";
056
057    protected Map<String, RestBinding> bindings;
058
059    protected static final String XP_CODECS = "codecs";
060
061    protected volatile Map<String, RestBinding> lookup;
062
063    protected List<Class<? extends MessageBodyWriter<?>>> writers;
064
065    protected List<Class<? extends MessageBodyReader<?>>> readers;
066
067    @Override
068    public void activate(ComponentContext context) {
069        bindings = new HashMap<String, RestBinding>();
070        writers = new ArrayList<>();
071        readers = new ArrayList<>();
072        me = this;
073        ioComponent = ((IOComponent) Framework.getRuntime().getComponentInstance(IOCOMPONENT_NAME).getInstance());
074    }
075
076    @Override
077    public void deactivate(ComponentContext context) {
078        bindings = null;
079        me = null;
080    }
081
082    @Override
083    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
084        if (XP_BINDINGS.equals(extensionPoint)) {
085            RestBinding binding = (RestBinding) contribution;
086            addBinding(binding);
087        } else if (XP_MARSHALLER.equals(extensionPoint)) {
088            MarshallerDescriptor marshaller = (MarshallerDescriptor) contribution;
089            writers.addAll(marshaller.getWriters());
090            readers.addAll(marshaller.getReaders());
091        } else if (XP_CODECS.equals(extensionPoint)) {
092            ioComponent.registerContribution(contribution, extensionPoint, contributor);
093        }
094    }
095
096    @Override
097    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
098        if (XP_BINDINGS.equals(extensionPoint)) {
099            RestBinding binding = (RestBinding) contribution;
100            removeBinding(binding);
101        } else if (XP_CODECS.equals(extensionPoint)) {
102            ioComponent.unregisterContribution(contribution, extensionPoint, contributor);
103        }
104    }
105
106    @Override
107    public <T> T getAdapter(Class<T> adapter) {
108        if (AutomationServer.class.isAssignableFrom(adapter)) {
109            return adapter.cast(this);
110        }
111        return null;
112    }
113
114    @Override
115    public void applicationStarted(ComponentContext context) {
116        super.applicationStarted(context);
117    }
118
119    /**
120     * Since 5.7.3, use {@link JsonFactoryManager#getJsonFactory()}
121     */
122    @Deprecated
123    public JsonFactory getFactory() {
124        return Framework.getLocalService(JsonFactoryManager.class).getJsonFactory();
125    }
126
127    @Override
128    public RestBinding getOperationBinding(String name) {
129        return lookup().get(name);
130    }
131
132    @Override
133    public RestBinding getChainBinding(String name) {
134        return lookup().get(Constants.CHAIN_ID_PREFIX + name);
135    }
136
137    @Override
138    public RestBinding[] getBindings() {
139        Map<String, RestBinding> map = lookup();
140        return map.values().toArray(new RestBinding[map.size()]);
141    }
142
143    protected String getBindingKey(RestBinding binding) {
144        return binding.isChain() ? Constants.CHAIN_ID_PREFIX + binding.getName() : binding.getName();
145    }
146
147    @Override
148    public synchronized void addBinding(RestBinding binding) {
149        String key = getBindingKey(binding);
150        bindings.put(key, binding);
151        lookup = null;
152    }
153
154    @Override
155    public synchronized RestBinding removeBinding(RestBinding binding) {
156        RestBinding result = bindings.remove(getBindingKey(binding));
157        lookup = null;
158        return result;
159    }
160
161    @Override
162    public boolean accept(String name, boolean isChain, HttpServletRequest req) {
163        if (isChain) {
164            name = "Chain." + name;
165        }
166        RestBinding binding = lookup().get(name);
167        if (binding != null) {
168            if (binding.isDisabled()) {
169                return false;
170            }
171            if (binding.isSecure()) {
172                if (!req.isSecure()) {
173                    return false;
174                }
175            }
176            Principal principal = req.getUserPrincipal();
177
178            if (binding.isAdministrator() || binding.hasGroups()) {
179                if (principal instanceof NuxeoPrincipal) {
180                    NuxeoPrincipal np = (NuxeoPrincipal) principal;
181                    if (binding.isAdministrator() && np.isAdministrator()) {
182                        return true;
183                    }
184                    if (binding.hasGroups()) {
185                        for (String group : binding.getGroups()) {
186                            if (np.isMemberOf(group)) {
187                                return true;
188                            }
189                        }
190                    }
191                }
192                return false;
193            }
194        }
195        return true;
196    }
197
198    private Map<String, RestBinding> lookup() {
199        Map<String, RestBinding> _lookup = lookup;
200        if (_lookup == null) {
201            synchronized (this) {
202                lookup = new HashMap<String, RestBinding>(bindings);
203                _lookup = lookup;
204            }
205        }
206        return _lookup;
207    }
208
209    @Override
210    public List<Class<? extends MessageBodyWriter<?>>> getWriters() {
211        return writers;
212    }
213
214    @Override
215    public List<Class<? extends MessageBodyReader<?>>> getReaders() {
216        return readers;
217    }
218
219}