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