ratpack: implementing a custom PublicAddress to force https - https

I was on a pac4j mail thread discussing why the redirect url ratpack-pac4j uses is using http even when the original page request is served over https. I did some digging and it looks like ratpack-pac4j is using the InferringPublicAddress. This works for local development, but because my elasticbean host proxies 443 to 80, ratpack-pac4j thinks it's over http and uses http for the redirect url. I want this call to be over https when on the server.
I'm trying to register a custom PublicAddress class that always returns http in development mode, but https in production mode:
.registry(Guice.registry(b -> {
b
.module(SessionModule.class)
.module(ThymeleafModule.class, conf -> conf.templatesMode("LEGACYHTML5"))
.add(PublicAddress.class, ForcedHttpsPublicAddress.create()); //PublicAddress.class, PublicAddress.inferred("https"));
}))
...
static private class ForcedHttpsPublicAddress implements PublicAddress
{
static ForcedHttpsPublicAddress create() {
return new ForcedHttpsPublicAddress();
}
ForcedHttpsPublicAddress() {
}
}
But when I call ctx.get(PublicAddress.class), it's still returning InferringPublicAddress. Am I registering the custom PublicAddress incorrectly?

Got help on the Ratpack forum. I needed to bind it instead of add it.
.bind(PublicAddress.class, ForcedHttpsPublicAddress.class)

Related

Error during https call through proxy using CXF

In camel-cxf I have to call a SOAP webservice (exposed in https) through a proxy: configuring the http conduit as follows
public void configureClient(Client client) {
String proxySrv = Util.getProperty(Constants.Config.PROXY_SRV);
int proxyPort = new Integer(Util.getProperty(Constants.Config.PROXY_PORT));
log.info("Configurazione del server proxy:'"+proxySrv+"' port:'"+proxyPort+"'");
HTTPConduit conduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
policy.setProxyServer(proxySrv); // set proxy host
policy.setProxyServerPort(proxyPort); // set proxy port
policy.setProxyServerType(ProxyServerType.SOCKS);
conduit.setClient(policy);
conduit.setAuthSupplier(new DefaultBasicAuthSupplier());
boolean proxyAuthEnabled = new Boolean(Util.getProperty(Constants.Config.PROXY_AUTH_EN));
String user = Util.getProperty(Constants.Config.PROXY_USER);
String pass = Util.getProperty(Constants.Config.PROXY_PASS);
log.info("Recuperati username:'+"+user+"' e password per il proxy:'"+proxySrv+"' port:'"+proxyPort+"'");
if (proxyAuthEnabled) {
ProxyAuthorizationPolicy ap = new ProxyAuthorizationPolicy();
ap.setUserName(user);
ap.setPassword(pass);
conduit.setProxyAuthorization(ap);
// conduit.getAuthorization().setUserName(user);
// conduit.getAuthorization().setPassword(pass);
log.info("Autenticazione abilitata per userName ='"+user+"' per il proxy:'"+proxySrv+"' port:'"+proxyPort+"'");
}
it works for http call (without the proxy server type set) but it doesn't work for https call. This proxy requires basic auth.
Reading various articles I saw that there is a bug in CXF that doesn't send the header authorization in the CONNECT call (and infact I'm getting 407 Authorization required -> even if with the same credentials with http calls it works).
Is there a way to fix it? I read about Olivier Billard solution
https://www.mail-archive.com/users#cxf.apache.org/msg06422.html
but I didn't undestand that solution (and I can't import at code any keystore).
Thanks
Hello I just faced this issue with the apache cxf client, the workaround suggested in the mailing list is to use the following static method of the java.net.Authenticator class :
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("youruser", "yourpassword".toCharArray());
}
});
This way the basic will be set automatically on all your HttpUrlConnection that uses the proxy, since java 8 you also have to enable basic authentication for HTTPS tunneling, you can do this with the following property:
-Djdk.http.auth.tunneling.disabledSchemes=""
I hope this helps

