Search in sources :

Example 1 with UserPass

use of org.commonjava.indy.subsys.http.util.UserPass in project indy by Commonjava.

the class ProxyResponseWriter method handleEvent.

@Override
public void handleEvent(final ConduitStreamSinkChannel channel) {
    HttpConduitWrapper http = new HttpConduitWrapper(channel, httpRequest, contentController, cacheProvider);
    if (httpRequest == null) {
        if (error != null) {
            logger.debug("handling error from request reader: " + error.getMessage(), error);
            handleError(error, http);
        } else {
            logger.debug("invalid state (no error or request) from request reader. Sending 400.");
            try {
                http.writeStatus(ApplicationStatus.BAD_REQUEST);
            } catch (final IOException e) {
                logger.error("Failed to write BAD REQUEST for missing HTTP first-line to response channel.", e);
            }
        }
        return;
    }
    // TODO: Can we handle this?
    final String oldThreadName = Thread.currentThread().getName();
    Thread.currentThread().setName("PROXY-" + httpRequest.getRequestLine().toString());
    channel.getCloseSetter().set((sinkChannel) -> {
        logger.debug("sink channel closing.");
        Thread.currentThread().setName(oldThreadName);
    });
    logger.info("\n\n\n>>>>>>> Handle write\n\n\n\n\n");
    if (error == null) {
        try {
            final UserPass proxyUserPass = UserPass.parse(ApplicationHeader.proxy_authorization, httpRequest, null);
            logger.debug("Proxy UserPass: {}\nConfig secured? {}\nConfig tracking type: {}", proxyUserPass, config.isSecured(), config.getTrackingType());
            if (proxyUserPass == null && (config.isSecured() || TrackingType.ALWAYS == config.getTrackingType())) {
                http.writeStatus(ApplicationStatus.PROXY_AUTHENTICATION_REQUIRED);
                http.writeHeader(ApplicationHeader.proxy_authenticate, String.format(PROXY_AUTHENTICATE_FORMAT, config.getProxyRealm()));
            } else {
                RequestLine requestLine = httpRequest.getRequestLine();
                String method = requestLine.getMethod().toUpperCase();
                switch(method) {
                    case GET_METHOD:
                    case HEAD_METHOD:
                        {
                            if (proxyUserPass != null) {
                                logger.debug("Passing BASIC authentication credentials to Keycloak bearer-token translation authenticator");
                                if (!proxyAuthenticator.authenticate(proxyUserPass, http)) {
                                    break;
                                }
                            }
                            final URL url = new URL(requestLine.getUri());
                            final RemoteRepository repo = getRepository(url);
                            transfer(http, repo, url.getPath(), GET_METHOD.equals(method), proxyUserPass);
                            break;
                        }
                    case OPTIONS_METHOD:
                        {
                            http.writeStatus(ApplicationStatus.OK);
                            http.writeHeader(ApplicationHeader.allow, ALLOW_HEADER_VALUE);
                            break;
                        }
                    default:
                        {
                            http.writeStatus(ApplicationStatus.METHOD_NOT_ALLOWED);
                        }
                }
            }
            logger.info("Response complete.");
        } catch (final Throwable e) {
            error = e;
        }
    }
    if (error != null) {
        handleError(error, http);
    }
    try {
        http.close();
    } catch (final IOException e) {
        logger.error("Failed to flush/shutdown response.", e);
    }
}
Also used : RequestLine(org.apache.http.RequestLine) HttpConduitWrapper(org.commonjava.indy.httprox.util.HttpConduitWrapper) UserPass(org.commonjava.indy.subsys.http.util.UserPass) RemoteRepository(org.commonjava.indy.model.core.RemoteRepository) IOException(java.io.IOException) URL(java.net.URL)

Example 2 with UserPass

use of org.commonjava.indy.subsys.http.util.UserPass in project indy by Commonjava.

the class ProxyResponseWriter method getRepository.

