Author Archives: admin

Authentication for Knockout/C# Web Application

We have an interesting situation: we’re developing a web application, using MVC4 that is heaving in Knockout. Mostly, I think, because the development thing thought it was cool, but also because the primary purpose of this application was to present data to the users from a database. That data needs to be presented in a number of different combinations. With KnockoutJS’s reputation for making tasks like that easier, it seemed to be a good fit.

However, not only will the data need to be presented on the web, it needs to expose web services so that users can use the data within their own applications.

And, on top of all of this, the user needs to be authenticated, and their level of permission known. This so we can determine which set of data to present (there will be multiple ones) and whether or not the user can do CRUD operations on the data.

So, I was presented the task of implementing user authentication. Since it took me a bit of time to figure it out, and I didn’t find alot of help on the web, I wanted to present what I did in order to help some other unlucky soul.

I should mention that this is code first programming. And that only the pertinent methods are shown in the Controllers; the CRUD you can figure out yourself.

Entities

    public class NameBaseEntity : IEntity
    {
        [Required]
        [DatabaseGeneratedAttribute(
              DatabaseGeneratedOption.Identity),Key()]
        public int Id { get; set; }

        [Required]
        [StringLength(256)]
        public string Name { get; set; }

        public DateTime Created_On { get; set; }
        public DateTime Modified_On { get; set; }
    }
    [Table("Group")]
    public class Group: NameBaseEntity
    {

    }
    [Table("User")]
    public class User: NameBaseEntity
    {
        [Required]
        public String Password { get; set; }
    }
   [Table("XRefUserGroup")]
    public class XRefUserGroup: IEntity
    {
        [Required]
        public int UserId { get; set; }

        [Required]
        public int GroupId { get; set; }

        [Key]
        public int Id
        {
            get;
            set; 
        }
        [Required]
        public System.DateTime Created_On
        {
            get;
            set;
        }
        [Required]
        public System.DateTime Modified_On
        {
            get;
            set;
        }
    }

Concrete Classes

    public class UserAuthentication: EntityOperations
    {

        public UserAuthentication(EFDbContext context, DbSet dataSet)
            : base(context, dataSet)
        {}

        /// Determine whether this user has provided the correct username and
        /// password to allow access to the system. If the user is valid,
        /// return the user id. If not, return 0.
        public int AuthenticateUser(String username, String password)
        {
            try
            {
                var userId = (from user in this._context.User
                              where user.Name == username && user.Password == password
                              select user.Id).Single();

                return userId;
            }
            catch (InvalidOperationException)
            {
                return 0;
            }

        }
    }
    public class UserAuthorization : EntityOperations
    {

        public UserAuthorization(EFDbContext context, DbSet dataSet)
            : base(context, dataSet)
        { }

        public bool IsUserMember(int UserId, int GroupId)
        {
            var isMember = from entry in this._context.XRefUserGroup
                           where entry.UserId == UserId && entry.GroupId == GroupId
                           select entry;
            return (isMember.Any());
        }

        public Dictionary<String, String> IsMemberOf(int UserId)
        {
            Dictionary<String, String> returnList = new Dictionary<String, String>();
            var memberGroups = from xref in this._context.XRefUserGroup
                               join groupName in this._context.Group on xref.GroupId equals groupName.Id
                               where xref.UserId == UserId
                               select new { GroupId = xref.GroupId, GroupName = groupName.Name };

            try
            {
                foreach (var dict in memberGroups)
                {
                    returnList.Add(dict.GroupId.ToString(), dict.GroupName);
                }
            }
            catch (InvalidOperationException) { }
            return returnList;                   
        }
    }

