001/*
002 * (C) Copyright 2006-2017 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 *     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.nuxeo.ecm.automation.core.Constants;
034import org.nuxeo.ecm.automation.io.services.IOComponent;
035import org.nuxeo.ecm.core.api.NuxeoPrincipal;
036import org.nuxeo.runtime.api.Framework;
037import org.nuxeo.runtime.model.ComponentContext;
038import org.nuxeo.runtime.model.ComponentInstance;
039import org.nuxeo.runtime.model.DefaultComponent;
040
041/**
042 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
043 */
044public class AutomationServerComponent extends DefaultComponent implements AutomationServer {
045
046    protected static final String XP_BINDINGS = "bindings";
047
048    protected static final String IOCOMPONENT_NAME = "org.nuxeo.ecm.automation.io.services.IOComponent";
049
050    protected IOComponent ioComponent;
051
052    private static final String XP_MARSHALLER = "marshallers";
053
054    protected Map<String, RestBinding> bindings;
055
056    protected static final String XP_CODECS = "codecs";
057
058    protected volatile Map<String, RestBinding> lookup;
059
060    protected List<Class<? extends MessageBodyWriter<?>>> writers;
061
062    protected List<Class<? extends MessageBodyReader<?>>> readers;
063
064    @Override
065    public void activate(ComponentContext context) {
066        bindings = new HashMap<>();
067        writers = new ArrayList<>();
068        readers = new ArrayList<>();
069        ioComponent = ((IOComponent) Framework.getRuntime().getComponentInstance(IOCOMPONENT_NAME).getInstance());
070    }
071
072    @Override
073    public void deactivate(ComponentContext context) {
074        bindings = null;
075    }
076
077    @Override
078    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
079        if (XP_BINDINGS.equals(extensionPoint)) {
080            RestBinding binding = (RestBinding) contribution;
081            addBinding(binding);
082        } else if (XP_MARSHALLER.equals(extensionPoint)) {
083            MarshallerDescriptor marshaller = (MarshallerDescriptor) contribution;
084            writers.addAll(marshaller.getWriters());
085            readers.addAll(marshaller.getReaders());
086        } else if (XP_CODECS.equals(extensionPoint)) {
087            ioComponent.registerContribution(contribution, extensionPoint, contributor);
088        }
089    }
090
091    @Override
092    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
093        if (XP_BINDINGS.equals(extensionPoint)) {
094            RestBinding binding = (RestBinding) contribution;
095            removeBinding(binding);
096        } else if (XP_CODECS.equals(extensionPoint)) {
097            ioComponent.unregisterContribution(contribution, extensionPoint, contributor);
098        }
099    }
100
101    @Override
102    public <T> T getAdapter(Class<T> adapter) {
103        if (AutomationServer.class.isAssignableFrom(adapter)) {
104            return adapter.cast(this);
105        }
106        return null;
107    }
108
109    @Override
110    public RestBinding getOperationBinding(String name) {
111        return lookup().get(name);
112    }
113
114    @Override
115    public RestBinding getChainBinding(String name) {
116        return lookup().get(Constants.CHAIN_ID_PREFIX + name);
117    }
118
119    @Override
120    public RestBinding[] getBindings() {
121        Map<String, RestBinding> map = lookup();
122        return map.values().toArray(new RestBinding[map.size()]);
123    }
124
125    protected String getBindingKey(RestBinding binding) {
126        return binding.isChain() ? Constants.CHAIN_ID_PREFIX + binding.getName() : binding.getName();
127    }
128
129    @Override
130    public synchronized void addBinding(RestBinding binding) {
131        String key = getBindingKey(binding);
132        bindings.put(key, binding);
133        lookup = null;
134    }
135
136    @Override
137    public synchronized RestBinding removeBinding(RestBinding binding) {
138        RestBinding result = bindings.remove(getBindingKey(binding));
139        lookup = null;
140        return result;
141    }
142
143    @Override
144    public boolean accept(String name, boolean isChain, HttpServletRequest req) {
145        if (isChain) {
146            name = Constants.CHAIN_ID_PREFIX + name;
147        }
148        RestBinding binding = lookup().get(name);
149        if (binding != null) {
150            if (binding.isDisabled()) {
151                return false;
152            }
153            if (binding.isSecure()) {
154                if (!req.isSecure()) {
155                    return false;
156                }
157            }
158            Principal principal = req.getUserPrincipal();
159
160            if (binding.isAdministrator() || binding.hasGroups()) {
161                if (principal instanceof NuxeoPrincipal) {
162                    NuxeoPrincipal np = (NuxeoPrincipal) principal;
163                    if (binding.isAdministrator() && np.isAdministrator()) {
164                        return true;
165                    }
166                    if (binding.hasGroups()) {
167                        for (String group : binding.getGroups()) {
168                            if (np.isMemberOf(group)) {
169                                return true;
170                            }
171                        }
172                    }
173                }
174                return false;
175            }
176        }
177        return true;
178    }
179
180    private Map<String, RestBinding> lookup() {
181        Map<String, RestBinding> _lookup = lookup;
182        if (_lookup == null) {
183            synchronized (this) {
184                lookup = new HashMap<>(bindings);
185                _lookup = lookup;
186            }
187        }
188        return _lookup;
189    }
190
191    @Override
192    public List<Class<? extends MessageBodyWriter<?>>> getWriters() {
193        return writers;
194    }
195
196    @Override
197    public List<Class<? extends MessageBodyReader<?>>> getReaders() {
198        return readers;
199    }
200
201}