Maintain state between WCF webservice calls from android - c#

I have a project in VB.NET which is using asp.net membership to manage user authentication. Now I want to build android app for this project so I decided to learn WCF and I have got average hold on WCF webservices. Now the issue I am facing is that when the user login into the android app following things happen:
Request goes to the webapplication and user credentials are authenticated.
After that when the user tries submits any data or try to view data , request again goes to the web application but now the web application should authenticate the user based on the credentials he has provided in the first request for login from the membership authentication.
Now the issue I am facing how to authenticate user in asp.net membership for each WCF Request in Per-Session Service call mode from java(Android).

There's several ways to do what I think you're asking for, I've thought of (and written) a few different potential solutions, however, the one I'm sharing here is something that I think will "slot-in" to existing solutions using ASP.NET Membership/Roles Provider. Hopefully I've given you enough information to do what you need to do, but you can always comment and ask more questions if anything's still unclear.
In your problem you describe using an ASP.NET Web Application containing a WCF Service for existing clients, but you're looking to expand to using Android (java) requests? Given that the ASP.NET Membership provider uses alot of "behind the scenes" SOAP interchanges (for authentication, authorization and encryption) that seem to be built into the service reference frameworks it'd be a fairly big task to write a java implementation...
So, I've written you an example of something that will integrate to the same "backend" provider, but will also allow you to send SOAP requests from any client without needing the service reference (I tested it using SoapUI for example)... I've written my solution in C# (as it's what the WCF samples were written in), however, you can quite easily use a code-converter to switch it to VB.NET. I also haven't provided you with the method to encrypt and decrypt passwords, you'll have to research that bit yourself.
You'll need to implement a new .svc file into your existing solution and create new web.config entries accordingly (I assume you know how to create a basicHttpBinding and service endpoint already).
You'll also need to duplicate your method calls (or instead, create a new class with the method content and reference it from wherever you're implementing the ServiceContract methods) and remove the "[PrincipalPermission(SecurityAction" attributes, and add the example methods below into the new service.
e.g. (using methods from Microsoft's MembershipAndRoleProvider WCF Sample) -
// Allows all Users to call the Add method
[PrincipalPermission(SecurityAction.Demand, Role = "Users")]
public double Add(double n1, double n2)
{
double result = n1 + n2;
return result;
}
Would become:
// Allows all Users to call the Add method
public double Add(double n1, double n2, string username, string token)
{
string isAuthorized = IsAuthorized(username, "Users", token)
if (isAuthorized.Contains("Success")
double result = n1 + n2;
return result;
else
throw new Exception("Authorization Exception: " + isAuthorized);
}
Here's my implementation(s), integrated into the Microsoft WCF Sample MembershipAndRoleProvider (download from here):
IsolatedAuthService.svc
<%#ServiceHost Language="C#" Debug="true" Service="Microsoft.ServiceModel.Samples.IsolatedAuthService" CodeBehind="IsolatedAuthService.cs" %>
IIsolatedAuthService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IIsolatedAuthService
{
[OperationContract]
string IsAuthorized(string username, string roleName, string token);
[OperationContract]
string AuthenticateUser(string username, string encryptedPassword);
}
}
IsolatedAuthService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using System.Web.Configuration;
using System.Configuration;
using System.IO;
using System.Security.Permissions;
using System.Security.Principal;
using System.ServiceModel.Activation;
using System.Threading;
namespace Microsoft.ServiceModel.Samples
{
public class IsolatedAuthService : IIsolatedAuthService
{
public string IsAuthorized(string username, string roleName, string token)
{
MembershipUser user = Membership.GetAllUsers()[username];
Configuration config = ConfigurationManager.OpenExeConfiguration(HostingEnvironment.MapPath("~") + "\\web.config");
SessionStateSection sessionStateConfig = (SessionStateSection)config.SectionGroups.Get("system.web").Sections.Get("sessionState");
InMemoryInstances instance = InMemoryInstances.Instance;
// Check for session state timeout (could use a constant here instead if you don't want to rely on the config).
if (user.LastLoginDate.AddMinutes(sessionStateConfig.Timeout.TotalMinutes) < DateTime.Now)
{
// Remove token from the singleton in this instance, effectively a logout.
instance.removeTokenUserPair(username);
return "User Unauthorized - login has expired!";
}
if (!instance.checkTokenUserPair(username, token))
return "User Unauthorized - not a valid token!";
// Check for role membership.
if (!Roles.GetUsersInRole(roleName).Contains(user.UserName))
return "User Unauthorized - Does not belong in that role!";
return "Success - User is Authorized!";
}
public string AuthenticateUser(string username, string encryptedPassword)
{
if (Membership.ValidateUser(username, Decrypt(encryptedPassword)))
{
// Not sure if this is actually needed, but reading some documentation I think it's a safe bet to do here anyway.
Membership.GetAllUsers()[username].LastLoginDate = DateTime.Now;
// Send back a token!
Guid token = Guid.NewGuid();
// Store a token for this username.
InMemoryInstances instance = InMemoryInstances.Instance;
instance.removeTokenUserPair(username); //Because we don't implement a "Logout" method.
instance.addTokenUserPair(username, token.ToString());
return token.ToString();
}
return "Error - User was not able to be validated!";
}
}
}
InMemoryInstances.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.ServiceModel.Samples
{
public class InMemoryInstances
{
private static volatile InMemoryInstances instance;
private static object syncRoot = new Object();
private Dictionary<string, string> usersAndTokens = null;
private InMemoryInstances()
{
usersAndTokens = new Dictionary<string, string>();
}
public static InMemoryInstances Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new InMemoryInstances();
}
}
return instance;
}
}
public void addTokenUserPair(string username, string token)
{
usersAndTokens.Add(username, token);
}
public bool checkTokenUserPair(string username, string token)
{
if (usersAndTokens.ContainsKey(username)) {
string value = usersAndTokens[username];
if (value.Equals(token))
return true;
}
return false;
}
public void removeTokenUserPair(string username)
{
usersAndTokens.Remove(username);
}
}
}
Bare in mind, this solution will not work if you're load-balancing your WCF service across multiple servers (due to the in-memory instance class), you could change the solution to use a database table instead of the in-memory instances if this is a requirement for you.