private RemoteRepository getRepository(final URL url) throws IndyDataException {
    int port = url.getPort();
    if (port < 1) {
        port = url.getDefaultPort();
    }
    String name = PROXY_REPO_PREFIX + url.getHost().replace('.', '-') + '_' + port;
    final String baseUrl = String.format("%s://%s:%s/", url.getProtocol(), url.getHost(), port);
    ArtifactStoreQuery<RemoteRepository> query = storeManager.query().packageType(GENERIC_PKG_KEY).storeType(RemoteRepository.class);
    RemoteRepository remote = query.getRemoteRepositoryByUrl(baseUrl);
    if (remote == null) {
        logger.debug("Looking for remote repo with name: {}", name);
        remote = query.getByName(name);
        // if repo with this name already exists, it has a different url, so we need to use a different name
        int i = 1;
        while (remote != null) {
            name = PROXY_REPO_PREFIX + url.getHost().replace('.', '-') + "_" + i++;
            logger.debug("Looking for remote repo with name: {}", name);
            remote = query.getByName(name);
        }
    }
    if (remote == null) {
        logger.debug("Creating remote repository for HTTProx request: {}", url);
        final UrlInfo info = new UrlInfo(url.toExternalForm());
        if (repoCreator == null) {
            throw new IndyDataException("No valid instance of ProxyRepositoryCreator. Cannot auto-create remote proxy to: '{}'", baseUrl);
        }
        final UserPass up = UserPass.parse(ApplicationHeader.authorization, httpRequest, url.getAuthority());
        remote = repoCreator.create(name, baseUrl, info, up, LoggerFactory.getLogger(repoCreator.getClass()));
        remote.setMetadata(ArtifactStore.METADATA_ORIGIN, ProxyAcceptHandler.HTTPROX_ORIGIN);
        final ChangeSummary changeSummary = new ChangeSummary(ChangeSummary.SYSTEM_USER, "Creating HTTProx proxy for: " + info.getUrl());
        storeManager.storeArtifactStore(remote, changeSummary, false, true, new EventMetadata());
    }
    return remote;
}
Also used : IndyDataException(org.commonjava.indy.data.IndyDataException) UrlInfo(org.commonjava.indy.util.UrlInfo) RemoteRepository(org.commonjava.indy.model.core.RemoteRepository) UserPass(org.commonjava.indy.subsys.http.util.UserPass) ChangeSummary(org.commonjava.indy.audit.ChangeSummary) EventMetadata(org.commonjava.maven.galley.event.EventMetadata)

Example 3 with UserPass

use of org.commonjava.indy.subsys.http.util.UserPass in project indy by Commonjava.

the class ProxyResponseWriter method doHandleEvent.

