Secure .Net API Calls via IP - asp.net-web-api

So pretty new to the API world.
Scenario:
Server 1 Runs a Web app that displays a web page with info called from Server 2.
Server 2 Runs a api service that should only answer to Server 1.
ISSUE:
User visits Server 1, and in the controller it calls server 2.
//Server 1
public class IndexController : Controller
{
private WebProfile GetProfile(string id)
{
var client = new HttpClient();
var builder = new UriBuilder(string.Format("{0}{1}", baseMapUrl, "/Profile"));
builder.Port = 17608;
var query = HttpUtility.ParseQueryString(builder.Query);
query["pId"] = id;
builder.Query = query.ToString();
string url = builder.ToString();
client.BaseAddress = builder.Uri;
//client.BaseAddress = new Uri(url);
var response = client.GetAsync("?pId=" + id);
while (response.Status==TaskStatus.WaitingForActivation)
{
//wait until my status changes
var a = response.Status;
}
var result = response.Result.Content.ReadAsStringAsync();
//do more stuff...
}
}
//Server 2
[HttpGet]
public class ProfileController : ApiController
{
public Profile Get(string id)
{
var profile = new WebProfile();
//Go and do stuff, get data and return
return profile;
}
So I created a firewall policy on Server 2 to only accept connections form Server 1 (IP) on port 17608.
But when tested, no traffic comes through to Server 2 as the call (IP) is not the IP address of Server 1, its the IP of the User visiting Server 1....
What am I doing wrong?
How do I resolve this as Server 2 should only accept calls from the IP of Server 1?

Related

Automated Tests Fail on App Service Using Custom Repository That Calls SQL Stored Procedure

I created a custom repository per https://aspnetboilerplate.com/Pages/Documents/Articles/Using-Stored-Procedures,-User-Defined-Functions-and-Views/index.html for my asp.net zero project. Everything works great when I test with the Swagger API test application and from my angular client. I then tried to write automated tests for the API using asp.net zero testing framework I get
System.AggregateException : One or more errors occurred. (The CommandType 'StoredProcedure' is invalid.)
---- System.ArgumentException : The CommandType 'StoredProcedure' is invalid."
It seems like the testing framework is using SQLLite for the DB Context. I am not sure how to work around this.
TEST THAT IS FAILING
[Fact]
public void Should_Get_All_StaticItems()
{
LoginAsTenant("Default", "admin");
//Act
**var types = _ptStaticDataTypeAppService.Get(new PTGetPTStaticDataTypeInput());**
//Assert
types.Result.Count.ShouldBe(_totalItems);
}
APP SERVICE METHOD
[AbpAuthorize(AppPermissions.Pages_Administration_PT_StaticDataType)]
public class PTStaticDataTypeAppService : PieceTrackerAppServiceBase, IPTStaticDataTypeAppService
{
IPTStaticDataTypeRepository _ptStaticDataRepository;
public PTStaticDataTypeAppService(IPTStaticDataTypeRepository ptStaticDataTypeRepository)
{
_ptStaticDataRepository = ptStaticDataTypeRepository;
}
public async Task<List<PTGetPTStaticDataTypeForViewDto>> Get(PTGetPTStaticDataTypeInput input)
{
return await _ptStaticDataRepository.Get(input);
}
REPOSITORY
public class PTStaticDataTypeRepository : PieceTrackerRepositoryBase<PTStaticDataType, long>, IPTStaticDataTypeRepository
{
private readonly IActiveTransactionProvider _transactionProvider;
public PTStaticDataTypeRepository(IDbContextProvider<PieceTrackerDbContext> dbContextProvider, IActiveTransactionProvider transactionProvider)
: base(dbContextProvider)
{
_transactionProvider = transactionProvider;
}
public async Task<List<PTGetPTStaticDataTypeForViewDto>> Get(PTGetPTStaticDataTypeInput input)
{
var data = new List<PTGetPTStaticDataTypeForViewDto>();
var cn = Context.Database.GetDbConnection();
if (cn.State != ConnectionState.Open)
await cn.OpenAsync();
using (var cmd = cn.CreateCommand())
{
cmd.CommandText = "usp_GetPTStaticDataType";
**cmd.CommandType = CommandType.StoredProcedure;**
The testing framework uses an in memory SQL Lite DB to pass thru the SQL commands. So I created a workaround in my custom repository.
private DbConnection GetSqlConnection()
{
const string TESTINGDB = "memory";
const string PTCONNSTRING = "Server=(local); Database=PieceTrackerDb; Trusted_Connection=True;";
var cn = Context.Database.GetDbConnection();
if (cn.ConnectionString.Contains(TESTINGDB))
{
cn = new SqlConnection(PTCONNSTRING);
}
var logMsg = "PTStaticDataTypeRepository.Get Connection String = " + cn.ConnectionString;
Console.WriteLine(logMsg);
return cn;
}

HttpClient always do Basic Authentication check after provide an authorization header?

The web api control, UserControl, has two methods, RetrieveUserID which needs Basic Authorization check
[HttpGet]
[Route("RetrieveUserID/{strUsername}")]
[Authorize]
public string RetrieveUserID(string strUsername)
{
//retrieve userID and return it
......
return strUserID;
}
Another method, FailAuthenticationReason, is used if fail to retrieve userID, it returns the detail fail info such as wrong username, wrong password, account is locked out, etc. which doesn't need to do any authentication check
[HttpGet]
[Route("FailAuthenticationReason/{strUsername}")]
public string FailAuthenticationReason(string strUsername)
{
//retrieve detail failed reason
......
return strFailedReason;
}//End of
It works well when I use a browser to check them. But when I use it in my app, after I provide the authorization header and fail to retrieve userID because of incorrect username and/or password, it also do the authorization check when it call FailAuthenticationReason
var authData = string.Format("{0}:{1}", entUsername.Text,entPassword.Text);
var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
App.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
var uri = new Uri(string.Format(App.strWebAPIURI + "/RetrieveUserID/{0}", entUsername.Text));
try
{
var response = await App.httpClient.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
......
}
else
{
//Fail to pass authorization
uri = new Uri(string.Format(App.strWebAPIURI + "/FailAuthenticationReason/{0}", entUsername.Text));
response = await App.httpClient.GetAsync(uri);
......
}
How can the program call FailAuthenticationReason without the authorization check?

What is the difference between a Web application and Web API?

It sounds naive on hearing but how different are web application and Web API when someone mentions it in their paper?
How different are they from their functionalities?
Very short: web application, it's a web site, which you see in your browser, and web api, it's a service, which you use in a web application.
See also Difference between ASP.NET MVC and ASP.NET Web API:
Asp.Net MVC is used to create web applications that returns both views
and data but Asp.Net Web API is used to create full blown HTTP
services with easy and simple way that returns only data not view.
Web Applications are meant for their human interactions through views whereas Web API aka Web Services are meant for system-to-system interactions (information exchange programatically ). They exchange data.
Web Application:
It is an end-to-end solution for a user. Which means, User can:
Open it using a browser
Interact with it. He can click on something and after some processing, its result will be reflected in the browser screen. Human-System interaction
Web API
With Web APIs alone, a user can not interact with it, because it only returns data, not views.
It is a system which interacts with another system
It does not return views, it returns data
It has an endpoint set, which can be hit by other systems to get data which it provides.
Explanation using an ANALOGY
Web Application:
Suppose we have a cook. We can ask him to cook us anything, anytime!Suppose we ask our cook to cook us a burger. He'll process our request and will provide us a burger. ( This is like a Web Application; a complete solution. )
Web API
Now if we ask him to make us 'McDonalds' burger, can he cook and bring us that? No!
Here comes the concept of APIs! (for this example, lets suppose McDonalds only give takeaways to cooks only)
McDonalds-Takeaways is like an API. Which allows other systems (cooks) to hit it and bring back desired data. So we can ask our solution (our cook) to
go to McDonalds Takeaway
Buy the burger and bring us that
So what happened is, we asked our "System" to talk to this McDonalds-takeaways (API System) and bring back the result we desired.
Web app is a website that is running in your browser and web Api is service
Web API is back-end application(server side) where actual functionality to call service/database call is happening to store and retrieve the data.
Web Application is front end application(client side)which is calling web API to present the data retrieved from back-end.
eg: To check the account balance in your mobile banking app, you are able to see your account details in front end. But all the calculations of interest /balance is happening in the back end.
In simplest word, a web application's response to requests are html, css, javascript and anything that a browser can render (graphical), whereas a web api returns non-graphical "data". Having said that, I think we can make a web api work like a web application because html is still data.
Create :
public IHttpActionResult GetAllProduct()
{
IList<product> pro = null;
using (var ctx = new TestMVCEntities())
{
pro = ctx.products.ToList();
}
if (pro.Count == 0)
{
return NotFound();
}
return Ok(pro);
}
public IHttpActionResult PostNewProduct(product pro)
{
using (var ctx = new TestMVCEntities())
{
ctx.InUPProduct(pro.pid,pro.pname,pro.pprice);
ctx.SaveChanges();
}
return Ok();
}
public IHttpActionResult PutOldProduct(product pro)
{
using (var ctx = new TestMVCEntities())
{
product c = (from x in ctx.products
where x.pid == pro.pid
select x).First();
if (c != null)
{
c.pname = pro.pname;
c.pprice = pro.pprice;
ctx.SaveChanges();
}
else
{
return NotFound();
}
}
return Ok();
}
public IHttpActionResult Delete(int id)
{
using (var ctx = new TestMVCEntities())
{
var pro = ctx.products
.Where(s => s.pid == id)
.FirstOrDefault();
ctx.Entry(pro).State = System.Data.Entity.EntityState.Deleted;
ctx.SaveChanges();
}
return Ok();
}
Consume :
public JsonResult GetProductsData()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44350/api/");
//HTTP GET
var responseTask = client.GetAsync("product");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<product>>();
readTask.Wait();
var alldata = readTask.Result;
var rsproduct = from x in alldata
select new[]
{
Convert.ToString(x.pid),
Convert.ToString(x.pname),
Convert.ToString(x.pprice),
Convert.ToString(x.pimage),
Convert.ToString(x.pisdemand),
Convert.ToString(x.pcname),
Convert.ToString(x.psupply)
};
return Json(new
{
aaData = rsproduct
},
JsonRequestBehavior.AllowGet);
}
else //web api sent error response
{
var pro = Enumerable.Empty<product>();
return Json(new
{
aaData = pro
},
JsonRequestBehavior.AllowGet);
}
}
}
public JsonResult InupProduct(string id,string pname, string pprice)
{
try
{
product obj = new product
{
pid = Convert.ToInt32(id),
pname = pname,
pprice = Convert.ToDecimal(pprice)
};
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44350/api/product");
//HTTP POST
var postTask = client.PostAsJsonAsync<product>("product", obj);
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
return Json(1, JsonRequestBehavior.AllowGet);
}
else
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}
/*context.InUPProduct(Convert.ToInt32(id),pname,Convert.ToDecimal(pprice));
return Json(1, JsonRequestBehavior.AllowGet);*/
}
catch (Exception ex)
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}
public JsonResult deleteRecord(int ID)
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44350/api/product");
//HTTP DELETE
var deleteTask = client.DeleteAsync("product/" + ID);
deleteTask.Wait();
var result = deleteTask.Result;
if (result.IsSuccessStatusCode)
{
return Json(1, JsonRequestBehavior.AllowGet);
}
else
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}
/* var data = context.products.Where(x => x.pid == ID).FirstOrDefault();
context.products.Remove(data);
context.SaveChanges();
return Json(1, JsonRequestBehavior.AllowGet);*/
}
catch (Exception ex)
{
return Json(0, JsonRequestBehavior.AllowGet);
}
}