Related

Attribute To Secure Web Api

I am working with a web api where it should have a request key and depending upon it, the api controller will do
specific task. I am using rest client program in vs code and did the following for testing:
GET http://localhost:PortNo/WeatherForecast/GetAllTeams
test: "12345678910" //Key
So in the controller, I did this to get the key value:
[HttpGet]
public async Task<ActionResult<IEnumerable<TeamDetails>>> GetAllTeams()
{
string Token = Request.Headers["test"]; //Getting the key value here
var teams = _service.GetAllTeams();
return Ok(teams)
}
But I've few things in mind and doing R & D like how can I make the above with an attribute. Say each controller
will have an attribute as follows and make the request invalid if no proper key found:
[InvalidToken] //This is the attribute
[HttpGet]
public async Task<ActionResult<IEnumerable<TeamDetails>>> GetAllTeams()
{
var teams = _service.GetAllTeams();
return Ok(teams)
}
I am not sure if this is going to make the api secure and my plan is to valid every http request (In my case, a simple form submission at the moment), so it should say the request is generated from the web api app.
N.B: I worked with web api earlier in small sections but now a broader thing to implement, so expecting few suggestions that can help me to guide for better design.
try it:
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
..
public class InvalidToken : Attribute, IActionFilter
{
public InvalidToken( )
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
var Authorization = context.HttpContext.Request.Headers["test"];
if ( Authorization != "12345678910")
{
context.ModelState.AddModelError("Authorization", "Authorization failed!");
return;
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// "OnActionExecuted"
}
}
Startup.cs
services.AddScoped<InvalidToken>();
// add filter to whole api
services.AddControllers(options =>
{
options.Filters.Add<InvalidToken>();
});

Trying to authenticate users against the AD - ASP.NET MVC