How to allow access cross origin requests ??? ( Angular, Rest, Spring )

i need some help
i am developing an app using angular with restful web service using spring framework
after calling this method from RESTController
#CrossOrigin(origins = "http://localhost:4200")
#RequestMapping(value="/search",method=RequestMethod.GET)
public Page<Bien> chercher(
#RequestParam(name="mc",defaultValue="") String mc,
#RequestParam(name="size",defaultValue="0") int page,
#RequestParam(name="page",defaultValue="5") int size) {
return bienRepository.chercher("%"+mc+"%",new PageRequest(page,size));
}
using
this.http.get('url:http://localhost:8080/searchmc=A').
map(resp=>resp.json()).
subscribe(data=>{this.pageBien=data;},err=>{console.log(err) } );
i got this problem
Failed to load url:http://localhost:8080/search?mc=A: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
Any suggestion please!
Your get request from the client seems incorrect.
The correct one should be :
this.http.get('http://localhost:8080/search?mc=A').map(resp=>resp.json()).
subscribe(data=>{this.pageBien=data;},err=>{console.log(err) } );
What changed:
Drop "url" prefix in the get call
You include the request parameters in the url after the ?, so http://localhost:8080/search?mc=A (Use & for every next request parameter, on example http://localhost:8080/search?mc=A&size=5)

appharbor force HTTPS for static files

I have a requirement to ensure all traffic on my website is redirected to HTTPS if it is requested over HTTP. When we deploy the site to appharbor, we use the custom RequireHttpsAttribute which works well for our MVC controllers.
However we also want to force any request for static files (images, stylesheets, javascript) via HTTP to be sent to HTTPS. Trying it using the web.config rewrite rules ends up in a redirect loop due to the load balancer sending https request to the web server as http.
Does anyone have any ideas on how to achieve this?
After receiving a reply from appharbor support, one of their suggestions was to implement code similar to the custom RequireHttpsAttribute for static files as well.
So I created a class called HttpRequestModule, and set it up to run for all requests (runAllManagedModulesForAllRequests set to true) I was able to force any direct requests to HTTP urls to redirect to HTTPS.
class HttpRequestModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(CheckHttpRequest);
}
private void CheckHttpRequest(object sender, EventArgs a)
{
if (app.Context.Request.IsSecureConnection) return;
if (app.Contact.Request.IsLocal) return;
if (string.Equals(app.Context.Request.Headers["X-Forwarded-Proto"],
"https",
StringComparison.InvariantCultureIgnoreCase))
{
return;
}
var secureUrl = "https://" + app.Context.Request["HTTP_HOST"] + HttpContext.Current.Request.RawUrl;
app.Context.Response.Redirect(secureUrl);
}
}

Can't get ASP.NET Web API 2 Help pages working when using Owin

