001/**
002 * Copyright (C) 2006-2020 Talend Inc. - www.talend.com
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 */
016package org.talend.sdk.component.server.service.security;
017
018import static java.util.Optional.ofNullable;
019
020import java.util.HashMap;
021import java.util.Map;
022
023import javax.enterprise.event.Observes;
024import javax.enterprise.inject.spi.AfterBeanDiscovery;
025import javax.enterprise.inject.spi.AfterDeploymentValidation;
026import javax.enterprise.inject.spi.BeanManager;
027import javax.enterprise.inject.spi.EventContext;
028import javax.enterprise.inject.spi.Extension;
029import javax.enterprise.inject.spi.ObserverMethod;
030import javax.enterprise.inject.spi.ProcessObserverMethod;
031import javax.enterprise.inject.spi.ProcessSyntheticObserverMethod;
032import javax.enterprise.inject.spi.configurator.ObserverMethodConfigurator;
033import javax.inject.Named;
034
035import org.talend.sdk.component.server.configuration.ComponentServerConfiguration;
036import org.talend.sdk.component.server.service.security.event.OnCommand;
037import org.talend.sdk.component.server.service.security.event.OnConnection;
038
039import lombok.extern.slf4j.Slf4j;
040
041// this extension will:
042// - capture all OnConnection/OnCommand observers
043// - bind the configured one (ComponentServerConfiguration) to handle the security
044@Slf4j
045public class SecurityExtension implements Extension {
046
047    private final Map<String, ObserverMethod<OnConnection>> onConnectionObservers = new HashMap<>();
048
049    private final Map<String, ObserverMethod<OnCommand>> onCommandObservers = new HashMap<>();
050
051    private ObserverMethod<OnConnection> onConnectionMtd;
052
053    private ObserverMethod<OnCommand> onCommandMtd;
054
055    void findOnConnectionMethod(@Observes final ProcessObserverMethod<OnConnection, ?> processObserverMethod) {
056        if (ProcessSyntheticObserverMethod.class.isInstance(processObserverMethod)) {
057            return;
058        }
059        onConnectionObservers.put(getName(processObserverMethod), processObserverMethod.getObserverMethod());
060        processObserverMethod.veto();
061    }
062
063    void findOnCommandMethod(@Observes final ProcessObserverMethod<OnCommand, ?> processObserverMethod) {
064        if (ProcessSyntheticObserverMethod.class.isInstance(processObserverMethod)) {
065            return;
066        }
067        onCommandObservers.put(getName(processObserverMethod), processObserverMethod.getObserverMethod());
068        processObserverMethod.veto();
069    }
070
071    void addSecurityHandlerObservers(@Observes final AfterBeanDiscovery afterBeanDiscovery) {
072        final ObserverMethodConfigurator<OnConnection> onConnection = afterBeanDiscovery.addObserverMethod();
073        onConnection.observedType(OnConnection.class).notifyWith(this::onConnection);
074        final ObserverMethodConfigurator<OnCommand> onCommand = afterBeanDiscovery.addObserverMethod();
075        onCommand.observedType(OnCommand.class).notifyWith(this::onCommand);
076    }
077
078    void bindSecurityHandlers(@Observes final AfterDeploymentValidation afterDeploymentValidation,
079            final BeanManager beanManager) {
080        final ComponentServerConfiguration configuration = ComponentServerConfiguration.class
081                .cast(beanManager
082                        .getReference(beanManager.resolve(beanManager.getBeans(ComponentServerConfiguration.class)),
083                                ComponentServerConfiguration.class, beanManager.createCreationalContext(null)));
084
085        final String connectionHandler = configuration.getSecurityConnectionHandler();
086        onConnectionMtd = ofNullable(onConnectionObservers.get(connectionHandler))
087                .orElseThrow(() -> new IllegalArgumentException("No handler '" + connectionHandler + "'"));
088
089        final String commandHandler = configuration.getSecurityCommandHandler();
090        onCommandMtd = ofNullable(onCommandObservers.get(commandHandler))
091                .orElseThrow(() -> new IllegalArgumentException("No handler '" + commandHandler + "'"));
092
093        // no more needed
094        onConnectionObservers.clear();
095        onCommandObservers.clear();
096
097        log
098                .info("Security configured with connection handler '{}' and command handler '{}'", connectionHandler,
099                        commandHandler);
100    }
101
102    private void onConnection(final EventContext<OnConnection> onConnection) {
103        onConnectionMtd.notify(onConnection);
104    }
105
106    private void onCommand(final EventContext<OnCommand> onCommand) {
107        onCommandMtd.notify(onCommand);
108    }
109
110    private String getName(final ProcessObserverMethod<?, ?> processObserverMethod) {
111        return ofNullable(processObserverMethod.getAnnotatedMethod().getDeclaringType().getAnnotation(Named.class))
112                .map(Named::value)
113                .orElseGet(() -> processObserverMethod.getAnnotatedMethod().getJavaMember().getName());
114    }
115}