A web application where people can view their SSRS reports internally or externally.
I'm trying to authenticate users logging in against the active directory groups using custom authorize roles, so the reports can be secured down based on if they are in a particular AD Group or not.
Now I know this works out the box with windows auth/form auth, but I'm using a custom authentication due to other reasons but what I do have is a table which has the custom usernames that the users are logging with mapped against their windows credentials.
I've been following this blog to test out this method of authenticating against the active directory groups and customized it to pass in the windows credentials mapped to the custom user login but having no luck so far.
With regards to the custom authentication, when I go find the matching domain name out of my table and store that domain name into the session variable, which then is passed into this AD authentication process for checking if the user exists in the group or not, see code below.
Custom authorize attribute,
using Helpers;
using Models;
using System;
using System.Web;
using System.Web.Mvc;
namespace Application.Validators
{
public class AuthorizeADAttribute : AuthorizeAttribute
{
public string Group { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (string.IsNullOrEmpty(Group))
{
return true;
}
var logOnInfo = httpContext.Session["LogOnInfo"] as LogOnModel;
var username = logOnInfo.DomainName;
try
{
return LDAPHelper.UserIsMemberOfGroups(username, Group);
}
catch (Exception ex)
{
return false;
}
}
}
}
LDAP Helper,
using System;
using System.Configuration;
using System.DirectoryServices.AccountManagement;
using System.Web;
namespace Application.Helpers
{
public class LDAPHelper
{
public static string GetLDAPContainer()
{
Uri ldapUri;
ParseLDAPConnectionString(out ldapUri);
return HttpUtility.UrlDecode(ldapUri.PathAndQuery.TrimStart('/'));
}
public static bool ParseLDAPConnectionString(out Uri ldapUri)
{
string connString =
ConfigurationManager.ConnectionStrings["ADConnectionString"]
.ConnectionString;
return Uri.TryCreate(connString, UriKind.Relative, out ldapUri);
}
public static bool UserIsMemberOfGroups(string username, string Group)
{
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(Group))
{
return false;
}
// Verify that the user is in the given AD group (if any)
using (var context = BuildPrincipalContext())
{
var userPrincipal = UserPrincipal.FindByIdentity(context,
IdentityType.SamAccountName,
username);
return userPrincipal.IsMemberOf(context, IdentityType.Name, Group);
}
}
public static PrincipalContext BuildPrincipalContext()
{
string container = GetLDAPContainer();
return new PrincipalContext(ContextType.Domain, null, container);
}
}
}
LDAP Connection string in the web.config (can confirm is correct),
<add name="ADConnectionString" connectionString="LDAP://CN=Managers;OU=Groups,OU=Users,DC=domain"/>
My issue I think is when I'm trying to return the container (GetLDAPHelper Method) from the LDAP conn string back to the PrincipalContext it justs returning null and throwing an error.
I'm looking to see if anyone has done anything remotely similar or is there a more suitable method for trying to achieve what i'm doing?
The issue is that the LDAP connection string is not a valid Uri, so when you attempt to make it one, ldapUri remains null. If you need to parse the connection string, for some reason, you'll need to do it another way. You can't use Uri.

Use Same Class as Controller for Many Tables

I am new to asp.net and azure mobile services.
Have some questions:
1)I have used the TodoItemController to query data from azure table storage
(just used their sample class as given below)
How do i modify it so that it acts as Generic Class for all Tables and not just for one table.for eg:if i had another Table called person apart from Todo
i want it to use the same class for both tables
2)Is the method Im suggesting a bad design pattern and if so why?
3)I also dint understand how this class gets called.
Saw somewhere that ../tables/Todo
is mapped to this class.if thats the case.Where is the mapping done.?
4)Will ApiController achieve my purpose 1?if So an example please
using System.Linq;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.OData;
using Microsoft.WindowsAzure.Mobile.Service;
using TempService.DataObjects;
using TempService.Models;
using System.Web.Http.OData.Query;
using System.Collections.Generic;
namespace TempService.Controllers
{
public class TodoItemController : TableController<TodoItem>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// Create a new Azure Storage domain manager using the stored
// connection string and the name of the table exposed by the controller.
string connectionStringName = "StorageConnectionString";
var tableName = controllerContext.ControllerDescriptor.ControllerName.ToLowerInvariant();
DomainManager = new StorageDomainManager<TodoItem>(connectionStringName,
tableName, Request, Services);
}
public Task<IEnumerable<TodoItem>> GetAllTodoItems(ODataQueryOptions options)
{
// Call QueryAsync, passing the supplied query options.
return DomainManager.QueryAsync(options);
}
// GET tables/TodoItem/1777
public SingleResult<TodoItem> GetTodoItem(string id)
{
return Lookup(id);
}
// PATCH tables/TodoItem/1456
public Task<TodoItem> PatchTodoItem(string id, Delta<TodoItem> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/TodoItem
public async Task<IHttpActionResult> PostTodoItem(TodoItem item)
{
TodoItem current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/TodoItem/1234
public Task DeleteTodoItem(string id)
{
return DeleteAsync(id);
}
}
}
So I will try to address your questions by point:
Yes and no. What you are trying to do is follow the Repository Pattern. So yes, you can make a BaseRepository that will do the bulk of the work with a generic data type. No, you will still have classes that inherit the base but specify the generic data type.
No this is not a bad design pattern.
So the TableController is a specialized ApiController for the data table. It is called via the route configuration that translates the URL "/tables/TodoItem/Id"
Again, the TableController is an ApiController. Not sure it will help, but there are a number of examples of the "Repository Pattern" for Azure Mobile Services. You can look here to get an idea:
http://www.codeproject.com/Articles/574357/Repository-Pattern-with-Windows-Azure-Mobile-Servi

Evaluating ASPX Pages from custom httpHandlers