private void doHandleEvent(final ConduitStreamSinkChannel sinkChannel) {
    if (directed) {
        return;
    }
    ProxyMeter meter = new ProxyMeter(httpRequest.getRequestLine().getMethod(), httpRequest.getRequestLine().toString(), startNanos, sliMetricSet, restLogger, peerAddress);
    try {
        HttpConduitWrapper http = new HttpConduitWrapper(sinkChannel, httpRequest, contentController, cacheProvider);
        if (httpRequest == null) {
            if (error != null) {
                logger.debug("Handling error from request reader: " + error.getMessage(), error);
                handleError(error, http);
            } else {
                logger.debug("Invalid state (no error or request) from request reader. Sending 400.");
                try {
                    http.writeStatus(ApplicationStatus.BAD_REQUEST);
                } catch (final IOException e) {
                    logger.error("Failed to write BAD REQUEST for missing HTTP first-line to response channel.", e);
                }
            }
            return;
        }
        restLogger.info("SERVE {} (from: {})", httpRequest.getRequestLine(), peerAddress);
        // TODO: Can we handle this?
        final String oldThreadName = Thread.currentThread().getName();
        Thread.currentThread().setName("PROXY-" + httpRequest.getRequestLine().toString());
        sinkChannel.getCloseSetter().set((c) -> {
            logger.trace("Sink channel closing.");
            Thread.currentThread().setName(oldThreadName);
            if (sslTunnel != null) {
                logger.trace("Close ssl tunnel");
                sslTunnel.close();
            }
        });
        logger.debug("\n\n\n>>>>>>> Handle write\n\n\n");
        if (error == null) {
            ProxyResponseHelper proxyResponseHelper = new ProxyResponseHelper(httpRequest, config, contentController, repoCreator, storeManager, metricsConfig, metricManager, cls);
            try {
                if (repoCreator == null) {
                    throw new IndyDataException("No valid instance of ProxyRepositoryCreator");
                }
                final UserPass proxyUserPass = parse(ApplicationHeader.proxy_authorization, httpRequest, null);
                logger.info("Using proxy authentication: {}", proxyUserPass);
                mdcManager.putExtraHeaders(httpRequest);
                // FIXME: We cannot trace through this interface currently!
                mdcManager.putExternalID(proxyUserPass == null ? null : proxyUserPass.getUser());
                logger.debug("Proxy UserPass: {}\nConfig secured? {}\nConfig tracking type: {}", proxyUserPass, config.isSecured(), config.getTrackingType());
                if (proxyUserPass == null && (config.isSecured() || TrackingType.ALWAYS == config.getTrackingType())) {
                    String realmInfo = String.format(PROXY_AUTHENTICATE_FORMAT, config.getProxyRealm());
                    logger.info("Not authenticated to proxy. Sending response: {} / {}: {}", PROXY_AUTHENTICATION_REQUIRED, proxy_authenticate, realmInfo);
                    http.writeStatus(PROXY_AUTHENTICATION_REQUIRED);
                    http.writeHeader(proxy_authenticate, realmInfo);
                } else {
                    RequestLine requestLine = httpRequest.getRequestLine();
                    String method = requestLine.getMethod().toUpperCase();
                    String trackingId = null;
                    boolean authenticated = true;
                    if (proxyUserPass != null) {
                        TrackingKey trackingKey = proxyResponseHelper.getTrackingKey(proxyUserPass);
                        if (trackingKey != null) {
                            trackingId = trackingKey.getId();
                            RequestContextHelper.setContext(RequestContextHelper.CONTENT_TRACKING_ID, trackingId);
                        }
                        String authCacheKey = generateAuthCacheKey(proxyUserPass);
                        Boolean isAuthToken = proxyAuthCache.get(authCacheKey);
                        if (Boolean.TRUE.equals(isAuthToken)) {
                            authenticated = true;
                            logger.debug("Found auth key in cache");
                        } else {
                            logger.debug("Passing BASIC authentication credentials to Keycloak bearer-token translation authenticator");
                            authenticated = proxyAuthenticator.authenticate(proxyUserPass, http);
                            if (authenticated) {
                                proxyAuthCache.put(authCacheKey, Boolean.TRUE, config.getAuthCacheExpirationHours(), TimeUnit.HOURS);
                            }
                        }
                        logger.debug("Authentication done, result: {}", authenticated);
                    }
                    if (authenticated) {
                        switch(method) {
                            case GET_METHOD:
                            case HEAD_METHOD:
                                {
                                    final URL url = new URL(requestLine.getUri());
                                    logger.debug("getArtifactStore starts, trackingId: {}, url: {}", trackingId, url);
                                    ArtifactStore store = proxyResponseHelper.getArtifactStore(trackingId, url);
                                    proxyResponseHelper.transfer(http, store, url.getPath(), GET_METHOD.equals(method), proxyUserPass, meter);
                                    break;
                                }
                            case OPTIONS_METHOD:
                                {
                                    http.writeStatus(ApplicationStatus.OK);
                                    http.writeHeader(ApplicationHeader.allow, ALLOW_HEADER_VALUE);
                                    break;
                                }
                            case CONNECT_METHOD:
                                {
                                    if (!config.isMITMEnabled()) {
                                        logger.debug("CONNECT method not supported unless MITM-proxying is enabled.");
                                        http.writeStatus(ApplicationStatus.BAD_REQUEST);
                                        break;
                                    }
                                    // e.g, github.com:443
                                    String uri = requestLine.getUri();
                                    logger.debug("Get CONNECT request, uri: {}", uri);
                                    String[] toks = uri.split(":");
                                    String host = toks[0];
                                    int port = parseInt(toks[1]);
                                    directed = true;
                                    // After this, the proxy simply opens a plain socket to the target server and relays
                                    // everything between the initial client and the target server (including the TLS handshake).
                                    SocketChannel socketChannel;
                                    ProxyMITMSSLServer svr = new ProxyMITMSSLServer(host, port, trackingId, proxyUserPass, proxyResponseHelper, contentController, cacheProvider, config, meter);
                                    tunnelAndMITMExecutor.submit(svr);
                                    socketChannel = svr.getSocketChannel();
                                    if (socketChannel == null) {
                                        logger.debug("Failed to get MITM socket channel");
                                        http.writeStatus(ApplicationStatus.SERVER_ERROR);
                                        svr.stop();
                                        break;
                                    }
                                    sslTunnel = new ProxySSLTunnel(sinkChannel, socketChannel, config);
                                    tunnelAndMITMExecutor.submit(sslTunnel);
                                    // client input will be directed to target socket
                                    proxyRequestReader.setProxySSLTunnel(sslTunnel);
                                    // When all is ready, send the 200 to client. Client send the SSL handshake to reader,
                                    // reader direct it to tunnel to MITM. MITM finish the handshake and read the request data,
                                    // retrieve remote content and send back to tunnel to client.
                                    http.writeStatus(ApplicationStatus.OK);
                                    http.writeHeader("Status", "200 OK\n");
                                    break;
                                }
                            default:
                                {
                                    http.writeStatus(ApplicationStatus.METHOD_NOT_ALLOWED);
                                }
                        }
                    }
                }
                logger.debug("Response complete.");
            } catch (final Throwable e) {
                error = e;
            } finally {
                mdcManager.clear();
            }
        }
        if (error != null) {
            handleError(error, http);
        }
        try {
            if (directed) {
                // do not close sink channel
                ;
            } else {
                http.close();
            }
        } catch (final IOException e) {
            logger.error("Failed to shutdown response", e);
        }
    } finally {
        meter.reportResponseSummary();
        span.ifPresent(SpanAdapter::close);
    }
}
Also used : SocketChannel(java.nio.channels.SocketChannel) HttpConduitWrapper(org.commonjava.indy.httprox.util.HttpConduitWrapper) UserPass(org.commonjava.indy.subsys.http.util.UserPass) IOException(java.io.IOException) URL(java.net.URL) TrackingKey(org.commonjava.indy.folo.model.TrackingKey) IndyDataException(org.commonjava.indy.data.IndyDataException) RequestLine(org.apache.http.RequestLine) ArtifactStore(org.commonjava.indy.model.core.ArtifactStore) ProxyMeter(org.commonjava.indy.httprox.util.ProxyMeter) ProxyResponseHelper(org.commonjava.indy.httprox.util.ProxyResponseHelper) SpanAdapter(org.commonjava.o11yphant.trace.spi.adapter.SpanAdapter)

