Custom provider in the url rewrite module - azure

I am developing on a website which is currently hosted in Azure on a VM. Now I am changing this website to be able to run it as an Azure website.
Now my problem:
I am using the url rewrite module from IIS with the database provider, which works perfectly in the VM. With the database provider users of the website can create their own simple rewrite rules.
But when I upload my website as an Azure Website and I access a url that's specified in the database I get an error:
"The page cannot be displayed because an internal server error has occurred.".
This is the logging configuration I currently use:
<rewrite>
<rules configSource="RewriteInbound_Live.config" />
<outboundRules configSource="RewriteOutbound_Live.config" />
<providers>
<provider name="DB" type="DbProvider, Microsoft.Web.Iis.Rewrite.Providers, Version=7.1.761.0, Culture=neutral, PublicKeyToken=0545b0627da60a5f">
<settings>
<add key="ConnectionString" value="*****" />
<add key="StoredProcedure" value="sp_GetRewrittenUrl" />
<add key="CacheMinutesInterval" value="60" />
</settings>
</provider>
</providers>
</rewrite>
I've turned on web server logging which doesn't give me any information, and I've enabled application logging which also doesn't give me any information.
My question, is it possible to use custom providers for the url rewite module in Azure, of can this be achieved in another way?

I had the same problem: DbProvider not available in azure web app. So I left most of the url rewriting stuff in the web.config untouched and moved the database-based rules to the global.asax file's Application_BeginRequest method.
For redirect rules, just use a Response.Redirect or an adequate Redirect301 implementation. On the other hand, for the rewrites, use HttpContext.Current.RewritePath.
You should add a caching mechanism in order to reduce repetitive queries to the DB:
void Application_BeginRequest(Object sender, EventArgs e)
{
//TODO: Cache sql queries
string requestUrl = Request.FilePath;
using (DBConn conn = new DBConn("GetRedirectUrl"))
{
conn["input"] = requestUrl;
string res = conn.ExecuteScalar()?.ToString();
if (!string.IsNullOrEmpty(res))
{
Response.Redirect301(res);
}
}
//TODO: Cache sql queries
using (DBConn conn = new DBConn("GetRewrittenUrl"))
{
conn["input"] = requestUrl;
string res = conn.ExecuteScalar()?.ToString();
if (!string.IsNullOrEmpty(res))
{
HttpContext.Current.RewritePath(res);
}
}
}

Related

How to: Password Protect Azure App service

I have website that is Hosted in a Azure App Service. are there any options in azure so that I can put a password on the website. Ideally without changing the websites code.
Just a basic password or user name and password, doesn't need to be google or facebook login or AD login.
It is a .net based website and I have seen a few options to do this, but it means I have to change the code of the website in someway or another.
Surely with all that sophisticated cloud technology, I can go in to the portal and set a password at a server level? - Or is the only way to make some kind of change to the application?
It is possible to enable Basic Authentication for Azure Web Apps with some settings in the applicationHost.xdt. You can load some modules in this file on the start of your Web App.
Steps:
Navigate to your WebApp in the Azure Portal
In the left menu, search for the header Development Tools an select Advanced Tools (Kudu)
Use the Debug Console > CMD tool, to navigate to the WebApp directory: \home\site
Create a file named: applicationHost.xdt
Paste the following:
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<location path="%XDT_SITENAME%" xdt:Locator="Match(path)">
<system.webServer>
<rewrite xdt:Transform="InsertIfMissing">
<allowedServerVariables xdt:Transform="InsertIfMissing">
<add name="RESPONSE_WWW_AUTHENTICATE" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</allowedServerVariables>
<rules xdt:Transform="InsertIfMissing">
<rule name="BasicAuthentication" stopProcessing="true" xdt:Transform="InsertIfMissing" xdt:Locator="Match(name)">
<match url=".*" />
<conditions>
<add input="{HTTP_AUTHORIZATION}" pattern="^Basic dXNlcjpwYXNzd29yZA==" ignoreCase="false" negate="true" />
</conditions>
<action type="CustomResponse" statusCode="401" statusReason="Unauthorized" statusDescription="Unauthorized" />
<serverVariables>
<set name="RESPONSE_WWW_AUTHENTICATE" value="Basic realm=Project" />
</serverVariables>
</rule>
</rules>
</rewrite>
</system.webServer>
</location>
</configuration>
Change the Basic Auth to your liking (default in example is: user:password)
Make sure the web.config rewrite rules don't contain <clear /> as this wil remove the effects from the applicationHost.xdt file
Save the file and Stop and Start your WebApp (a simple Restart will not suffice)
Notes:
Not sure if this works on Linux based WebApps..
You can add this step to you're deployment pipelines by using FTP
Update: I've noticed issues with applicationHost.xdt while using it on secondary Web App slots. Only the primary slot seems to work.
PS: Cross-post from my answer here.
You can use Authentication and authorization in Azure App Service.
Authentication/Authorization was previously known as Easy Auth.
Azure App Service provides built-in authentication and authorization support, so you can sign in users and access data by writing minimal or no code in your web app, RESTful API, and mobile back end, and also Azure Functions. This article describes how App Service helps simplify authentication and authorization for your app.
Source: Authentication and authorization in Azure App Service and Azure Functions.
EDIT:
The above is a solution to have a password protected App Service without changing any code whatsoever. At this point there is no alternative, as you can see in the open feedback issue Allow HTTP Basic authentication on basic apps
Hi everyone, we understand the demand for this feature, but we do not plan to support authentication at this level. We suggest using EasyAuth for this scenario.
https://learn.microsoft.com/en-us/azure/app-service/overview-authentication-authorization
EDIT 2:
This method forces the user to use google or facebook, etc...
This is not true. You can also create a user in your Azure Active Directory and use that one with Easy Auth. The username would be something like username#<YOUR-TENANT>.onmicrosoft.com

