org.codinjutsu.tools.jenkins.security.DefaultSecurityClient.java Source code

Java tutorial

Introduction

Here is the source code for org.codinjutsu.tools.jenkins.security.DefaultSecurityClient.java

Source

/*
 * Copyright (c) 2013 David Boissier
 *
 * 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 org.codinjutsu.tools.jenkins.security;

import com.intellij.openapi.vfs.VirtualFile;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.lang.StringUtils;
import org.codinjutsu.tools.jenkins.exception.ConfigurationException;
import org.codinjutsu.tools.jenkins.model.VirtualFilePartSource;
import org.codinjutsu.tools.jenkins.util.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

class DefaultSecurityClient implements SecurityClient {

    private static final String BAD_CRUMB_DATA = "No valid crumb was included in the request";
    static final String CRUMB_NAME = ".crumb";
    private static final int DEFAULT_SOCKET_TIMEOUT = 10000;
    private static final int DEFAULT_CONNECTION_TIMEOUT = 10000;

    protected String crumbData;

    protected final HttpClient httpClient;
    protected Map<String, VirtualFile> files = new HashMap<String, VirtualFile>();

    DefaultSecurityClient(String crumbData) {
        this.httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
        this.crumbData = crumbData;
    }

    @Override
    public void connect(URL jenkinsUrl) {
        execute(jenkinsUrl);
    }

    public String execute(URL url) {
        String urlStr = url.toString();

        ResponseCollector responseCollector = new ResponseCollector();
        runMethod(urlStr, responseCollector);

        if (isRedirection(responseCollector.statusCode)) {
            runMethod(responseCollector.data, responseCollector);
        }

        return responseCollector.data;
    }

    @Override
    public void setFiles(Map<String, VirtualFile> files) {
        this.files = files;
    }

    private PostMethod addFiles(PostMethod post) {
        if (files.size() > 0) {
            ArrayList<Part> parts = new ArrayList<Part>();
            int i = 0;
            for (String key : files.keySet()) {
                VirtualFile virtualFile = files.get(key);
                parts.add(new StringPart("name", key));
                parts.add(new StringPart("json", "{\"parameter\":{\"name\":\"" + key + "\",\"file\":\""
                        + String.format("file%d", i) + "\"}}"));
                parts.add(new FilePart(String.format("file%d", i), new VirtualFilePartSource(virtualFile)));
                i++;
            }
            post.setRequestEntity(
                    new MultipartRequestEntity(parts.toArray(new Part[parts.size()]), post.getParams()));
            files.clear();
        }

        return post;
    }

    private void runMethod(String url, ResponseCollector responseCollector) {
        PostMethod post = new PostMethod(url);

        if (isCrumbDataSet()) {
            post.addRequestHeader(CRUMB_NAME, crumbData);
        }

        post = addFiles(post);

        try {
            if (files.isEmpty()) {
                httpClient.getParams().setParameter("http.socket.timeout", DEFAULT_SOCKET_TIMEOUT);
                httpClient.getParams().setParameter("http.connection.timeout", DEFAULT_CONNECTION_TIMEOUT);
            } else {
                httpClient.getParams().setParameter("http.socket.timeout", 0);
                httpClient.getParams().setParameter("http.connection.timeout", 0);
            }

            int statusCode = httpClient.executeMethod(post);
            final String responseBody;
            try (InputStream inputStream = post.getResponseBodyAsStream();) {
                responseBody = IOUtils.toString(inputStream, post.getResponseCharSet());
            }
            checkResponse(statusCode, responseBody);

            if (HttpURLConnection.HTTP_OK == statusCode) {
                responseCollector.collect(statusCode, responseBody);
            }
            if (isRedirection(statusCode)) {
                responseCollector.collect(statusCode, post.getResponseHeader("Location").getValue());
            }
        } catch (HttpException httpEx) {
            throw new ConfigurationException(
                    String.format("HTTP Error during method execution '%s': %s", url, httpEx.getMessage()), httpEx);
        } catch (UnknownHostException uhEx) {
            throw new ConfigurationException(String.format("Unknown server: %s", uhEx.getMessage()), uhEx);
        } catch (IOException ioEx) {
            throw new ConfigurationException(
                    String.format("IO Error during method execution '%s': %s", url, ioEx.getMessage()), ioEx);
        } finally {
            post.releaseConnection();
        }
    }

    protected void checkResponse(int statusCode, String responseBody) throws AuthenticationException {
        if (statusCode == HttpURLConnection.HTTP_NOT_FOUND) {
            throw new AuthenticationException("Not found");
        }

        if (statusCode == HttpURLConnection.HTTP_FORBIDDEN || statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
            if (StringUtils.containsIgnoreCase(responseBody, BAD_CRUMB_DATA)) {
                throw new AuthenticationException("CSRF enabled -> Missing or bad crumb data");
            }

            throw new AuthenticationException("Unauthorized -> Missing or bad credentials", responseBody);
        }

        if (HttpURLConnection.HTTP_INTERNAL_ERROR == statusCode) {
            throw new AuthenticationException("Server Internal Error: Server unavailable");
        }
    }

    private boolean isRedirection(int statusCode) {
        return statusCode / 100 == 3;
    }

    protected boolean isCrumbDataSet() {
        return StringUtils.isNotBlank(crumbData);
    }

    private static class ResponseCollector {

        private int statusCode;
        private String data;

        void collect(int statusCode, String body) {
            this.statusCode = statusCode;
            this.data = body;
        }

    }
}