io.dropwizard.primer.PrimerBundle.java Source code

Java tutorial

Introduction

Here is the source code for io.dropwizard.primer.PrimerBundle.java

Source

/*
 * Copyright 2016 Phaneesh Nagaraja <phaneesh.n@gmail.com>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.dropwizard.primer;

import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.github.toastshaman.dropwizard.auth.jwt.JsonWebTokenParser;
import com.github.toastshaman.dropwizard.auth.jwt.hmac.HmacSHA512Verifier;
import com.github.toastshaman.dropwizard.auth.jwt.parser.DefaultJsonWebTokenParser;
import feign.Feign;
import feign.Logger;
import feign.Target;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.okhttp.OkHttpClient;
import feign.ranger.RangerTarget;
import feign.slf4j.Slf4jLogger;
import io.dropwizard.Configuration;
import io.dropwizard.ConfiguredBundle;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.primer.auth.PrimerAuthenticatorRequestFilter;
import io.dropwizard.primer.auth.PrimerAuthorizationRegistry;
import io.dropwizard.primer.client.PrimerClient;
import io.dropwizard.primer.core.PrimerError;
import io.dropwizard.primer.exception.PrimerException;
import io.dropwizard.primer.exception.PrimerExceptionMapper;
import io.dropwizard.primer.model.PrimerAuthorizationMatrix;
import io.dropwizard.primer.model.PrimerBundleConfiguration;
import io.dropwizard.primer.model.PrimerRangerEndpoint;
import io.dropwizard.primer.model.PrimerSimpleEndpoint;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

/**
 * @author phaneesh
 */
@Slf4j
public abstract class PrimerBundle<T extends Configuration> implements ConfiguredBundle<T> {

    //private static List<String> whiteList = new ArrayList<>();

    private static PrimerClient primerClient = null;

    public abstract PrimerBundleConfiguration getPrimerConfiguration(T configuration);

    public abstract Set<String> withWhiteList(T configuration);

    public abstract PrimerAuthorizationMatrix withAuthorization(T configuration);

    public static PrimerClient getPrimerClient() {
        return primerClient;
    }

    /**
     * Default method which provides a default curator for service discovery to work in case there is no other
     * curator instance available. Override this to supply your own creator
     *
     * @param configuration Application configuration
     * @return CuratorFramework
     */
    public CuratorFramework getCurator(T configuration) {
        final PrimerBundleConfiguration primerBundleConfiguration = getPrimerConfiguration(configuration);
        final val config = (PrimerRangerEndpoint) primerBundleConfiguration.getEndpoint();
        final CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                .connectString(config.getZookeeper()).namespace(config.getNamespace())
                .retryPolicy(new RetryNTimes(1000, 500)).build();
        curatorFramework.start();
        return curatorFramework;
    }

    @Override
    public void initialize(Bootstrap<?> bootstrap) {
        bootstrap.getObjectMapper().registerSubtypes(new NamedType(PrimerSimpleEndpoint.class, "simple"));
        bootstrap.getObjectMapper().registerSubtypes(new NamedType(PrimerRangerEndpoint.class, "ranger"));
    }

