I would like to use the Jira REST Client API for Java in an application that needs to go through a proxy to access the desired Jira instance. Unfortunately I didn't find a way to set it when using the given factory from that library:
JiraRestClientFactory factory = new AsynchronousJiraRestClientFactory();
String authentication = Base64.getEncoder().encodeToString("username:password".toBytes());
return factory.createWithAuthenticationHandler(URI.create(JIRA_URL), new BasicAuthenticationHandler(authentication));
How can we use the Jira API and set a proxy ?
The only solution I found on the internet was to set it with system parameters (see solution 1 below). Unfortunately that did not fit my requirements as in the company I work for, there are multiple proxies and depending on the service to call, it has to use another proxy configuration. In that case, I cannot set the system properties without destroying all calls to other services that would need another proxy.
Nevertheless, I was able to find a way to set it by re-implementing some classes (see solution 2).
Important limitation: the proxy server must not ask for credentials.
Context
Maybe as context before, I created a class containing proxy configuration:
#Data
#AllArgsConstructor
public class ProxyConfiguration {
public static final Pattern PROXY_PATTERN = Pattern.compile("(https?):\\/\\/(.*):(\\d+)");
private String scheme;
private String host;
private Integer port;
public static ProxyConfiguration fromPath(String path) {
Matcher matcher = PROXY_PATTERN.matcher(path);
if (matcher.find()) {
return new ProxyConfiguration(matcher.group(1), matcher.group(2), toInt(matcher.group(3)));
}
return null;
}
public String getPath() {
return scheme + "://" + host + ":" + port;
}
}
Set system properties for proxy
Call the following method with your proxy configuration at the start of the application or before using the Jira REST API:
public static void configureProxy(ProxyConfiguration proxy) {
if (proxy != null) {
System.getProperties().setProperty("http.proxyHost", proxy.getHost());
System.getProperties().setProperty("http.proxyPort", proxy.getPort().toString());
System.getProperties().setProperty("https.proxyHost", proxy.getHost());
System.getProperties().setProperty("https.proxyPort", proxy.getPort().toString());
}
}
Re-implement AsynchronousHttpClientFactory
Unfortunately, as this class has many private inner classes and methods, you will have to do an ugly copy paste and change the following code to give the wanted proxy configuration:
public DisposableHttpClient createClient(URI serverUri, ProxyConfiguration proxy, AuthenticationHandler authenticationHandler) {
HttpClientOptions options = new HttpClientOptions();
if (proxy != null) {
options.setProxyOptions(new ProxyOptions.ProxyOptionsBuilder()
.withProxy(HTTP, new Host(proxy.getHost(), proxy.getPort()))
.withProxy(HTTPS, new Host(proxy.getHost(), proxy.getPort()))
.build());
}
DefaultHttpClientFactory<?> defaultHttpClientFactory = ...
}
You can then use it (in the following example, my re-implementation of AsynchronousHttpClientFactory is called AtlassianHttpClientFactory):
URI url = URI.create(JIRA_URL);
String authentication = Base64.getEncoder().encodeToString("username:password".toBytes());
DisposableHttpClient client = new AtlassianHttpClientFactory().createClient(url, proxy, new BasicAuthenticationHandler(authentication));
return new AsynchronousJiraRestClient(url, client);
Note that after all those problems, I also decided to write a Jira client library supporting authentication, proxy, multiple HTTP clients and working asynchronously with CompletableFuture.
Related
I know cleartext been disabled by default by android. May I know where exactly I can enable in aosp instead of adding in all packages with network config files?
Where I can permit by adding the below line?
cleartextTrafficPermitted="true
external/okhttp/android/main/java/com/squareup/okttp/Handler
public static OkUrlFactory createHttpOkUrlFactory(Proxy proxy) {
OkHttpClient client = new OkHttpClient();
// Explicitly set the timeouts to infinity.
client.setConnectTimeout(0, TimeUnit.MILLISECONDS);
client.setReadTimeout(0, TimeUnit.MILLISECONDS);
client.setWriteTimeout(0, TimeUnit.MILLISECONDS);
// Set the default (same protocol) redirect behavior. The default can be overridden for
// each instance using HttpURLConnection.setInstanceFollowRedirects().
client.setFollowRedirects(HttpURLConnection.getFollowRedirects());
// Do not permit http -> https and https -> http redirects.
client.setFollowSslRedirects(false);
// Permit cleartext traffic only (this is a handler for HTTP, not for HTTPS).
client.setConnectionSpecs(CLEARTEXT_ONLY);
// When we do not set the Proxy explicitly OkHttp picks up a ProxySelector using
// ProxySelector.getDefault().
if (proxy != null) {
client.setProxy(proxy);
}
// OkHttp requires that we explicitly set the response cache.
OkUrlFactory okUrlFactory = new OkUrlFactory(client);
// Use the installed NetworkSecurityPolicy to determine which requests are permitted over
// http.
OkUrlFactories.setUrlFilter(okUrlFactory, CLEARTEXT_FILTER);
ResponseCache responseCache = ResponseCache.getDefault();
if (responseCache != null) {
AndroidInternal.setResponseCache(okUrlFactory, responseCache);
}
return okUrlFactory;
}
private static final class CleartextURLFilter implements URLFilter {
#Override
public void checkURLPermitted(URL url) throws IOException {
String host = url.getHost();
if (!NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(host)) {
throw new IOException("Cleartext HTTP traffic to " + host + " not permitted");
}
}
}
In any apps if I use http, I get error as Cleartext HTTP traffic to 124.60.5.6 not permitted";
So instead of changing in apps, is it possible to change in aosp?
Seems like its enough if you do
builder.setCleartextTrafficPermitted(true);
in line 189 seems sufficient since you are using older applications which probably doesn't have any network config and only uses default ones.
source: https://android.googlesource.com/platform/frameworks/base.git/+/refs/heads/master/core/java/android/security/net/config/NetworkSecurityConfig.java#189
Old Answer
I hope you have done your homework on the implications on bypassing a security feature. That being said, the class responsible for the exception is in framework with package android.security.net.config and class responsible is NetworkSecurityConfig.
As of writing this answer, the static builder class has a property boolean mCleartextTrafficPermittedSet which is set to false by default. You might have to default it to true which makes the method getEffectiveCleartextTrafficPermitted() in the NetworkSecurityConfig class return mCleartextTrafficPermitted which in return returns DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED which is by default set to true
The flow would be
getEffectiveCleartextTrafficPermitted() returns mCleartextTrafficPermitted returns DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED returns true by default.
If this is all confusing, call setCleartextTrafficPermitted(true) on the builder whenever the builder is created.
The source for the class is available here: https://android.googlesource.com/platform/frameworks/base.git/+/refs/heads/master/core/java/android/security/net/config/NetworkSecurityConfig.java
Note: I have not tried this and merely gone through the source and inferred the above. You are welcome to try and correct me if something is wrong.
Edit by updating from #Shadow:
In NetworkSecurityConfig, change the boolean variable from true to false.
//public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = false;
Also in ManifestConfigSource, comment the below line,
/*boolean usesCleartextTraffic =
(mApplicationInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0
&& mApplicationInfo.targetSandboxVersion < 2;*/
and directly apply as usesCleartextTraffic as true.
boolean usesCleartextTraffic =true;
You need to go to AndroidManifest.xml and add
<application
android:usesCleartextTraffic="true"
android:networkSecurityConfig="#xml/network_security_config"
....
</application>
I strongly advise that you create the network_security_config to only allow your domain and subdomain. Here is a quick tutorial
I need to change the thread pool of the underlying Grizzly transport layer.
According to the docs of GrizzlyHttpServerFactory:
Should you need to fine tune the underlying Grizzly transport layer, you can obtain direct access to the corresponding Grizzly structures with server.getListener("grizzly").getTransport().
and
To make certain options take effect, you need to work with an inactive HttpServer instance (that is the one that has not been started yet). To obtain such an instance, use one of the below factory methods with start parameter set to false
Since I like to put my self in the worse situations :-) the method I need shuld be:
HttpServer server= GrizzlyHttpServerFactory
.createHttpServer(getURI(), this.config, serviceLocator, false);
but the only method available (nearest to my case) is:
public static HttpServer createHttpServer(final URI uri,
final GrizzlyHttpContainer handler, final boolean secure,
final SSLEngineConfigurator sslEngineConfigurator, final boolean start) {
//....
}
If I understand the GrizzlyHttpContainer is private so I should use:
GrizzlyHttpContainer httpContainer =
new GrizzlyHttpContainerProvider().createContainer(GrizzlyHttpContainer.class, config);
But, since I'm sharing a ServiceLocator between resources and internal classes (a couple of ActiveMQ subscribers). I wonder if it were possible to achieve something like this:
GrizzlyHttpContainer httpContainer =
new GrizzlyHttpContainerProvider()
.createContainer(GrizzlyHttpContainer.class, configuration, serviceLocator);
Ideally what i need is a method like this:
public class GrizzlyHttpContainerProvider implements ContainerProvider {
#Override
public <T> T createContainer(Class<T> type, Application application, Object parentContext) throws ProcessingException {
if (HttpHandler.class == type || GrizzlyHttpContainer.class == type) {
return type.cast(new GrizzlyHttpContainer(application, parentContext));
}
return null;
}
}
Any suggestion about how to achieve this?
I'd would prefer a cleaner solution then creating the server with one of the provided methods that (for my case) auto start the server. Then stop it (waiting for termination somehow) and then finally:
this.server.getListener("grizzly").getTransport().setWorkerThreadPool(....);
and restarting it.
Best Regards,
Luca
Edit
This is cheating :-) ... this is the "dark way" (don't do it at home):
private GrizzlyHttpContainer getGrizzlyHttpContainer(final Application application,
final Object context) {
try {
Class<?> cls = Class.forName(
"org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer");
Constructor<?> cons = cls.getDeclaredConstructor(Application.class, Object.class);
//System.out.println("Constructor Name--->>>"+cons.getName());
cons.setAccessible(true);
return (GrizzlyHttpContainer)cons.newInstance(application, context);
} catch (Exception err) {
return null;
}
}
I am using Spotify web API to get access to user data:
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
final String clientId = "clientId";
final String clientSecret = "clientSecret";
final String redirectURI = "http://localhost:8080/callback/";
final Api api = Api.builder()
.clientId(clientId)
.clientSecret(clientSecret)
.redirectURI(redirectURI)
.build();
/* Set the necessary scopes that the application will need from the user */
final List<String> scopes = Arrays.asList("user-read-private", "user-read-email");
/* Set a state. This is used to prevent cross site request forgeries. */
final String state = "someExpectedStateString";
String authorizeURL = api.createAuthorizeURL(scopes, state);
/* Here connecting to authorizeURL */
}
}
Then I connect to authorizeUrl and get the access token (code) from redirectUri which looks like this: localhost:8080/callback/?code=123
I can get the code and display it using Spring controller in my localhost:
#RestController
public class TokenController {
private static final String template = "Your Spotify acces code: %s";
#RequestMapping("/callback/")
public Token tokenValue(#RequestParam(value="code", defaultValue="Spotify access code") String value) {
return new Token(String.format(template, value));
}
}
How do I pass this code back to my Application so that I can finish the process of authorization?
I initially stored it within the http server and then polled the http server from my application by another API method. Very clunky and potentially insecure.
A later solution was to embed some lightweight http server classes within my application. This allowed me to simply read some class variables. It took quite a lot of integration but the solution is quite clean. It seems a lot of code for one value.
I'm still not happy with this but it will do for now.
we are using JAXWS metro client to interface with a 3rd Party .Net web service. We need to maintain state with the web service.
So, here's the scenario. There are several user applications that would invoke the metro client which in turn invokes the .Net web service.
I've run the wsimport tool and generated the necessary classes.
But since, we have to maintain the state, I'm thinking implement object pool of the service class.
That way, each user app is always married to the specific service object that it is using.
So, the flow would be:
COSServiceImpl -> COSServiceFactory instantiates/maintains COSService (wsimport generated service class that will be pooled) -> .Net web service.
So, the implementation is as follows. Anyone has any better suggestions? Thoughts?
UserApp.java
COSServiceImpl impl = new COSServiceImpl();
ClaimantAccount claimantAccount = impl.getClaimantAccount(String claimantID)
COSServiceImpl.java
public ClaimantAccount getClaimantAccount(String claimantID) {
ICOSService port = COSServiceFactory.getCOSServicePort();
ClaimantInfo info = port.retrieveClaimantInfo(claimantID);
ClaimantAccount account = new ClaimantAccount();
account.setXXX(info.getXXX);
return account;
}
COSServiceFactory.java
public class COSServiceFactory extends BasePoolableObjectFactory<COSService> {
private static GenericObjectPool<COSService> servicePool = null;
static {
try {
init();
} catch(Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static void init() {
servicePool = new GenericObjectPool<COSService>(new COSServiceFactory());
for (int i=0; i < poolSize; i++) {
servicePool.addObject();
}
public COSService makeObject() throws Exception {
URL wsdlURL = null;
service = new COSService(wsdlURL, new QName(nameSpace,localPart) );
return service;
}
private static COSService getCOSService() {
COSService service = null;
try {
service = (COSService) servicePool.borrowObject();
} catch (Exception e) {
e.printStackTrace();
}
return service;
}
public static ICOSService getWebServicePort() {
ICOSService port = getCOSService().getWSHttpBindingICOSService();
BindingProvider bindingProvider = (BindingProvider) port;
// Is there any other place to set the request timeout, may be a handler???
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.request.timeout", Config.getIntProperty("request.timeout"));
return port;
}
Also, is there any other place where we can set the request timeout? Is it okay do it this way? With the above code, I don't think we are modifying the port object. I haven't tested this yet, but will the request timeout property work?
Thanks and appreciate your comments.
Vijay Ganapathy
If it helps anyone, here is what my understanding is:
We don't need to pool the Service instances although we can. Based on my testing, it seems to be working fine.
The reason we don't need to pool the service objects is that when we invoke the Service.getPort() method of the service class to create the ICOSService web service port object, the getPort() method returns a new ICOSService object everytime using the java.lang.reflect.Proxy's newProxyInstance method.
On the server-side I have a ListenerManager which fires callbacks to its Listeners. The manager is exported using a Spring RmiServiceExporter
On the client-side I have a proxy to the manager created by an RmiProxyFactoryBean, and a Listener implementation registered through this proxy with the manager on the server side.
So far so good: the ListenerManager is given a Listener and it invokes its callbacks, however since the listener is just a deserialized copy of the client-side object, the callback runs on the server side, not the client side.
How can I get Spring to generate a proxy on the server-side to the client-side listener so that the callback invoked by the server is executed remotely on the client-side? Surely I don't need another (exporter, proxy factory) pair in the opposite direction?
A pure RMI solution: the client-side listener object needs to implement java.rmi.server.UnicastRemoteObject. If it does, and each of its methods throw RemoteException then when it is passed to the server through the manager proxy everything is wired up automatically, and method invocations on the server-side proxy to this listener are remote invocations of methods on the real client-side object.
This will do, but it's even better to be able to wrap the object for export without requiring a particular superclass. We can use a CGLIB Enhancer to "proxy" the listener as a subclass of UnicastRemoteObject that also implements the service interfaces. This still requires that the target object implement java.rmi.Remote and declare throws RemoteException.
Next step is a solution that can export arbitrary objects for remote invocation of their methods, without requiring that they implement Remote or declare throws RemoteException. We must integrate this proxying with the existing Spring infrastructure, which we can do with a new implementation of RmiBasedExporter modelled on the non-registry bits of RmiServiceExporter#prepare() to export the RMI stub of our proxy and on the invocation part of RmiClientInterceptor.doInvoke(MethodInvocation, RmiInvocationHandler). We need to be able to get hold of an exported proxy instance of our service interfaces. We can model this on the means used by Spring to apparently "export" non-RMI interfaces. Spring proxies the interface to generate a RmiInvocationWrapper for invocation of a non-RMI method, serialises the method details and arguments, then invokes this on the far side of the RMI connection.
Use a ProxyFactory and an RmiInvocationHandler implementation to proxy the target object.
Use a new implementation of RmiBasedExporter to getObjectToExport(), and export it using UnicastRemoteObject#export(obj, 0).
For the invocation handler, rmiInvocationHandler.invoke(invocationFactory.createRemoteInvocation(invocation)), with a DefaultRemoteInvocationFactory.
Handle exceptions and wrap appropriately to avoid seeing UndeclaredThrowableExceptions.
So, we can use RMI to export arbitrary objects. This means we can use one of these objects on the client-side as a parameter to an RMI method call on an RMI server-side object, and when the deserialised stub on the server-side has methods invoked, those methods will execute on the client-side. Magic.
Following Joe Kearney's explaination, I have created my RMIUtil.java. Hope there is nothing left.
BTW, please ref this
for "java.rmi.NoSuchObjectException: no such object in table"
Just add some code to Joe's answer.
Extends RmiServiceExporter and get access to exported object:
public class RmiServiceExporter extends org.springframework.remoting.rmi.RmiServiceExporter {
private Object remoteService;
private String remoteServiceName;
#Override
public Remote getObjectToExport() {
Remote exportedObject = super.getObjectToExport();
if (getService() instanceof Remote && (
getServiceInterface() == null || exportedObject.getClass().isAssignableFrom(getServiceInterface()))) {
this.remoteService = exportedObject;
}
else {
// RMI Invokers.
ProxyFactory factory = new ProxyFactory(getServiceInterface(),
new RmiServiceInterceptor((RmiInvocationHandler) exportedObject, remoteServiceName));
this.remoteService = factory.getProxy();
}
return exportedObject;
}
public Object getRemoteService() {
return remoteService;
}
/**
* Override to get access to the serviceName
*/
#Override
public void setServiceName(String serviceName) {
this.remoteServiceName = serviceName;
super.setServiceName(serviceName);
}
}
The interceptor used in the proxy (the remote service callback):
public class RmiServiceInterceptor extends RemoteInvocationBasedAccessor
implements MethodInterceptor, Serializable {
private RmiInvocationHandler invocationHandler;
private String serviceName;
public RmiServiceInterceptor(RmiInvocationHandler invocationHandler) {
this(invocationHandler, null);
}
public RmiServiceInterceptor(RmiInvocationHandler invocationHandler, String serviceName) {
this.invocationHandler = invocationHandler;
this.serviceName = serviceName;
}
/**
* {#inheritDoc}
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocationHandler.invoke(createRemoteInvocation(invocation));
}
catch (RemoteException ex) {
throw RmiClientInterceptorUtils.convertRmiAccessException(
invocation.getMethod(), ex, RmiClientInterceptorUtils.isConnectFailure(ex),
extractServiceUrl());
}
}
/**
* Try to extract service Url from invationHandler.toString() for exception info
* #return Service Url
*/
private String extractServiceUrl() {
String toParse = invocationHandler.toString();
String url = "rmi://" + StringUtils.substringBefore(
StringUtils.substringAfter(toParse, "endpoint:["), "]");
if (serviceName != null)
url = StringUtils.substringBefore(url, ":") + "/" + serviceName;
return url;
}
}
When exporting the service with this RmiServiceExporter, we cand send a rmi callback with:
someRemoteService.someRemoteMethod(rmiServiceExporter.getRemoteService());