How to expose only some routes depending on host - vapor

I have a Vapor app that needs to do most things authenticated via HTTPS, but also needs to receive unauthenticated PUT requests via HTTP.
Can I conditionalize my route definitions based on the server's host name or authentication type? How can I capture that information from the server?

If you start up the different instances of vapor using the command line argument --hostname, you can put this code in your configure.swift and then include different routes as needed per host. You will then get 404s if invalid routes are attempted on the wrong hosts.
if let index = env.arguments.index(of: "--hostname")
{
if env.arguments.count > index
{
let hostname = env.arguments[index+1]
if hostname == "hostA"
{
// load routes
}
else
{
// load other routes
}
}
}
An alternative is to use custom Middleware. Something like this enables the hostname being called in the request to be inspected and prohibited routes can be re-directed:
struct HostSpecificMiddleware:Middleware
{
func respond( to request: Request, chainingTo next: Responder ) throws -> Future<Response>
{
let host = request.http.headers.filter{ (arg) in let (name, _) = arg; return name == "Host" }[0]
if host.1 == "hostA:8080"
{
if request.http.url.path == "routeA"
{
throw Abort.redirect(to:"routeNotAllowed")
}
}
return try next.respond(to: request)
}
}
You can then configure the middleware into routes in configure.swift using:
let soMW = HostSpecificMiddleware()
let users = router.grouped(uriUsers).grouped(soMW)
The second approach gives you much more flexibility.

Related

Make root point to static webpage in Vapor

I have a static web page stored in the Public folder of my Vapor server. It has an index.html file. However when I navigate to root (http://localhost:8080) it displays Not found.
What I need to do make root point to index.html?
On Vapor 3, adding a home route worked for me. Specifically, I added the route in routes.swift like this:
router.get { req -> Future<View> in
let dir = DirectoryConfig.detect()
let path = dir.workDir + "/Public/index.html"
return try req.view().render(path)
}
For Vapor 4 ...
Inside routes.swift:
app.get { req -> EventLoopFuture<View> in
return req.view.render(app.directory.publicDirectory + "index.html")
}
This assumes you have a "Public" folder in your project directory root with an index.html file inside.
Also, in configure.swift:
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
Build project, run server, and point a browser to localhost:8080, you don't even have to specify localhost:8080/index.html, it just goes.
You could create a middleware to handle this.
import Vapor
final class IndexPageMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
// check if path is /
guard request.url.path == "/" else {
// otherwise pass to next responder
return next.respond(to: request)
}
// respond with index
let indexPath = request.application.directory.publicDirectory + "/index.html"
let response = request.fileio.streamFile(at: indexPath)
return request.eventLoop.makeSucceededFuture(response)
}
}
then in configure.swift add the following
app.middleware.use(IndexPageMiddleware())

Varnish direct request to internal ip

I have an hash configuration like this
sub vcl_init {
new bar = directors.hash();
bar.add_backend(server1, 1.0);
bar.add_backend(server2, 1.0);
}
I would like to add a rewrite rule that if in the url there is a word it must redirect to one specific internal server
if (req.url ~ "/newsletter" ) {
set req.http.x-redir = "https://" + "10.1.3.4" + req.url;
return (synth (301, req.http.x-redir));
}
But this rule doesn't work because 10.1.3.4 is an internal ip not a DNS. Is it possible to do this?
Seems like you want to use a different director backend (the internal server) if the request contains newsletter. Thats possible.
Example
sub vcl_recv {
if (req.url ~ "^/newsletter") {
set req.backend = newsletter;
} else {
set req.backend = default.
}
}
See https://varnish-cache.org/docs/3.0/tutorial/advanced_backend_servers.html
But you cant do a 301, thats for the external request, while here we are talking internal routing.

Grails channel security causing a redirect loop