Azure Authenticated Web Api Error only with POST calls

would you help me?
i've created a web api on azure and chose not to allow anonymous requests but to use azure active directory to authenticate the requests. the app beneath has "sign-in and read user profile" permissions set.
if the controller behind the web api accepts GET requests it works, while it gives me the error : "You do not have permission to view this directory or page."
before i call the web api i open a iframe on the page (sharepoint page) to implicitly get the token from the web api, which calls the basic GET action below:
[ActionName("Connect")]
[HttpGet]
public IHttpActionResult Connect()
{
return base.Content(HttpStatusCode.OK, "OK", new JsonMediaTypeFormatter(), "text/plain");
}
later on i call via jquery another action (POST)...
public IHttpActionResult PostPromote([FromBody] string request)
but at this point i receive the 403 (Forbidden) message.
the CORS for my webapi is set in the web.config file:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="https://mytenant.sharepoint.com" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Credentials" value="true"/>
</customHeaders>
</httpProtocol>
if i switch the method to GET it works, as well as (of course) if i permit anonymous requests.
thank you a lot, that's making me crazy.

Enabling CORS for Web API in Azure Web Apps

I have deployed a Web API project to Azure Web app. And from a angularjs app I am trying a $http.get request to the API. but it gives a CORS(cross-origin) exception though I have enabled it in my Web API startup config
app.UseCors(CorsOptions.AllowAll);
I want to Enable CORS for Azure Web App, that would solve the problem I believe
EDIT
http://nearestbuyweb.azurewebsites.net/ this is the URL of the Web app. It is trying to access http://nearestbuyapi.azurewebsites.net/api/MenuBar where the exception occurs.
I think it is not possible with Azure Web App. MSDN Question
Please help!
Note: You use CORS settings to let other websites access your site's API. Not to access other site's APIs.
Based on your comments it sounds like you're getting the CORS error when you try to make external requests from your site. That's exactly the behavior CORS is supposed to block.
For the errors to go away you would have to apply the CORS config settings on the site who's API you're trying to access.
In your case you want to make sure you're applying the config changes on the http://nearestbuyapi.azurewebsites.net site. NOT on http://nearestbuyweb.azurewebsites.net/
<system.webServer>
<httpProtocol>
<customHeaders>
<clear />
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
</system.webServer>
I have CORS in Azure working using this:
WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.EnableCors();
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "PublicApi",
routeTemplate: "api/v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Web.config:
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers></system.webServer>
You need to remove the options handler in IIS using web.config.
http://eugeneagafonov.com/post/38312919044/iis-options-cors-aspnet-webapi-en
Sorry Guys,
The issue happens only at my corporate network. Having googled I found that corporate network can be disable CORS requests . Found this here link