    @Override
    public void run(T configuration, Environment environment) throws Exception {
        final val primerConfig = getPrimerConfiguration(configuration);
        final JsonWebTokenParser tokenParser = new DefaultJsonWebTokenParser();
        final byte[] secretKey = primerConfig.getPrivateKey().getBytes(StandardCharsets.UTF_8);
        final HmacSHA512Verifier tokenVerifier = new HmacSHA512Verifier(secretKey);

        initializeAuthorization(configuration, tokenParser, tokenVerifier);
        final JacksonDecoder decoder = new JacksonDecoder();
        final JacksonEncoder encoder = new JacksonEncoder();
        final Slf4jLogger logger = new Slf4jLogger();

        environment.lifecycle().manage(new Managed() {
            @Override
            public void start() throws Exception {
                primerClient = Feign.builder().decoder(decoder).encoder(encoder)
                        .errorDecoder((methodKey, response) -> {
                            try {
                                final PrimerError error = environment.getObjectMapper()
                                        .readValue(response.body().asInputStream(), PrimerError.class);
                                return PrimerException.builder().message(error.getMessage())
                                        .errorCode(error.getErrorCode()).status(response.status()).build();
                            } catch (IOException e) {
                                return PrimerException.builder().status(response.status()).errorCode("PR000")
                                        .message(e.getMessage()).build();
                            }
                        }).client(new OkHttpClient()).logger(logger).logLevel(Logger.Level.BASIC)
                        .target(getPrimerTarget(configuration, environment));
            }

            @Override
            public void stop() throws Exception {

            }
        });
        environment.jersey().register(new PrimerExceptionMapper());
        environment.jersey()
                .register(PrimerAuthenticatorRequestFilter.builder()
                        .configuration(getPrimerConfiguration(configuration))
                        .objectMapper(environment.getObjectMapper()).build());
    }

    private Target<PrimerClient> getPrimerTarget(T configuration, Environment environment) {
        final val primerConfig = getPrimerConfiguration(configuration);
        switch (primerConfig.getEndpoint().getType()) {
        case "simple":
            final val endpoint = (PrimerSimpleEndpoint) primerConfig.getEndpoint();
            return new Target.HardCodedTarget<>(PrimerClient.class,
                    String.format("http://%s:%d", endpoint.getHost(), endpoint.getPort()));
        case "ranger":
            final val config = (PrimerRangerEndpoint) primerConfig.getEndpoint();
            try {
                return new RangerTarget<>(PrimerClient.class, config.getEnvironment(), config.getNamespace(),
                        config.getService(), getCurator(configuration), false, environment.getObjectMapper());
            } catch (Exception e) {
                log.error("Error creating ranger endpoint for primer", e);
                return null;
            }
        default:
            throw new IllegalArgumentException("unknown primer target type specified");
        }
    }

    private void initializeAuthorization(T configuration, JsonWebTokenParser tokenParser,
            HmacSHA512Verifier tokenVerifier) {
        final val primerConfig = getPrimerConfiguration(configuration);
        final Set<String> whiteListUrls = new HashSet<>();
        final Set<String> dynamicWhiteList = withWhiteList(configuration);
        if (dynamicWhiteList != null) {
            whiteListUrls.addAll(dynamicWhiteList);
        }
        if (primerConfig.getWhileListUrl() != null) {
            whiteListUrls.addAll(primerConfig.getWhileListUrl());
        }
        PrimerAuthorizationMatrix permissionMatrix = primerConfig.getAuthorizations();
        //If no authorizations are provided in config then just get authorizations programmatically
        if (permissionMatrix == null) {
            permissionMatrix = withAuthorization(configuration);
        } else { //Else needs to merge both the authorizations
            val dynamicAuthMatrix = withAuthorization(configuration);
            if (permissionMatrix.getAuthorizations() == null) {
                permissionMatrix.setAuthorizations(dynamicAuthMatrix.getAuthorizations());
            } else {
                permissionMatrix.getAuthorizations().addAll(dynamicAuthMatrix.getAuthorizations());
            }
            if (permissionMatrix.getAutoAuthorizations() == null) {
                permissionMatrix.setAutoAuthorizations(dynamicAuthMatrix.getAutoAuthorizations());
            } else {
                permissionMatrix.getAutoAuthorizations().addAll(dynamicAuthMatrix.getAutoAuthorizations());
            }
            if (permissionMatrix.getStaticAuthorizations() == null) {
                permissionMatrix.setStaticAuthorizations(dynamicAuthMatrix.getStaticAuthorizations());
            } else {
                permissionMatrix.getStaticAuthorizations().addAll(dynamicAuthMatrix.getStaticAuthorizations());
            }
        }
        PrimerAuthorizationRegistry.init(permissionMatrix, whiteListUrls, primerConfig, tokenParser, tokenVerifier);
    }
}