Controllers

   public class GroupController : Controller
    {
        private EFDbContext db = new EFDbContext();

       //
       // POST: /Group/PostGroupMembership/5
        public ActionResult PostGroupMembership(int memberId)
        {

            DbSet dbGroup = db.XRefUserGroup;

            HomeController controller = new HomeController();

            UserAuthorization auth = new UserAuthorization(db, dbGroup);
            UserAuthentication ua = new UserAuthentication(db, db.User);

            Dictionary<String, String> groupMembership = auth.IsMemberOf(memberId);

            User user = ua.GetItemById(memberId);

            Session.Add("Id", memberId);
            Session.Add("Uname", user.Name);
            Session.Add("Groups", String.Join(", ", groupMembership.Select(m => m.Key + ":" + m.Value).ToArray()));
            try
            {
                var tmp = groupMembership.Values.Where(p => p == "Administrator");
                Session.Add("IsAdmin",true);

            }
            catch (InvalidOperationException)
            {
                Session.Add("IsAdmin", false);
            }
            return RedirectToAction("Index", "Home");
        }
    public class UserController : ApiController
    {
        private DbContext db = new EFDbContext();

        /// Determine whether the user is authorized to use this system.

        public int GetAuthenticateUser(String user, String pass)
        {
            DbSet dbUser = db.User;

            UserAuthentication ua = new UserAuthentication(db, dbUser);

            return ua.AuthenticateUser(user, pass);
        }

        [HttpPost]
        public int PostAuthenticateUser(JObject identification)
        {
            DbSet dbUser = db.User;

            IList keys = identification.Properties().Select(p => p.Name).ToList();

            var obj = JObject.Parse(keys[0]);

            UserAuthentication ua = new UserAuthentication(db, dbUser);

            return ua.AuthenticateUser(obj["username"].Value(),obj["password"].Value());
        }
   }

KnockoutJS Code on the HTML Pages

var User = function (username, password) {
    var self = this;
    self.username = ko.observable();
    self.password = ko.observable();
    self.id = ko.observable(0);
    self.isAdmin = ko.observable(false);
    self.membership = ko.observable();
    self.hasError = ko.observable(false);

    self.id.subscribe(function (newValue) {
        this.hasError(newValue && newValue == 0);
    }, this);

    self.showName = function () {
        alert(self.username());
    };

    self.postLogin = function () {
        var jsonData = ko.toJSON(this);
        $.ajax({
            url: "/api/User/PostAuthenticateUser",
            type: "POST",
            data: jsonData,
            success: function (data) {
                self.id(data);
                if (data > 0) {
                    window.location.replace("/Group/PostGroupMembership?memberId=" + data);

                } else {

                    self.hasError(true);
                }
            },
            error: function () {
                self.id = 0;
            }
        });
    };

    return this;
};

On the Index page

    
   var Member = function () {
   var self = this;
   self.username = ko.observable('@Session["Uname"]');
   self.id = ko.observable('@Session["Id"]');
   self.isAdmin = ko.observable('@Session["IsAdmin"]' == 'True');
   self.membership = ko.observable('@Session["Groups"]');
};
$(document).ready(function () {
    var test = new Member();
    ko.applyBindings(test);
});

Migrating Gitorious to Another Server, Part II

UPDATE

When we last left our intrepid explorer, she was having problems with the new ssh keys being ‘Ready’. Uploading them was no problems. But the ‘Ready’ column in settings never had a checkmark.

The keys were in the file:

/var/www/gitorious/.ssh/authorized_keys

I found a clue here:

https://groups.google.com/forum/#!topic/gitorious/kb8CGXjt244

I tried the suggested command line (edited for the ‘acutal’ installation path):

QUEUE=* /var/www/gitorious/app/bin/rake resque:work

When I ran it manually, I saw the following warnings:

Failed loading some test dependencies: cannot load such file -- ci/reporter/rake/minitest WARNING! No value set for 
exception_recipients in gitorious.yml. Will not be able to send 
email regarding unhandled exceptions

But the keys were finally ‘ready’.

After reading about ‘Upstart’ files in CentOS, I did check that the script was running. And it was:

root 42854 1 0 15:26 ? 00:00:00 /bin/bash -i -l -c chruby ruby- && /var/www/gitorious/app/bin/rake resque:work git 42883 42854 64 15:26 ? 00:00:01 ruby /var/www/gitorious/app/bin/rake resque:work root 42886 51222 0 15:26 pts/0 00:00:00 grep resque

So I’m still at a loss as to why the polling isn’t working. I’m also curious why, if the key was in the authorized_keys file why I still couldn’t connect with that server.

More items to ponder!

Migrating Gitorious to Another Server

We’re migrating our cloud service to a more stable platform. Because of the nature of our original set up, it actually made mode sense to create new VMs and move our software team’s tools: Redmine, TeamCity and Gitorious, to the new servers.

I’m working currently on migrating Gitorious. It looks to be a fairly straightforward process: install Gitorious on the new server, upgrade the old server’s version to the the same one, then use the latest daily backup to move the data from one server to the other. The middle step may not be necessary to everyone. But it appeared that I was several updates behind.

I installed Gitorious on our new server. I was quite happy to see that there is now an installation script. When I originally installed the software, I ran through several online sets of directions before I finally found one that worked on our CentOS installation. Note that the directions I’ve linked to are for 5.5, not 6.x. But they did work.  I never did find one that ran successfully on Ubuntu.

But, as I said, installation ran much more smoothly with the installation script. I did run into one issue, however. The upgrade page says to type the following:

bundle exec rake changelog

to determine which version you’re on, and which ones you can upgrade to. I couldn’t get that command to work, so I’m going to assume that the latest version to which I can update the old one is the current version on the new one. Crossing fingers!

I am, as I said migrating to a new server. It is in a different network, so the IP address and FQDN will be different from the current server.

Installation

Well, the installation, I’m happy to say, went very smoothly. Using the installation script, everything went into place and the server started without a hitch. There were a couple of minor glitches along the way: There’s an error that kept popping up:

Failed loading some test dependencies: cannot load such file -- ci/reporter/rake/minitest

But a search on the web essentially said, “Don’t worry about it.” So I didn’t.

The other glitch was in the configuration file, gitorious.yml. Apparently, the notification email variable had changed from exception_notification_emails to exception_recipients. So I commented out the former, added the latter, and put in my email address.

Restoring Data

Next step was restoring data. I took the latest backup from our old server and, using the ‘bin/restore’ command, restored the data to my system.

Herein lay the problems. First of all, the changelog tool didn’t work to let me know what the new version was that I was installing. But I discovered by reading the documentation (amazing thing, documentation!) that I was installing version 3.0. Unfortunately, the furthest I could upgrade the old site was to 2.3. We’ll I’d see if I could get at least our repositories and projects restored, if nothing else.

Next issue: it couldn’t write to db/schema.rb. Odd, since the installation set the owners and permissions on the files. But I changed the owner from root to ‘git’ and that problem was solved.

The last issue was related to that new email variable:

undefined method `new' for ExceptionNotifier:Module

As soon as I commented that out, the restore worked perfectly ….. sorta.

When I tried restarting the server, I started getting 502 errors. I tried restarting the server and it didn’t seem to help.

I figured at this point that I probably had a mix of versions in place. So I tried an upgrade of the new install.

I’d again added the new email variable, exception_notifier, and again I received the above error.  So that variable is staying out of the config file for now.

Where I Am Now

Everything is up and running. All the projects show up and the repositories are all present and accounted for.

I am having a problem with the new system not accepting ssh public keys. You can upload them, but they are never ‘accepted’. Leastways, they weren’t showing as ‘accepted’ within the usual 5 -10 minutes. I’m guessing it might be an OpenSSH problem. But we’ll see.

I’ll post a comment here when I figure that one out.