I am new to Grails and I am working on an exisiting application. I am trying to force the anyone using our website to allways be on https. I added the Spring Security Core plugin
//BuildConfig.groovy
compile "org.grails.plugins:spring-security-core:2.0.0"
and I just added
///Config.groovy
grails.plugin.springsecurity.secureChannel.definition = [
'/**': 'REQUIRES_SECURE_CHANNEL'
When I try to go on localhost:8080/myapp, it redirects me to https://localhost:8443/myapp, but I get a "This webpage has a redirect loop ERR_TOO_MANY_REDIRECTS" message.
I added print statements in my SecurityFilters.groovy, and I can see the infinite loop going
baseFilter(controller: "*", action: "*")
{
before = {
println "baseFilter"
// If auth controller then ok to continue
if (controllerName.equals("auth"))
{
return true;
}
// If no subject (user) and not auth controller then user must authenticate
if (!session.subject && !(controllerName.equals("auth")))
{
params.targetUri = request.forwardURI - request.contextPath
if (params.action=="profile") {
params.targetUri=params.targetUri + "?page=" + params?.page
}
else if (params.action=="results") {
params.targetUri="/home"
}
println "baseFilter: Redirecting: PARAMS = $params"
redirect(controller:'auth', action:'login', params: params)
return false;
}
}
}
It's just:
baseFilter
baseFilter: Redirecting: PARAMS = [action:auth, format:null, controller:login, targetUri:/login/auth]
Over and over.
I've tried many other things I found on Stackoverflow and other websites, but they either do not work, or are too complicated.
Thank you.
Ok, so this isn't the answer to the question, but I managed to achieve what I was trying to do, which was to force SLL, and redirect any attempts to use http. I did this by using the shiro plugin, which was already being used by my application. In the Buildconfig.groovy, just add compile ":shiro:1.2.1" to you plugins. In the config.groovy I added the following properties:
security {
shiro {
filter {
loginUrl = "/login"
successUrl = "/"
unauthorizedUrl = "/unauthorized"
filterChainDefinitions = """
/** = ssl[443]
"""
}
}
}
You can modify your filterChainDefinitions to only force ssl on certain urls. I just used /** because I always want SSL.

drupal redirect language url alias

I would like to redirect urls that are using the wrong url alias.
Example, in my site I have:
English -> /prices/high-school -> node/112
Spanish -> (/es)/precios/high-school -> node/115
When a person or search engine reaches /es/prices/high-school a 404 is returned. What I would like is to redirect /es/prices/high-school to node/115.
I would like to do this in a general form, writing a module or using an existing one if possbile.
Thanks.
I already figured it out.
In the preprocess hook I need to check the page, strip the prefix and get the node id from the original id.
See code below:
if(current_path()=="search404")
{
$url = request_path();
if (startsWith($url,'es/') ||
startsWith($url,'de/') ||
startsWith($url,'it/') ||
startsWith($url,'fr/') )
{
$originalPath = substr($url,3,strlen($url)-3);
$path = drupal_lookup_path("source", $originalPath,"en");
if (isset($path))
{
$node = menu_get_object("node", 1, $path);
if (isset($node))
{
$prefix = substr($url,0,2);
$translated_paths = translation_path_get_translations('node/' . $node->nid);
if (isset($translated_paths) && array_key_exists ($prefix,$translated_paths))
{
if (isset($_GET['destination'])) {
unset($_GET['destination']);
}
$new_path = $translated_paths[$prefix];
drupal_goto($new_path, array(),301);
}
}
}
}
}
It won't be a solution to add different url aliases for the language versions? I mean:
node/112 -> /prices/high-school
node/115 -> /es/precios/escuela-secundaria
i18n module handles language based paths and redirects too.

Logic block in Grails URLMappings

My site has urls like 'http://someRandomUsername.mysite.com'.
Sometimes users will try urls like
'http://www.someRandomeUsername.mysite.com'. I'd like to have some
logic in my url mappings to deal with this.
With the mappings below when I hit the page , with or without the
unneeded www, I get:
2012-03-01 14:52:16,014 [http-8080-5] ERROR [localhost].[/ambit] -
Unhandled exception occurred whilst decorating page
java.lang.IllegalArgumentException: URL mapping must either provide a
controller or view name to map to!
Any idea how to accomplish this? The mapping is below.
Thanks!
Jason
static mappings = {
name publicMap: "/$action?/$id?" {
def ret = UrlMappings.check(request)
controller = ret.controller
userName = ret.userName
}
}
static check =
{ request ->
def tokens = request?.serverName?.split(/\./) as List ?: []
def ret = [controller:'info']
if(tokens.size() > 3 && token[0] == 'www')
{
ret.userName = tokens[1]
ret.controller = 'redirect'
ret.action = 'removeWWW'
}
else if(tokens.size() == 3)
{
ret.userName = tokens[0]
ret.controller = 'info'
}
return ret
}
Honestly, like DmitryB said, the best way to do this is via the web server, whether it's IIS, Apache, or Tomcat.
Having said that, I feel the best way to accomplish this in Grails would be using filters.
You could create something like this in your ~/conf directory:
public class StripFilters {
def filters = {
stripWWWFilter(controller: '*', action: '*') {
before = {
def tokens = request.serverName.tokenize(/\./) ?: []
if(tokens.size() > 3 && tokens[0] == 'www') {
def url = request.request.requestURL.toString().replace('www.', '')
redirect([url:url, params: [userName: tokens[1]], permanent: true])
return false
}
}
}
}
}
This should do the trick.

Resources