Staging Azure Websites

I'm new to Azure and I'm working on a project that uses the new Websites model as opposed to Cloud Services. I want to set up a continuous delivery process like this one: http://www.windowsazure.com/en-us/develop/net/common-tasks/publishing-with-tfs/
However, I don't want my site to be publicly addressable after each continuous deployment. Instead, I want my continuous deployments to be accessible only by my team for testing purposes. How can this best be achieved?
Thoughts so far:
Wrap the whole site in forms authentication - but I don't like the fact that this means I will be deploying a different version of my site to production than that which I deploy to testing.
IP address restrictions - but I don't know if this can be done with Azure Websites and whether this is a good solution?
The Azure Websites Authentication / Authorization feature was designed to support this exact scenario. Basically, you create a site slot, add AAD authentication to it using a few clicks, and from then on your staging slot will always require a valid login, even after you do a swap operation.
Blog Post: http://azure.microsoft.com/blog/2014/11/13/azure-websites-authentication-authorization/
Demo Video: http://azure.microsoft.com/en-us/documentation/videos/azure-websites-easy-authentication-and-authorization-with-chris-gillum/
You can add IP restrictions using the URL Rewrite Module, which Azure web sites seem to have enabled by default.
Your web.config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="SayNoToZombies" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REMOTE_ADDR}" pattern="::1" negate="true" />
</conditions>
<action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." statusDescription="Sorry, you're not allowed" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
You can replace pattern="::1" (localhost in IPv6) with a suitable regex to match your permitted IP, e.g., pattern="87.236.134.47" or if more than one:
pattern="(62\.231\.142\.233)|(87\.236\.134\.47)|(::1)|(127\.0\.0\.1)"
I had to do something similar for a client, but couldn't find a way to restrict access to the site from the Azure portal itself. I went with the IP address restriction option, but did it through code in the application itself. My application was already using forms authentication, so I could perform the IP address check in the sign in action.
In your case I would suggest a custom action filter. Perform the check in the filter and if the IP address is not allowed, return a http 401 (unauthorised) status code.
Create an app setting called AllowedIpAddresses or some such, in which you can add a comma separated list of allowed IP addresses. When you perform the check, you can set your site to allow all traffic if AllowedIpAddresses is empty or doesn't exist. That way, you can ignore this setting in production and all traffic will be allowed by default. You can set up custom app settings for each site within the Azure portal.
Here's what a custom filter might look like. I haven't tested this!
public class AccessRestrictionFilterAttribute : ActionFilterAttribute
{
// simple wrapper around ConfigurationManager.AppSettings for testability
private readonly IAppSettingsHandler appSettingsHandler;
public AccessRestrictionFilterAttribute(IAppSettingsHandler appSettingsHandler)
{
this.appSettingsHandler = appSettingsHandler;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var isAllowed = false;
var userIpAddress = filterContext.HttpContext.Request.UserHostAddress;
if (appSettingsHandler.AllowedIpAddresses.Split(new[] { ',' }).Any(x => x == userIpAddress))
{
isAllowed = true;
}
if (!isAllowed)
{
filterContext.Result = new HttpUnauthorizedResult();
}
base.OnActionExecuting(filterContext);
}
}

IIS Url Rewrite maps from custom provider?

Is it possible to access a dictionary or something of a certain rewrite map from your rewrite custom provider?
Something like...{Provider:key,mapname}
which in code does something like {Mapname:key}
?
When you inherit from IRewriteProvider, in your Initialize() method you will get a Dictionary with all the settings:
public void Initialize(IDictionary<string, string> settings, IRewriteContext rewriteContext)
{
...
}
which comes from the following config section:
<system.webServer>
<rewrite>
<providers>
<provider name="MyCustomRewriteProvider" type="MyCustomRewriteProvider.....">
<settings>
<!--add key="keyName" value="keyValue" /-->
</settings>
</provider>
</providers>
</rewrite>
...
</system.webServer>
You can refer to this article for detailed information on how to create a custom URL Rewrite provider:
Developing a Custom Rewrite Provider for URL Rewrite Module

Resources