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);
}
}
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;
}
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);
}
}
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;
}
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));
}
Aggregations