I've installed the correct package for Web Api 2
Install-Package Microsoft.AspNet.WebApi.HelpPage -Pre
But the help area is not being mapped and is returning 404 (Web Api working fine). I'm using Microsoft.Owin.Host.SystemWeb as the host. Below is my Startup code.
public class Startup
{
public void Configuration(IAppBuilder app)
{
//Required for MVC areas new HttpConfiguration() doesn't work with MVC
var config = GlobalConfiguration.Configuration;
AreaRegistration.RegisterAllAreas();
WepApiStartup.Configure(config);
app.UseWebApi(config);
}
}
GlobalConfiguration.Configuration is web host specific HttpConfiguraiton, which should only be used with web host scenario. Use it with OWIN host will cause unexpected issues.
Please use the following code instead:
public class Startup
{
public static HttpConfiguration HttpConfiguration { get; private set; }
public void Configuration(IAppBuilder app)
{
HttpConfiguration = new HttpConfiguration();
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(HttpConfiguration);
app.UseWebApi(HttpConfiguration);
}
}
Replace all GlobalConfiguration.Configuration with Startup.HttpConfiguration in the project include help page files.
Found the solution after a lot of digging/trial and error. The issue is well described here: http://aspnetwebstack.codeplex.com/discussions/453068
UseWebApi and UseHttpMessageHandler don't call Next OWIN's middleware other than for 404. This means if you use UseWebApi that's it, Next is never called therefore you can't use it with any other middleware (Nancy or Web Api Help pages for example).
Thanks to #aliostad patch:
https://github.com/aliostad/CacheCow/blob/master/samples/UsingCacheCowWithNancyAndOwin/HttpMessageHandlerAdapterModified.cs#L43
You can get it working as expected. I hope the team merge the pull request for this as UseWebApi breaks the Owin design goals IMO.
Update 13 Feb 2014
I've written an Owin extension to workaround this:
internal static void UseWebApiAndHelp(this IAppBuilder app, HttpConfiguration config)
{
WepApiStartup.Configure(config);
app.UseHandlerAsync((request, response, next) =>
{
if (request.Path == "/") //app.Map using a regex exclude list would be better here so it doesn't fire for every request
{
response.StatusCode = 301;
response.SetHeader("Location", "/Help");
return Task.FromResult(0);
}
return next();
});
// Map the help path and force next to be invoked
app.Map("/help", appbuilder => appbuilder.UseHandlerAsync((request, response, next) => next()));
app.UseWebApi(config);
}
Update 01 July 2015
You can also host the help pages using WebApi instead of MVC, which is great for self host http://blogs.msdn.com/b/yaohuang1/archive/2012/12/20/making-asp-net-web-api-help-page-work-on-self-hosted-services.aspx
Update 10 September 2015
For Web Api I tried #hongye-sun answer and it works too, follow what #gspatel says by changing HelpPageAreaRegistration.RegisterArea and the HelpController's constructor. My workaround works as well so pick whatever one works best for your situation.
However I'm still getting the issue when using UseWebApi with other middleware and it not invoking Next() (seems to only happen when using IIS not self host). I've found my workaround of mapping the path and forcing next to be invoked is a valid workaround for all Owin middleware Nancy, Simple.Web etc.
Update 13 Jan 2016
I've developed Owin middleware to generate the ASP.NET Web API Help pages we know and love that completely solves this problem. My blog post explains the background to this issue in detail

Handling URL's with AppHarbor without Modifying All My Controllers

