Introduction

PragmaTouch team is currently working on a new e-learning project. Below you can see a conceptual system diagram.

Users who access the Management UI inside the ASP.NET MVC 3 application are the content producers and they will access the system from standard web browsers to produce the content. Users who will consume the content (depicted at the bottom of the diagram) will access the system through our official mobile applications and other 3rd party applications. Our official applications and other client apps read/write data to the system through our Web API JSON.

We evaluated some options like OAuth, OAuth2 and simple API key authentication during the development process. All of the standard methods we have evaluated were too standard to implement or not secure enough. So we decided to go for our own simple but yet fairly secure API authentication mechanism.

API Authentication Ticket Request

On the server side authentication ticket requests are handled by an authentication controller which simply checks if "Request Validation Parameters" sent by the client are valid. If validation parameters are valid authentication controller generates a ticket and sends the ticket as JSON data to the client. Here is the authentication controller code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using Newtonsoft.Json;
using System.Globalization;

namespace PragmaTouch.WebApp
{
    public class AuthTicketController : Controller
    {
	    private readonly IAuthRequestValidator _authRequestValidator = null;
		public AuthController(IAuthRequestValidator authRequestValidator )
		{
			_authRequestValidator = authRequestValidator;
		}
		
        public ActionResult Index(string p1, string p2)
        {
          if (!_authRequestValidator.IsAuthRequestValid(p1,p2))
          {
            var authData = new AuthData { Ticket = String.Empty , Success = false, Error = "Invalid validation parameters"};
            return Json(authData, JsonRequestBehavior.AllowGet);
          }
          var ticket = new AuthTicket 
          { 
            ApiKey = "apikey_comes_here",
            P1 = p1,
            P2 = p2 
          };
          ticket.SetDefaultExpiresOn();

          JsonNetResult result = new JsonNetResult();
          result.Formatting = Formatting.Indented;
          result.Data = ticket.CreateEncryptedAuthData();
          return result;
        }

        public ActionResult ServerDateTime()
        {
          JsonNetResult result = new JsonNetResult();
          result.Formatting = Formatting.Indented;
          result.Data = new { dateTime = DateTime.Now };
          return result;
        }
    }
}

 

  • IAuthRequestValidator instance is injected by Ninject.MVC
  • Upon successfull validation AuthTicket is generated including the validation parameters and an empty ApiKey value. ApiKey value is a shared secret both by the server and client. Server does not put the Api Key value into the AuthTicket intentionally because the server will expect the client to populate the key to AuthTicket on subsequent API calls
  • Server sets a default expiration for the ticket.  
  • AuthTicket is encrypted by the server and the result is served to the client as AuthData JSON object.

API Request

 

The client application adds the authentication ticket (serialized as JSON) into the Authorization header of every API request. On the server side we implemented an ApiAuthorizeFilter attribute which intercepts the requests to the secured API actions and validates the authentication ticket included in the Authorization header of the request. Here is the code for the ApiAuthorizeFilter attribute

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;

namespace PragmaTouch.WebApp
{
  public class ApiAuthorizeAttribute:AuthorizeAttribute
  {
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
      var authorizationHeader = httpContext.Request.Headers["Authorization"];
      if (String.IsNullOrWhiteSpace(authorizationHeader))
        return false;

      try
      {
        AuthData authData = JsonConvert.DeserializeObject(authorizationHeader);
        AuthTicket authTicket = JsonConvert.DeserializeObject(authData.Ticket.Decrypt());
        DateTime expiresOn = DateTime.Now.AddDays(1);
        
        if (ConfigHelper.CheckForExpiredAuthTicket)
          expiresOn = AuthTicket.ParseExpiresOn(authTicket.ExpiresOn);
        
        return authTicket.ApiKey == ConfigHelper.ApiKey && expiresOn > DateTime.Now;
      }
      catch
      {
        return false;
      }
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
      var result = new JsonNetResult();
      result.Formatting = Newtonsoft.Json.Formatting.Indented;
      result.Data = new AuthData{Success = false, Error = "Authorization required"};
      filterContext.Result = result; 
    }
  }
}

 

  • ApAuthorizeAttribute simply checks if the authentication ticket has a valid Api Key value and if authentication ticket has expired (to minimaze chance of reply attacks)
  • Expiration control is optional and can be turned on/off with a value specified in the web.config file of the ASP.NET MVC 3 application
  • The same crypto algorithm and crypto keys are used on both sides (the server and client)

 