Using WebAPI in LINQPad?

When I tried to use the Selfhosted WebAPI in LINQPad, I just kept getting the same error that a controller for the class didn't exist.
Do I have to create separate assemblies for the WebAPI (Controllers/Classes) and then reference them in my query?
Here's the code I'm using
#region namespaces
using AttributeRouting;
using AttributeRouting.Web.Http;
using AttributeRouting.Web.Http.SelfHost;
using System.Web.Http.SelfHost;
using System.Web.Http.Routing;
using System.Web.Http;
#endregion
public void Main()
{
var config = new HttpSelfHostConfiguration("http://192.168.0.196:8181/");
config.Routes.MapHttpAttributeRoutes(cfg =>
{
cfg.AddRoutesFromAssembly(Assembly.GetExecutingAssembly());
});
config.Routes.Cast<HttpRoute>().Dump();
AllObjects.Add(new UserQuery.PlayerObject { Type = 1, BaseAddress = "Hej" });
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
using(HttpSelfHostServer server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Server open, press enter to quit");
Console.ReadLine();
server.CloseAsync();
}
}
public static List<PlayerObject> AllObjects = new List<PlayerObject>();
public class PlayerObject
{
public uint Type { get; set; }
public string BaseAddress { get; set; }
}
[RoutePrefix("players")]
public class PlayerObjectController : System.Web.Http.ApiController
{
[GET("allPlayers")]
public IEnumerable<PlayerObject> GetAllPlayerObjects()
{
var players = (from p in AllObjects
where p.Type == 1
select p);
return players.ToList();
}
}
This code works fine when in a separate Console Project in VS2012.
I started using AttributeRouting via NuGET when I didn't get the "normal" WebAPI-routing to work.
The error I got in the browser was: No HTTP resource was found that matches the request URI 'http://192.168.0.196:8181/players/allPlayers'.
Additional error: No type was found that matches the controller named 'PlayerObject'
Web API by default will ignore controllers that are not public, and LinqPad classes are nested public, we had similar problem in scriptcs
You have to add a custom controller resolver, which will bypass that limitation, and allow you to discover controller types from the executing assembly manually.
This was actually fixed already (now Web API controllers only need to be Visible not public), but that happened in September and the latest stable version of self host is from August.
So, add this:
public class ControllerResolver: DefaultHttpControllerTypeResolver {
public override ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver) {
var types = Assembly.GetExecutingAssembly().GetExportedTypes();
return types.Where(x => typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(x)).ToList();
}
}
And then register against your configuration, and you're done:
var conf = new HttpSelfHostConfiguration(new Uri(address));
conf.Services.Replace(typeof(IHttpControllerTypeResolver), new ControllerResolver());
Here is a full working example, I just tested against LinqPad. Note that you have to be running LinqPad as admin, otherwise you won't be able to listen at a port.
public class TestController: System.Web.Http.ApiController {
public string Get() {
return "Hello world!";
}
}
public class ControllerResolver: DefaultHttpControllerTypeResolver {
public override ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver) {
var types = Assembly.GetExecutingAssembly().GetExportedTypes();
return types.Where(x => typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(x)).ToList();
}
}
async Task Main() {
var address = "http://localhost:8080";
var conf = new HttpSelfHostConfiguration(new Uri(address));
conf.Services.Replace(typeof(IHttpControllerTypeResolver), new ControllerResolver());
conf.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var server = new HttpSelfHostServer(conf);
await server.OpenAsync();
// keep the query in the 'Running' state
Util.KeepRunning();
Util.Cleanup += async delegate {
// shut down the server when the query's execution is canceled
// (for example, the Cancel button is clicked)
await server.CloseAsync();
};
}