I'm trying to host an MVC 3 application (FunnelWeb) on AppHarbor. For a reason that's still not clear to me, when my route is only a Controller+Action (e.g. mysite/admin is Admin+Index and mysite/login is Admin+login) everything works fine, but if I have anything else in the route (e.g. a variable like {*page}) my URL will be mysite:12345/mypage (where 12345 is a port number assigned by AppHarbor and mypage is the name of the page I'm requesting). This makes the request fail as the port 12345 is not publicly exposed.
AppHarbor uses load balancing to distribute the request between multiple IIS's. This is their way of doing stuff and this is why internally the requests are routed to some non-standard ports. I don't have a problem with that, but I have problem with MVC that tries to route me to that internal URL.
I'm not pointing fingers here; it's nobody's fault :) so let's move to the question:
Why there is a difference between requesting a route with Controller+Action only and requesting a route with a variable like {*page}? Be technical please :)
Here is an example of how to handle requests in AppHarbor, however, it seems that it requires me to modify all my controllers (OMG). Is there any way to implement this without modifying my controllers?
Any other suggestions are welcomed :)
Thanks in advance.
UPDATE: Coincidentally, the behaviour that I observed matches the conclusion that I reached. However, the issue has nothing to do with ASP.Net MVC routing. The short story is, FunnelWeb forces lowercase URL's, so, whenever it receives a request to a resource it convert it to lowercase, if needed, and issue a 301 response. The problem is, when creating the URL for the 301 response, the request URL (absolute URL) is now the URL used when the request made from the load balancer to IIS and not the one made from the client; hence, the request fails.
This is known issue with FunnelWeb url generation on AppHarbor. When using standard MVC methods to generate relative URLs, this is not a problem. AppHarbor has a short guide and sample on how the generate public URLs in the knowledge base.
It's possible that the following is now all you need:
<appSettings>
<!-- AppHarbor Setting to stop AppHb load balancer internal port numbers from showing up in URLs-->
<add key="aspnet:UseHostHeaderForRequestUrl" value="true" />
</appSettings>
This is noted as an update on AppHarbor's support page at http://support.appharbor.com/kb/getting-started/workaround-for-generating-absolute-urls-without-port-number
MSDN says the following about UseHostHeaderForRequestUrl:
aspnet:UseHostHeaderForRequestUrl - If this value attribute is false [default], the Url property is dynamically built from the host, port, and path provided by the web server. If this value attribute is true, the Url property is dynamically built by using the host and port provided by the incoming "Host" header and the path provided by the web server.
There is a way, but it requires a couple of classes.
When ASP.NET MVC registers a route, it defines a route handler. This route handler returns a HTTP handler that handles the request. If you use a custom route handler that returns a custom HTTP handler, you can rewrite the HTTP context by using a couple decorator classes.
Start by creating a HttpContextProxy and HttpRequestProxy that derives from the base classes and wraps all methods and properties to an inner instance. I've made the hard work available.
Next create the decorators, first the HTTP context decorator:
using System.Web;
public class HttpContextDecorator : HttpContextProxy
{
public HttpContextDecorator(HttpContextBase innerHttpContext)
: base(innerHttpContext)
{
}
public override HttpRequestBase Request
{
get
{
return new HttpRequestDecorator(base.Request);
}
}
}
The HTTP request decorator:
using System;
using System.Web;
public class HttpRequestDecorator : HttpRequestProxy
{
public HttpRequestDecorator(HttpRequestBase innerHttpRequest)
: base(innerHttpRequest)
{
}
public override bool IsSecureConnection
{
get
{
return string.Equals(Headers["X-Forwarded-Proto"], "https", StringComparison.OrdinalIgnoreCase);
}
}
public override Uri Url
{
get
{
var url = base.Url;
var urlBuilder = new UriBuilder(url);
if (IsSecureConnection)
{
urlBuilder.Port = 443;
urlBuilder.Scheme = "https";
}
else
{
urlBuilder.Port = 80;
}
return urlBuilder.Uri;
}
}
public override string UserHostAddress
{
get
{
const string forwardedForHeader = "HTTP_X_FORWARDED_FOR";
var forwardedFor = ServerVariables[forwardedForHeader];
if (forwardedFor != null)
{
return forwardedFor;
}
return base.UserHostAddress;
}
}
}
As mentioned, you also need to override the MVC classes - here the HTTP handler:
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
public class CustomMvcHandler : MvcHandler
{
public CustomMvcHandler(RequestContext requestContext)
: base(requestContext)
{
requestContext.HttpContext = new HttpContextDecorator(requestContext.HttpContext);
}
protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
httpContext = new HttpContextDecorator(httpContext);
return base.BeginProcessRequest(httpContext, callback, state);
}
protected override void ProcessRequest(HttpContextBase httpContext)
{
httpContext = new HttpContextDecorator(httpContext);
base.ProcessRequest(httpContext);
}
}
Then the route handler:
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
public class CustomMvcRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CustomMvcHandler(requestContext);
}
}
Finally, you'll need to replace the associated handler for all registered routes (or map them properly from the beginning):
var routes = RouteTable.Routes.OfType<Route>().Where(x => x.RouteHandler is MvcRouteHandler);
foreach (var route in routes)
{
route.RouteHandler = new CustomMvcRouteHandler();
}

Resources