I have search everywhere for help and its starting to annoy me.
I am creating an Internal Tooling Website which stores Tools and their related information.
My vision is to have a web address (Http://website.local/Tool/ID)
Where ID is the ID of the Tool we want displayed.
My reasoning is then I can extend the functionality of the URL to allow for various other functions.
Currently I use a custom httpHandler which intercepts any URL which is in the 'Tool' Folder.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Tooling_Website.Tool
{
public class ToolHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
//The URL that would hit this handler is: http://{website}/Tool/{AN ID eg: http://{website}/Tool/PDINJ000500}
//The idea is that what would be the page name is now the ID of the tool.
//tool is an ASPX Page.
tool tl = new tool();
System.Web.UI.HtmlTextWriter htr = new System.Web.UI.HtmlTextWriter(context.Response.Output);
tl.RenderControl(htr);
htr.Close();
}
}
}
Basically I have a page inside the 'Tool' folder (Tool\tool.aspx) which I want my customer httpHandler to Render into the Response.
But this method doesn't work (It doesn't fail, just doesn't show anything) I can write the raw file to the response but obviously thats not my goal.
Thanks,
Oliver
If you still want to use your custom approach, you can try to do the following in your IHttpHandler derived class:
public void ProcessRequest(HttpContext context)
{
//NOTE: here you should implement your custom mapping
string yourAspxFile = "~/Default.aspx";
//Get compiled type by path
Type type = BuildManager.GetCompiledType(yourAspxFile);
//create instance of the page
Page page = (Page) Activator.CreateInstance(type);
//process request
page.ProcessRequest(context);
}

How share data in WCF webservice

In order to call webservices dynamicly, I use WCF Dynamic Proxy from Microsoft
If I understood properly how it works, the code load the wsdl and compile on system class in order to consume distant webservice. I put this code in a "generic webservice". Its goal is to call any webservice with a request in parameter, and respond the answer of the webservice called.
But a problem appears : each request to this "generic webservice" pulls a new compilation of the proxy, and use time and ressources of the server.
My objective is to save instance of each proxies during a laps of time, and renew the instance when this laps is reached.
After few hours of googling, I found two ways :
Use my WCF webservice "by session", but I don't find any tutorial which explains how create easily the session layer
Use a singleton in order to save my datas and mutualize them with all instances of webservice
I exclude the first solution because I don't know how to do this. So I decided to use the second way.
There is my implementation :
FactoryTest is the singleton, contening the hashtable with instances
ProxyTest is the class which contains information about each instances of distant webservices
There is the code of FactoryTest :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WcfSamples.DynamicProxy;
using System.Threading;
using System.Collections;
namespace WS_Generic
{
public sealed class FactoryTest
{
private static object syncRoot = new Object();
private static Hashtable hashFactory = new Hashtable();
public static DynamicProxy getProxy(String sServiceWsdl, String sContract)
{
if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
{
lock (syncRoot)
{
if (hashFactory[sServiceWsdl] == null || ((ProxyTest)hashFactory[sServiceWsdl]).getTimeFromCreation().TotalSeconds > 60 * 60 * 6)
{
hashFactory.Add(sServiceWsdl, new ProxyTest(sServiceWsdl, sContract));
}
}
}
return ((ProxyTest)hashFactory[sServiceWsdl]).getProxy();
}
public static bool isProxyExists(String sServiceWsdl, String sContract)
{
lock (syncRoot)
{
return hashFactory[sServiceWsdl] == null ? false : true;
}
}
}
}
There is the code of ProxyTest :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WcfSamples.DynamicProxy;
namespace WS_Generic
{
public class ProxyTest
{
private DateTime instanceCreation;
private String sServiceWsdl;
private String sContract;
private DynamicProxyFactory factory;
private volatile DynamicProxy proxy;
public ProxyTest(String sServiceWsdl, String sContract)
{
instanceCreation = DateTime.Now;
this.sServiceWsdl = sServiceWsdl;
this.sContract = sContract;
this.factory = new DynamicProxyFactory(this.sServiceWsdl);
this.proxy = factory.CreateProxy(this.sContract);
}
public DynamicProxy getProxy()
{
return proxy;
}
public TimeSpan getTimeFromCreation()
{
return DateTime.Now.Subtract(instanceCreation);
}
}
}
The problem is the webservice seems to reset the static status of FactoryTest after each call. So each time I called the webservice, my hashtable is empty and the factory create a new instance.
If anybody had already the problem of share datas between differents threads in WCF webservice (and found the solution), thanks in advance to give me some tips :)
PS : Sorry for my english, that's not my native language
If you store data in static variable WCF itself will not affect their purging. The problem must be somewhere else (application restart, app domain recreation, etc.).
Btw. this solution has only very limited usage because long living shared proxies should not be used and in many cases it can result in unexpected behavior. It can perhaps work only for services using basicHttpBinding.

Categories