I have the following two lines of codes in my model, however, both virtual and path have values "\". Where have I gone wrong?
var virtual = VirtualPathUtility.ToAbsolute(HttpContext.Current.Request.ApplicationPath);
var path =HttpContext.Current.Request.ApplicationPath;
From MSDN:
Gets the ASP.NET application's virtual application root path on the server.
So this is the part of the URL's path that is the root of the IIS Web Application the code is running in. The root URL ("http://domain/") is always an IIS Application, so will give "/" as its ApplicationPath.
You perhaps need to convert some child (virtual) folder into an IIS Application to see a longer path result.
Updated from comment:
I want to have the part ":/..."
This information is all available within the properties of Request.Uri. In particular "http" is Uri.Scheme, severname is Uri.Host and the port is Uri.Port (but check Uri.IsDefaultPort to check if you need to specify it).
http://weblogs.asp.net/srkirkland/archive/2009/09/17/a-urlhelper-extension-for-creating-absolute-action-paths-in-asp-net-mvc.aspx
Related
Context
I've been running an intranet admin panel in Symfony 3.x for several years. The users login with google oauth and the system checks if the email matches a validated one in a lookup-list. The oauth client handling is done with the "HWI OAuth Bundle".
In order to start a clean way to migrate this admin panel into SF4 and later to SF5 we've started breaking our monolyth into microservices running in docker.
Moving to docker behind a reverse proxy
Today we were moving this admin panel into a docker. Then we are having the public apache2 doing a ProxyPass towards the docker running the admin panel. Let's imagine the docker runs in http://1.2.3.4:7540 Let's assume the public address is https://admin-europe.example.com
What happens is that the symfony application has a relative URL, as the route google_login configured in the routing.yml and in the service configuration defined in the security.yml:
routing:
# Required by the HWI OAuth Bundle.
hwi_oauth_redirect:
resource: "#HWIOAuthBundle/Resources/config/routing/redirect.xml"
prefix: /connect
hwi_oauth_connect:
resource: "#HWIOAuthBundle/Resources/config/routing/connect.xml"
prefix: /connect
hwi_oauth_login:
resource: "#HWIOAuthBundle/Resources/config/routing/login.xml"
prefix: /login
# HWI OAuth Bundle route needed for each resource provider.
google_login:
path: /login/check-google
logout:
path: /logout
security:
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
anonymous: true
logout:
path: /logout
target: /
handlers: [ admin.security.logout.handler ]
oauth:
resource_owners:
google: "/login/check-google"
login_path: /
use_forward: false
failure_path: /
oauth_user_provider:
service: admin.user.provider
So when the application was not dockerized, it run properly because the route requested to be the "redirect route" to google was https://admin-europe.example.com/login/check-google.
Nevertheless, now that it's inside the docker when the HWI bundle is building the data block to send to google it requests for this http://1.2.3.4:7540/login/check-google to be authorised as the "redirect URI" but of course it should not. Of course the redirect URI should continue to be https://admin-europe.example.com/login/check-google.
I naturally get this error message:
The reverse proxy
We already have in the reverse proxy the ProxyPassReverse and, in fact, the very same configuration has been working hassle-free for over a month with another microservice we already successfully moved (but that service did not need auth, was a public site).
This is natural, as ProxyPassReverse will tackle into http data but the google-oauth info-block is not handled by the ProxyPassReverse, as it's natural.
The problem
The problem here is not to have this address validated (put a domain alias into the private IP address, etc.)
The problem here is how to generate the "proper public URL" from inside the docker without creating a hard-dependency for the container contents in function of the environment it's going to run. Doing so would be an anti-pattern.
Exploring solutions
Of course the "easy" solution would be to "hardcode" the "external route" inside the container.
But this has a flaw. If I also want the same docker to be accessed from, say, https://admin-asia.example.com/ (note the -asia instead of the -europe), I'll run into problems as the asia users will be redirected to the europe route. This is a mere example, don't care about the specific europe-asia thing... the point is that the container should not be conscious of the sorrounding architecture. Or at least, conscious to "interact" but definitively not to have "hardcoded" inside the container things that depend on the environment.
Ie: Forget about the -europe and -asia thing. Imagine the access is admin-1111. It does not make sense that I have to "recompile" and "redeploy" the container if one day I want it to be accessible as admin-2222.
Temporal solution
I think it would solve the problem to point both the route in the rounting.yml and the config in the security.yml to a "parameter" (in 3.x in parameters.yml) and then move that into an Environment Variable when updating to SF4, but I'm unsure on how the cache compiler of the symfony would behave with a route that does not have a value, but a route that "changes dynamically".
Then pass the value of the redirecion when the container is started. This would solve the problem only partially: All the container would be bound to a redirect route set at the time of start, but it still would not solve the case of the same container instance accessed via different names thus needing multiple redirect routes. Instead when running non-dockerized that works as it just takes the "hostname" to build the absolute path on a relative-path definition.
Investigation so far
When accessing, the browser shows I'm going to
https://accounts.google.com/o/oauth2/auth
?response_type=code
&client_id=111111111111-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com
&scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.profile.emails.read+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.login
&redirect_uri=http%3A%2F%2Fmy.nice.domain.example.com%3A7040%2Fapp_dev.php%2Flogin%2Fcheck-google
Here we see that the redirect_uri parameter is the place where we'll return after passing the control to google momentarily.
So somebody needs to be building this URL.
I seeked for "redirect_uri" within the source code and I found that the involved classes are GoogleResourceOwner which extends GenericOAuth2ResourceOwner.
Both classes seem to belong to the domain as per the tests passing the $redirectUri as a string which needs to be already built by the caller.
The involved method is public function getAuthorizationUrl($redirectUri, array $extraParameters = array()). This receives the redirect URI and builds the auth URI with the redurect URI encoded as a parameter.
So, who are the consumers/clients of getAuthorizationUrl()?
I only found one single client usage in OAuthUtils, in a line that says return $resourceOwner->getAuthorizationUrl($redirectUrl, $extraParameters); within the function public function getAuthorizationUrl(Request $request, $name, $redirectUrl = null, array $extraParameters = array())
I see that mainly this OAuthUtils is acting as an adapter between the Symfony Request and the OAuth domain model. Within this method we mainly find code to create the $redirectUri.
The cleanest solution for me would be to create a child class OAuthUtilsBehindProxy inheriting from OAuthUtils, overwriting the method getAuthorizationUrl() and having it interpret the X-FORWARDED-* headers of the request, and then have the dependency injection to autowire my class everywhere the OAuthUtils is used with the hope that noone is doing a new OAuthUtils and every user of this class is getting it passed on the constructor.
This would be clean and woul work.
But frankly it seems an overkill to me. I'm pretty sure someone before me has put an app that needs Google OAuth made with HWI behind a reverse proxy and I wonder if there's a "config option" that I'm missing or really I have to re-code all this and inject it via D.I.
So, question
How do I have HWI-OAuth bundle to behave properly when running in a docker container behind a reverse proxy in regards on how to build the "redirect route" for the google-oauth service?
Is there any way to tell either the HWI bundle or either symfony to add a "full-host" prefix IN FUNCTION of the the X-FORWARDED-* headers "if available"? This would leave the docker image "fixed" and would run in "any" environment.
The underlying reason is the way Symfony generated the full-addresses from a relative path or route name.
Here's the investigation:
The method HWI/OAuthUtils::getAuthorizationUrl() is the one that generates the OAUth auth URI and consumes the method Symfony/HttpUtils::generateUri() to get the absolute URI of the redirect_to callback that will be encoded inside the Auth URI.
The method Symfony/HttpUtils::generateUri() generates an absolute URI (that in our case will be the callback) and to do so, the method handles 3 general cases:
The parameter is already an absolute URI (the return is the parameter without further processing)
The parameter is a relative URL (the function calls the Request class to build the proto + host + port + project-path prefix to prepend to the relative URI)
The parameter is a route name (the funcion calls the Router class to build the absolute URI)
In my example I was configuring a relative URL (google: "/login/check-google") in the security.yml so HttpUtils was delegating into the Request class.
Looking at the source of the Request class we observe:
The Request class is able to use proxy headers to build the absolute class.
But for security, by default symfony does not trust that a proxy exists merely because there are X-FORWARDED-* headers in it.
Indeed it's more secure plus more flexible.
There are 2 levels of security:
Somewhere we need to tell the Request class what is the list of trusted IPs that are proxies accessing the application.
Somewhere else we need to tell the Request class what specific proxy headers are trusted and what headers are not, even it supports different standards headers (RFC headers, non-RFC apache headers, etc)
Stated here https://symfony.com/blog/fixing-the-trusted-proxies-configuration-for-symfony-3-3 is that you need to configure the trusted proxies in the front-controller by calling the static method Request::setTrustedProxies();
So adding those couple of lines in the front-controller one killing non-nee4ded headers and the other with the IP ranges of the proxies, solved the problem:
# app.php
<?php
use Symfony\Component\HttpFoundation\Request;
$loader = require __DIR__.'/../app/autoload.php';
include_once __DIR__.'/../var/bootstrap.php.cache';
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
Request::setTrustedHeaderName( Request::HEADER_FORWARDED, null ); # <-- Kill unneeded header.
Request::setTrustedProxies( [ '192.168.104.0/24', '10.0.0.0/8' ] ); # <-- Trust any proxy that lives in any of those two private nets.
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
With this change:
Symfony Request is able to build correct public absolute addresses from relative addresses if called thru a proxy, by deducting the host from HTTP_X_FORWARDED_HOST and HTTP_X_FORWARDED_PORT instead of HTTP_HOST and SERVER_PORT.
Symfony HttpUtils also, as it was delegating to Request.
HWI is in turn able to build a correct absolute callback redirect_to.
HWI can set the proper callback encoded inside the AuthUri.
The AuthURI that contains the proper absolute URI taking in account the proxy effect is sent to google.
Google sees the "public URI" as the one registered in the google configuration.
The workflow completes and the login process can end successfully.
I have a site in Umbraco 7.7.2 and I need to take the relative Url my pages. I use the method Url and it works on my machine but when I upload my code to my production (on Azure-cloud configuration) it returns the absolute Url instead :|. what should I do?
My site's structure is simple so I have access to those specific pages like this:
var umbraco = new UmbracoHelper(UmbracoContext.Current);
var startPage= umbraco.TypedContentAtRoot().First();
var specificPage= startPage.FirstChild(c => c.IsDocumentType("specificPage"));
var url= specificPage.Url; // returns relative address on my local and absolute url on cloud
The URL it returns depends on the hostnames configured for the site, and the setting in /config/umbracoSettings.config.
In umbracoSettings.config you can set the "useDomainPrefixes" property to true which tells Umbraco to include the domains in the URLs it generates. Is that set n live?
The other thing to check is if the Hostnames are set on the home node in the back office.
When you say relative, do you mean root relative? E.g. /my-page/ as opposed to http://example.com/my-page/? As far as I know Umbraco shouldn't return actual relative links to the page you're on.
I'm using ServiceStack in an MVC4, VS2012 project running on windows7. I'm attempting to call a default "/search" route when the application loads. To do this I have the following code in my AppHost Configure method
Plugins.Add(new RazorFormat());
SetConfig(new EndpointHostConfig
{
ServiceStackHandlerFactoryPath = "api",
DefaultRedirectPath = "/search"
});
If I call this route directly (via /api/search) it works correctly. However when I run my project I simply get a HTTP Error 403.14 - Forbidden error. It's appears to be attempting to locate a static source document from the website root (I've removed all of these) rather than the dynamic route specified in DefaultRedirectPath
I've also added a HttpHandler via the CatchAllHandlers method to see if the route is being attempted but it appears the DefaultRedirect is simply not happening.
Any suggestions would be greatly appreciated. From everything I've read this should just work.
I'm assuming that you've set up your /api path correctly in web.config too.
DefaultRedirectPath is just a simple redirect used when you request the root of your API (i.e. "/api"). It literally returns a 302 with your DefaultRedirectPath as the Location header (combined with the application root URL, if your ASP.NET application isn't at the root of the server). In other words, it's not a "route", just a relative URL. And in your case, it will redirect to /search in the root of your application, not to /api/search.
It should work, if you use DefaultRedirectPath = "/api/search".
However, when ServiceStack isn't at the root of the website, it will use MetadataRedirectPath first, and only if that is null or empty, DefaultRedirectPath. So you'll need to set MetadataRedirectPath to null, if this is what you want.
As for your test with CatchAllHandlers, as far as I can tell, CatchAllHandlers will actually cause your DefaultRedirect to not be used - the CatchAllHandler will be used as a handler, and the DefaultRedirectHandler that does the DefaultRedirectPath redirect won't ever come into play.
That will take care of redirecting from "/api" to "/api/search".
MVC is in control of your root url - the "api" addition to web.config and AppHost.Config does exactly that - keep ServiceStack only in control of "/api", while letting MVC take care of the rest. So, with that setup, if you want a redirect from "/" to "/api/search", you'd need to do it in the MVC home controller.
I am changing the way links show on my web site. I changed from allowing space in the URL to a new format where the URL has dashes where spaces used to be.
This effects only ONE string in the middle of the URL.
Google has indexed many of my pages with the old spaces in the URL but now they show up as 404s. Is it possible for me to put some code in place (temporary) that can redirect those URLs with spaces to the ones with dashes. I think it's a 403 redirect. A permanent redirect.
Thanks,
We wen't through the same thing recently. We ended up creating a LegacyController, which basically called into RedirectToActionPermanent or RedirectToRoutePermanent. (HTTP 301 - Moved Permanently).
Ideally, you should let IIS7 do the redirects, but we couldn't, because we needed to call our DB in order to figure out where to go.
If your redirect is as simple as you say it is (e.g no "dynamic" info in the URL), then you should use IIS.
Why don't you try to configure you routing to support both: legacy and new routes?
Basically /a b c/page and /a-b-c/page should be mapped to the same action of controller.
hi is there a wait to load a full url.?
url= 'http://www.example.com/whatever.php'
$('#selector').load(url); // this way returns null (empty result)
instead of :
url = 'whatever.php'
$('#selector').load(url); // works fine
Some may think whats the difference i want to use this because im using multiple directories. so i could be on a page like...
example.com/dir/
but the dir folder will not have the whatever.php
so anyone has a fix for this that i should always use the full url?
thank you.
You could always use relative paths
putting / before the path will tell the browser to go the root of the page. For your example you could call /whatever.php.
You can also move up one directory at a time. Lets say you are in a page at http://www.example.com/dir/foo/bar.php and want to access something in the dir folder, you could specify ../inTheDir.php to move up one directory or ../../inTheRoot.php to move up two.
This should work for you, but based on your comment it sounds like you have a problem somewhere else since your www. page doesn't seem to respond correctly.
No, there isn't.
If http://www.example.com/ takes longer to load than http://example.com/ then it is probably because you have the DNS record for example.com cached but not the record for www.example.com.
Corrected after having realized a typo changed the meaning of the question.:
This is a case of having a mismatch between the host name the page is loaded from and the host name the Ajaxed resource is requested from. i.e. The Same Origin Policy.
Pick a host name to be canonical, use that one in your requests, and redirect (with a 301 status code) from the other so that people don't go to the wrong one by mistake.