Example 4 with UserPass

use of org.commonjava.indy.subsys.http.util.UserPass in project indy by Commonjava.

the class ProxyResponseHelper method createRepo.

/**
 * Create repositories (group, remote, hosted) when trackingId is present. Otherwise create normal remote
 * repository with specified name.
 *
 * @param trackingId
 * @param url
 * @param name distinct remote repository name. null if trackingId is given
 */
private ProxyCreationResult createRepo(String trackingId, URL url, String name) throws IndyDataException {
    UrlInfo info = new UrlInfo(url.toExternalForm());
    UserPass up = UserPass.parse(ApplicationHeader.authorization, httpRequest, url.getAuthority());
    String baseUrl = getBaseUrl(url, false);
    logger.debug(">>>> Create repo: trackingId=" + trackingId + ", name=" + name);
    ProxyCreationResult result = repoCreator.create(trackingId, name, baseUrl, info, up, LoggerFactory.getLogger(repoCreator.getClass()));
    ChangeSummary changeSummary = new ChangeSummary(ChangeSummary.SYSTEM_USER, "Creating HTTProx proxy for: " + info.getUrl());
    RemoteRepository remote = result.getRemote();
    if (remote != null) {
        storeManager.storeArtifactStore(remote, changeSummary, false, true, new EventMetadata());
    }
    HostedRepository hosted = result.getHosted();
    if (hosted != null) {
        storeManager.storeArtifactStore(hosted, changeSummary, false, true, new EventMetadata());
    }
    Group group = result.getGroup();
    if (group != null) {
        storeManager.storeArtifactStore(group, changeSummary, false, true, new EventMetadata());
    }
    return result;
}
Also used : Group(org.commonjava.indy.model.core.Group) UrlInfo(org.commonjava.indy.util.UrlInfo) ProxyCreationResult(org.commonjava.indy.httprox.handler.ProxyCreationResult) UserPass(org.commonjava.indy.subsys.http.util.UserPass) RemoteRepository(org.commonjava.indy.model.core.RemoteRepository) ChangeSummary(org.commonjava.indy.audit.ChangeSummary) EventMetadata(org.commonjava.maven.galley.event.EventMetadata) HostedRepository(org.commonjava.indy.model.core.HostedRepository)