Refreshing the Authentication Ticket

Clients can refresh the authentication ticket using two alternatives

  1. Simply request for a new authentication ticket
  2. Call the ServerDateTime action in the AuthTicketController to find out the current server datetime then add some extra time to the ExpiresOn value of the authentication ticket if ticket has expired.

Sample Client Code

public class SampleApiClient
{	
	public class ServerDateTime
    {
      public string dateTime { get; set; }
    }

    private AuthData _authTicket = null;
	private bool ApiAuthenticate(stirng p1, string p2)
	{
		_authTicket = null;
		try
		{
		string apiUrl = "http://localhost/AuthTicket";
		WebClient wc = new WebClient();

		string authDataJson = wc.DownloadString(String.Format("{0}?p1={1}&p2={2}", apiUrl, p1, p2));

		_authTicket = JsonConvert.DeserializeObject(authDataJson);
		if (!_authTicket.Success)
		{
		  return false;
		}

		var authTicket = JsonConvert.DeserializeObject(_authTicket.Ticket.Decrypt());
		authTicket.ApiKey = ConfigHelper.ApiKey;
		_authTicket.Ticket = JsonConvert.SerializeObject(authTicket).Encrypt();
		return true;
		}
		catch (Exception ex)
		{
			// Do some error reporting
			throw ex;
		}
	}
	
	public bool GetServerDateTime(out DateTime dateTime)
    {
      dateTime = DateTime.MinValue;
      string apiUrl = "http://localhost/Api/AuthTicket/ServerDateTime";
      if (String.IsNullOrWhiteSpace(apiUrl))
        return false;
 
      WebClient wc = new WebClient();
      string jsonResult = wc.DownloadString(apiUrl);
      ServerDateTime result = JsonConvert.DeserializeObject(jsonResult);
      dateTime = AuthTicket.ParseExpiresOn(result.dateTime);
      return true;
    }
	
	public void ApiExpireAuthTicket()
    {
      if (_authTicket == null || String.IsNullOrWhiteSpace(_authTicket.Ticket))
        return;

      var authTicket = JsonConvert.DeserializeObject(_authTicket.Ticket.Decrypt());
      authTicket.ExpiresOn = String.Empty;
      _authTicket.Ticket = JsonConvert.SerializeObject(authTicket).Encrypt();
    }
	
    public bool RefreshAuthenticationTicket(object sender, EventArgs e)
    {
      if (_authTicket == null || String.IsNullOrWhiteSpace(_authTicket.Ticket))
        return;

      DateTime serverDateTime = DateTime.Now;
      if (!GetServerDateTime(out serverDateTime))
      {
        return false;
      }
      
      var authTicket = JsonConvert.DeserializeObject(_authTicket.Ticket.Decrypt());
      authTicket.ExpiresOn = AuthTicket.CreateExpiresOnString(serverDateTime.AddHours(12));
      _authTicket.Ticket = JsonConvert.SerializeObject(authTicket).Encrypt();
	  return true;
    }
	private string ApiRequest(string apiRequestUrl)
    {
      try
      {  
        MyWebClient wc = new MyWebClient(_authTicket);
        return wc.DownloadString(apiRequestUrl);
        
      }
      catch (Exception ex)
      {
			// Do some error reporting
			throw ex;
      }
    }
	
	
}

Here is the custom WebClient class which is used to add the authentication ticket to the request Authorization header as JSON serialized data.

public class MyWebClient:WebClient
{
	AuthData _ticket = null;
	public MyWebClient():base()
	{

	}

	public MyWebClient(AuthData ticket):this()
	{
	  _ticket = ticket;
	}