Sending the unique phone id as an email

I try to create an app that allows the user to register himself for my service.
The problem is that it is very important that i can limit each user to a very single account
i figured out I could probably do this with the Phone unique id and the windows live id
i also figured out how to get These within the app , but now my problem is how to get them to me!
Can anyone help me on how to send the phone id with the desired username to my email address ?
Thank you
EDIT
I use this code to get the needed values
public static class ExtendedPropertyHelper
{
private static readonly int ANIDLength = 32;
private static readonly int ANIDOffset = 2;
public static string GetManufacturer()
{
string result = string.Empty;
object manufacturer;
if (DeviceExtendedProperties.TryGetValue("DeviceManufacturer", out manufacturer))
result = manufacturer.ToString();
return result;
}
//Note: to get a result requires ID_CAP_IDENTITY_DEVICE
// to be added to the capabilities of the WMAppManifest
// this will then warn users in marketplace
public static byte[] GetDeviceUniqueID()
{
byte[] result = null;
object uniqueId;
if (DeviceExtendedProperties.TryGetValue("DeviceUniqueId", out uniqueId))
result = (byte[])uniqueId;
return result;
}
// NOTE: to get a result requires ID_CAP_IDENTITY_USER
// to be added to the capabilities of the WMAppManifest
// this will then warn users in marketplace
public static string GetWindowsLiveAnonymousID()
{
string result = string.Empty;
object anid;
if (UserExtendedProperties.TryGetValue("ANID", out anid))
{
if (anid != null && anid.ToString().Length >= (ANIDLength + ANIDOffset))
{
result = anid.ToString().Substring(ANIDOffset, ANIDLength);
}
}
return result;
}
}
Now i need to store thes in variables ( what i cant really get to work ) and then send them to my php script which extracts them
in addition to this i need to ask the user to enter his email address and include this in the POST too ,
can you help?
You can get DeviceExtendedProperties.DeviceUniqueId from Microsoft.Phone.Info namespace.
Don't forget to declare in WMAppManifest.xml
like this:
<Capabilities>
<Capability Name="ID_CAP_IDENTITY_DEVICE"/>
<Capability Name="ID_CAP_IDENTITY_USER"/>
</Capabilities>
Link to msdn here
Then, you can send this id to your e-mail:
var emailComposeTask = new EmailComposeTask
{
To = "your-email#domiain.com",
Subject = "Test Message using EmailComposeTask",
Body = deviceId
};
emailComposeTask.Show();
But this will open an-email client, and I don't thik that user will be so kind to send you an email. So, you'd better send a POST request to your server
private void Button_Click(object sender, RoutedEventArgs e)
{
//collect all data you need:
var deviceId = Convert.ToBase64String(ExtendedPropertyHelper.GetDeviceUniqueID());
var userName = ExtendedPropertyHelper.GetWindowsLiveAnonymousID();
var manufatcurer = ExtendedPropertyHelper.GetManufacturer();
//create request string
//[see the explanation on MSDN][2]
var requestUrl = string
.Format("http://myPageUrlAddress.com/script.aspx?deviceid={0}&user={1}&manufacturer={2}",
deviceId, userName, manufatcurer);
System.Uri myUri = new System.Uri(requestUrl);
//create a request instance
HttpWebRequest myRequest = (HttpWebRequest)HttpWebRequest.Create(myUri);
myRequest.Method = "POST";
myRequest.ContentType = "application/x-www-form-urlencoded";
//and it will be sent.
//Also you need to create GetRequestStreamCallback method to
//handle server responce.
myRequest.BeginGetRequestStream(new
AsyncCallback(GetRequestStreamCallback), myRequest);
}
//this method is empty. You can show tha dialog box about successful sending.
public void GetRequestStreamCallback(IAsyncResult result) { ; }
What about e-mail - just create a TextBox on the same Page, and save user input to a variable.
If "my service" is a web service, then you could use the web service instead of the mail system.
In either case you can use Convert.ToBase64String(phoneId) to convert the phone id to a string.
To send strings via mail from WP7 you need to use EmailComposeTask.

Resources