Example 5 with UserPass

use of org.commonjava.indy.subsys.http.util.UserPass in project indy by Commonjava.

the class AbstractProxyRepositoryCreatorTest method testGetDistinctName.

@Ignore
@Test
public void testGetDistinctName() {
    AbstractProxyRepositoryCreator creator = new AbstractProxyRepositoryCreator() {

        @Override
        public ProxyCreationResult create(String trackingID, String name, String baseUrl, UrlInfo urlInfo, UserPass userPass, Logger logger) {
            return null;
        }
    };
    String host = "127-0-0-1";
    String port = "80";
    List<String> names = new ArrayList<>();
    String name_0 = "httprox_" + host + "_" + port;
    String name_1 = name_0 + "_1";
    String name_2 = name_0 + "_2";
    String name_3 = name_0 + "_3";
    String name_4 = name_0 + "_4";
    names.add(name_0);
    String name = creator.getNextName(names);
    assertThat(name, equalTo(name_1));
    names.add(name_2);
    names.add(name_1);
    names.add(name_3);
    name = creator.getNextName(names);
    assertThat(name, equalTo(name_4));
}
Also used : UrlInfo(org.commonjava.indy.util.UrlInfo) ArrayList(java.util.ArrayList) UserPass(org.commonjava.indy.subsys.http.util.UserPass) Logger(org.slf4j.Logger) AbstractProxyRepositoryCreator(org.commonjava.indy.httprox.handler.AbstractProxyRepositoryCreator) Ignore(org.junit.Ignore) Test(org.junit.Test)

Aggregations

UserPass (org.commonjava.indy.subsys.http.util.UserPass)6 RemoteRepository (org.commonjava.indy.model.core.RemoteRepository)3 UrlInfo (org.commonjava.indy.util.UrlInfo)3 IOException (java.io.IOException)2 URL (java.net.URL)2 ArrayList (java.util.ArrayList)2 RequestLine (org.apache.http.RequestLine)2 ChangeSummary (org.commonjava.indy.audit.ChangeSummary)2 IndyDataException (org.commonjava.indy.data.IndyDataException)2 HttpConduitWrapper (org.commonjava.indy.httprox.util.HttpConduitWrapper)2 EventMetadata (org.commonjava.maven.galley.event.EventMetadata)2 HeaderMap (io.undertow.util.HeaderMap)1 HttpString (io.undertow.util.HttpString)1 SocketChannel (java.nio.channels.SocketChannel)1 TrackingKey (org.commonjava.indy.folo.model.TrackingKey)1 AbstractProxyRepositoryCreator (org.commonjava.indy.httprox.handler.AbstractProxyRepositoryCreator)1 ProxyCreationResult (org.commonjava.indy.httprox.handler.ProxyCreationResult)1 ProxyMeter (org.commonjava.indy.httprox.util.ProxyMeter)1 ProxyResponseHelper (org.commonjava.indy.httprox.util.ProxyResponseHelper)1 ArtifactStore (org.commonjava.indy.model.core.ArtifactStore)1