	protected override WebRequest GetWebRequest(Uri address)
	{
	  WebRequest request = base.GetWebRequest(address);
	  if (_ticket != null)
		request.Headers.Add("Authorization", JsonConvert.SerializeObject(_ticket));

	  return request;
	}
}

 


aliozgur posted on February 2, 2012 17:07

Recently we have reorginzed our Jira and implemented a User Request project to get the user requests in a single Jira project with all components in place. With this implementation user requests are assigned to our team managers then they decide how these requests will be mapped to internal projects. We also implemented a custom workflow and at some point (User Test) the user request is assigned to the reporter (our users). When our users complete the tesing and indicate success or failure the request is assigned back to the team managers. This implementation is working pretty well. Our users no longer create issues in our internal projects and the team managers can control the workload and schedule of their teams. With this implementation we can also give to the point reports to the management. In future we plan to be able to dedicate a virtual budget for each department our users work for so that we can calculate a virtual cost and time spent reports for each department.

This implementation has one downside so far; when a user request is created team managers, most of the time, create one or more issues in internal projects. Team managers copy the user entered summary and description while creating issues in our internal projects which are then linked to user requests. When we started this project we were just cloning the user requests and then we were moving the cloned user request to our internal projects. But this usage was braking the continuity of the user request issue numbers. For example; when the original user request with key value of UR-1 is cloned  the cloned user request will have the key value of UR-2. When we move UR-2 to our internal project Jira will not reclaim UR-2 for the next user request instead the next user request will have UR-3 as the key value.

We felt uncomfortable with the discontinued issue numbers so we decided not to use clone/move method and just decided to manually copy user entered summary and description to the issues created in our internal projects. Manual copy/paste is tedious and prone to errors so I cecked out the web to see if Jira has some built in plugin which will allow us to Clone/Copy an issue from one project to another project. Unfortunately I found out that Jira does not have this feature or any plugin to perform this task easily.

The next step was to discover if we could implement this plugin ourselves. The book titled "Jira Development Cookbook" by Jobin Kuruvilla from Packt helped me to grasp the details of Jira plugin development. We will develop our own Issue Copy plugin for Jira, when we have some spare time from daily tasks.

I will share the source code of the plugin from my blog once it is completed, so stay tuned and take a look at "Jira Development Cookbook" by Jobin Kuruvilla from Packt if you are interested in Jira development.


Posted in: General Development  Tags:
aliozgur posted on August 10, 2011 12:36

We have been using Jira at our office for 5-6 years or so. During this time I was a member of the development team and was just an end user of Jira. After the recent change in my position I'm expected to use Jira as a management tool. Default installation of Jira comes with some defaults which will definitely help you but if you want to effectively manage your team and the job you have to fine tune your Jira setup.

Jira 4 Essentials by Patrick Li (Packt Publishing)  is a must read book if you need to fine tune and customize your Jira installation to meet your very own needs. The book walks through each customizable bit of Jira and with the Help Desk Project section at the end of each chapter customizations are applied to a practical sample implementation. I highly recommend team managers and Jira admins to read Jira 4 Essentials. 

JIRA 4 Essentials


Posted in: General Development  Tags:
aliozgur posted on October 7, 2009 10:47

Yesterday I visited Rob Conery's blog and the post about commercial SubSonic support options made me think again about the open source philosophy. In my opinion Rob must decide in which category of Open Source is SubSonic located.

  • Is it a real open source project as defined by OSI
  • Is it an open source project in a way ASP.NET MVC is

If SubSonic is in the first category, I believe NHibernate is in that category, Ayende’s commerical support offering is not acceptable. Since bug fixes are included in that offering and Rob or Ayende are commiters that would not sound right to the community.

If SubSonic is in second category and Rob decide that SubSonic is open source but main official release is maintained and owned only by him or a company that commericial support offering would be ok.

Perfect Examples

Following OSS examples are very well suited to define my objection

Linus Torvalds does not offer bug fixes as a commerical support for the official Linux kernel. He does not because he is the main authority, and the unpaid authority, who decides if a bug fix or patch be applied to the official release of the kernel. (I do no think he has the time to review all submitted patches but he in a way organized the inner workings) But we all know that Suse and RedHat offer bug fixes and patches for their own distribution, which is understandable and valid. I do not mind the way RedHat or Suse patches and bug fixes are applied to the official kernel releases.

Another example is the Mozialla Foundation. If NHibernate had a non profit foundation as Mozilla and the foundation offered commercial support via kind of Mozilla Corporation that would be OK too. And I want to remind you that Mozilla like foundations do not distribute share profit to any third parties.

Questions

  1. As far as I know NHibernate is not copyrighted to anybody or any entity. So may other contributors claim copyright for the bugs they introduce which may cause some complications?
  2. The material itself and the functionality that material provides is not paid in OSS projects. Does offering commercial bug fix support right for the official release of the project cause the material to have some sort of monetary value, since fixing bugs is commercialized which means introducing bugs may be commercialized too?
  3. What if I, as a non commiter to NHibernate project, wanted to offer commercial bug fix support too? Do you think that project leads would allow me to be a committer just beacuse of that even if I'm not qualified to be a commiter? Shall I interpret Ayende as monopoly in NHibernate community context? Don't you think that being a virtual/possible monopoly conflicts with the open source?
  4. If an OSS is not copyrighted to anybody or any entity, do you think that OSS project leads hold legal rights to decide whom to let in or kick out?


Posted in: General Development  Tags:
aliozgur posted on January 30, 2009 13:10

The sample Ruby code has no syntax errors. What does the code through lines 3-5 mean? And what is printed to the screen/console?

Any Suggestions ? Smile

 

class Sample
  attr_writer :name
  attr_reader :name do
     "Mr. " + @name
  end
end

s = Sample.new
s.name = "Ali"
puts s.name

 


Posted in: General Development , Ruby  Tags:
aliozgur posted on May 19, 2008 09:00

Evolution of new web technologies directed enterprises to web based software solutions. New tecnologies and frameworks offer lots features for web based solutions but entreprises still need some sort of rich client back office software to access backend data or perform specialized and complicated business tasks.

Pragma BackOffice (PragmaBO) is an effort to provide extensible rich client framework for enterprise level back office solutions based on Microsoft platform (Windows, .NET , C#, MS SQL Server 2005/Express ). Main features of PragmaBO are More...


Posted in: .NET Development , General Development  Tags:
aliozgur posted on May 15, 2008 10:17
Subversion kullanımı ile ilgili iş arkadaşlarıma yönelik yazdığım notlar bu sayfada.

Posted in: General Development  Tags:
aliozgur posted on May 15, 2008 10:15

Agile yazılım geliştirme yöntemlerinden biri olarak oldukça popüler bir yaklaşım olan ICONIX'e genel bir bakış sağlamak için oluşturduğum sayfa burada .


Posted in: General Development  Tags:

For a while I am thinking about the Microsoft's support to open source projects. Some news that attracted my attention to this issue were

  • Foundation of CodePlex, open source project hosting web site of Microsoft
  • Ms-PL license developed by Microsoft  
  • Release of .NET Framework libraries  source code . Read more
  • Release of ASP .NET MVC source code. Read more

Heritage

Microsoft has developed very successfull technologies through history. COM/ActiveX formed a good baseline for MS platforms and related runtimes like MTS, COM+ and DCOM were a must for enterprise level application development targeted at MS platforms. VB was a fantastic programming language, even I've never written single line of VB code I remember how my co-workers rocked with VB, ASP was not perfect but it was, actually it still is, productive and easy to learn and VC++ powered with MFC was the programming language of choice for lower level software development. All these runtimes, frameworks and languages were not perfect, but they did succeeded in helping software developers to produce valuable software. However some of these technologies required software developers to have some sort of geek talent. For example it was very difficult for a regular software developer to write some sort of event sink COM code or programmatically configure the DCOM environment. I think Microsoft has learned more than we expected from the history I briefly tried to explain. Now we are at the age of .NET and related technologies and personally I expect less hesitation than the previous experiences we all ran through.
More...