ASP.NET – Pro9ramming https://pro9ramming.com Software craftsman's blog Wed, 15 Apr 2020 17:51:13 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.3 Generate Machine Key JSON Token https://pro9ramming.com/generate-machine-key-json-token/ Tue, 16 May 2017 12:03:46 +0000 http://pro9ramming.com/blog/?p=480 When creating a password reset functionality, cryptographic token generation is often required. Idea is to serialize an object to JSON (using Newtonsoft.Json) and protect it using MachineKey. To be able to safely transfer the token over URL (GET request), HEX string can be used.

public class MachineKeyToken
    {
        public static string GetToken(object tokenData, string purpose)
        {
            var json = JsonConvert.SerializeObject(tokenData);
            var plainBytes = Encoding.UTF8.GetBytes(json);
            var protectedBytes = MachineKey.Protect(plainBytes, purpose);
            return BytesToHexString(protectedBytes);
        }

        public static T GetFromToken<T>(string token, string purpose)
        {
            var stringBytes = HexStringToBytes(token);
            var unprotectedBytes = MachineKey.Unprotect(stringBytes, purpose);
            var plainBytes = Encoding.UTF8.GetString(unprotectedBytes);
            return JsonConvert.DeserializeObject<T>(plainBytes);
        }

        private static string BytesToHexString(byte[] bytes)
        {
            return BitConverter.ToString(bytes).Replace("-", "");
        }

        private static byte[] HexStringToBytes(string hexString)
        {
            if (string.IsNullOrEmpty(hexString))
                throw new InvalidOperationException("String is either null or empty.");

            if (hexString.Length % 2 != 0)
                throw new InvalidOperationException("Hex string length must be even.");

            return Enumerable.Range(0, hexString.Length)
                         .Where(x => x % 2 == 0)
                         .Select(x => Convert.ToByte(hexString.Substring(x, 2), 16))
                         .ToArray();
        }

    }

 

]]>
Send E-mail using ASP.NET https://pro9ramming.com/send-email-using-asp-net/ Wed, 19 Apr 2017 13:38:33 +0000 http://pro9ramming.com/blog/?p=470 Continue reading Send E-mail using ASP.NET]]> E-mail can be sent using System.Net.Mail namespace classes (not deprecated System.Web.Mail). First place the following configuration in Web.config:

<system.net>
    <mailSettings>
      <smtp from="from email">
        <network host="smtp.googlemail.com" 
                 port="587" 
                 userName="username" 
                 password="pass" 
                 defaultCredentials="false" 
                 enableSsl="true" />
      </smtp>
    </mailSettings>
  </system.net>

For testing purposes Google Mail server can be used. Google Mail requres SSL, so enableSsl=”true” has to be placed in the config. In order to send mail, SmtpClient and MailMessage classes are used:

using (SmtpClient client = new SmtpClient())
{
      MailMessage msg = new MailMessage();
      msg.To.Add("to email");
      msg.Subject = "Subject";
      msg.Body = "Body";
      msg.IsBodyHtml = true;
      client.Send(msg);
}

Configuration from Web.config is going to be automatically recognized. From address can be overriden using msg.From property. If body contains HTML, msg.IsBodyHtml property can be used.

]]>
ASP.NET MVC file upload to Azure Blob storage https://pro9ramming.com/asp-net-mvc-file-upload-to-azure-blob-storage/ Thu, 13 Apr 2017 12:41:09 +0000 http://pro9ramming.com/blog/?p=465 Continue reading ASP.NET MVC file upload to Azure Blob storage]]> Azure has it’s own blob storage functionality called Azure Blob storage. It’s a classic object storage cloud service like Amazon S3 and OpenStack Swift. Also, Riak works in similar fashion. Logic behind is pretty simple, account can have multiple containers which can have multiple files (objects or blobs). It can handle huge amounts of unstructured data.

First create a new ASP.NET MVC project and create a FileController. After that create a method which returns specific container.

private CloudBlobContainer GetContainer()
        {
            var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageAccount"]);
            var blobClient = storageAccount.CreateCloudBlobClient();
            var container = blobClient.GetContainerReference(ConfigurationManager.AppSettings["StorageContainer"]);
            return container;
        }

Account and container data can be saved in <appSettings> in Web.config:

<appSettings>
    <add key="StorageAccount" value="ACCOUNT CONNECTION STRING"/>
    <add key="StorageContainer" value="CONTAINER NAME" />
</appSettings>

Razor syntax for file upload looks like this:

@using (Html.BeginForm("UploadFile", "File", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <input type="file" name="file" />
    <br />
    <input type="submit" value="Upload" />
}

Action method UploadFile() contains some validation logic. GetBlockBlobReference() creates a new blob with specified fileName:

[HttpPost]
        public ActionResult UploadFile()
        {
            if (Request.Files.Count > 0)
            {
                var file = Request.Files[0];
                if (file != null && file.ContentLength > 0)
                    SaveToStorage(file.InputStream, file.FileName);
            }
            return View();
        }

        public void SaveToStorage(Stream inputStream, string fileName)
        {
            var container = GetContainer();
            var blob = container.GetBlockBlobReference(fileName);

            using (inputStream)
                blob.UploadFromStream(inputStream);
        }

Blob can be downloaded by using MemoryStream:

[HttpGet]
        public ActionResult DownloadFile(string fileName)
        {
            try
            {
                var container = GetContainer();
                var blob = container.GetBlockBlobReference(fileName);

                var memStream = new MemoryStream();
                blob.DownloadToStream(memStream);

                Response.ContentType = blob.Properties.ContentType;
                Response.AddHeader("Content-Disposition", "Attachment;filename=" + fileName);
                Response.AddHeader("Content-Length", blob.Properties.Length.ToString());
                Response.BinaryWrite(memStream.ToArray());

                return new HttpStatusCodeResult(200);
            }
            catch (Exception ex)
            {
                return Content(ex.ToString());
            }
        }

 

]]>
OWIN cookie authentication with roles (part 2) https://pro9ramming.com/owin-cookie-authentication-with-roles-part-2/ Fri, 17 Feb 2017 12:50:10 +0000 http://pro9ramming.com/blog/?p=443 Continue reading OWIN cookie authentication with roles (part 2)]]> In Part 1 of this tutorial, basic OWIN cookie authentication with roles has been set. This part describes further features than are added upon Part 1.

ReturnUrl 

First feature is ReturnUrl functionality (the possibility to return the user to the right place when after login). First of all, signature of Login() action method has to be changed a bit, by adding returnUrl parameter:

public ActionResult Login(LoginViewModel model, string returnUrl)

After checking credentials, adding claims and creating a cookie, returnUrl has to be checked:

if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
   return Redirect(returnUrl);
else
   return RedirectToAction("UserPanel");

If it’s not local or not specified, redirect to User panel.

In /Account/Login, Login form should look like this:

@using (Html.BeginForm("Login", "Account", new { returnUrl = Request.QueryString["ReturnUrl"] }, FormMethod.Post))

Make sure to place anonymous object in place where routeValues parameter is specified (not where htmlAttributes are).

AntiForgeryToken

In order to avoid CSRF attacks, AntiForgeryToken should be used in forms. Add the following line inside the form:

@Html.AntiForgeryToken()

And the [ValidateAntiForgeryToken] attribute to Login() method:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)

Using AntiForgeryToken with claims identity requires additional step. Add the following line in Application_Start() method in Global.asax.cs:

protected void Application_Start()
{
   //...
   AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
}

Changing/updating of claims

Claims have to be updated when user’s role is changed. Add the following code in AccountController:

[Authorize(Roles = "User")]
        public ActionResult SetAdminRole()
        {
            var identity = new ClaimsIdentity(User.Identity);
            identity.RemoveClaim(identity.FindFirst(ClaimTypes.Role));
            identity.AddClaim(new Claim(ClaimTypes.Role, "Administrator"));
            HttpContext.GetOwinContext().Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties { IsPersistent = true });
            return RedirectToAction("UserPanel");
        }

And link in /Account/UserPanel:

<p>@Html.ActionLink("Set admin role", "SetAdminRole", "Account")</p>

When user clicks specified link, his role (claim) is going to be changed.

ExpireTimeSpan and SlidingExpiration

These settings are set in CookieAuthenticationOptions:

app.UseCookieAuthentication(new CookieAuthenticationOptions {
                AuthenticationType = "ApplicationCookie",
                LoginPath = new PathString("/Home/Index"),
                CookieSecure = CookieSecureOption.SameAsRequest,
                ExpireTimeSpan = TimeSpan.FromSeconds(10),
                SlidingExpiration = true
            });

ExpireTimeSpan indicates the time cookie is valid. If SlidingExpiration is true, than middleware re-issues a new cookie with new expiration time when request is more than halfway through the expiration window. Default value for ExpireTimeSpan is 14 days, and SlidingExpiration is true. For testing purposes, in the above code, ExpirationTimeSpan is set to 10 seconds (TimeSpan.FromSeconds(10)).

 

]]>
OWIN cookie authentication with roles (part 1) https://pro9ramming.com/owin-cookie-authentication-with-roles-part-1/ Fri, 17 Feb 2017 12:19:34 +0000 http://pro9ramming.com/blog/?p=439 Continue reading OWIN cookie authentication with roles (part 1)]]> The aim of this tutorial is to create local cookie-based authentication using OWIN (without Identity). Additionally, roles authorization is going to be integrated into the project.

Start by creating an empty ASP.NET Web Application (for purposes of this project, it’s called “Example Project”).

Add new HomeController (with Index view) and AccountController (with Login, UserPanel and AdminPanel views).

Install following NuGet packages:

Microsoft.Owin.Host.SystemWeb

Microsoft.Owin.Security.Cookies

Microsoft.AspNet.Identity.Core

Create Startup class in the root of the project:

using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

[assembly: OwinStartup(typeof(ExampleProject.Startup))]
namespace ExampleProject
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                CookieSecure = CookieSecureOption.SameAsRequest
            });

        }
    }
}

When running this in production environment with HTTPS (SSL), use CookieSecureOption.Always to always issue a cookie with security flag.

Add the following ViewModel to Models folder:

public class LoginViewModel
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public bool RememberMe { get; set; }
    }

AccountController looks like this:

public class AccountController : Controller
    {
        public ActionResult Login()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Login(LoginViewModel model) //possibly add returnUrl
        {
            if (ModelState.IsValid)
            {
                if ((model.Username == "admin" && model.Password == "admin") || (model.Username == "user" && model.Password == "user"))
                {
                    var claims = new List<Claim>();
                    claims.Add(new Claim(ClaimTypes.Name, model.Username)); //available through User.Identity.Name
                    claims.Add(new Claim(ClaimTypes.Role, GetRole(model.Username)));
                    var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
                    Request.GetOwinContext().Authentication.SignIn(new AuthenticationProperties
                    {
                        IsPersistent = model.RememberMe
                    }, identity);
                    return RedirectToAction("UserPanel");
                }
                else
                    ModelState.AddModelError("", "Provided credentials not valid");
            }
            else
            {
                ModelState.AddModelError("", "Model state not valid");
            }
            return View("Index");
        }

        private string GetRole(string username)
        {
            return username == "admin" ? "Administrator" : "User";
        }

        [Authorize(Roles = "User, Administrator")]
        public ActionResult UserPanel()
        {
            Debug.WriteLine("UserPanel User.Identity.Name: " + User.Identity.Name);
            var claims = ((ClaimsIdentity)User.Identity).Claims;
            Debug.WriteLine("Role is: " + claims.SingleOrDefault(k => k.Type == ClaimTypes.Role).Value);
            return View();
        }

        [Authorize(Roles = "Administrator")]
        public ActionResult AdminPanel()
        {
            Debug.WriteLine("AdminPanel User.Identity.Name: " + User.Identity.Name);
            return View();
        }

        [Authorize(Roles = "Administrator, User")]
        public ActionResult Logout()
        {
            //DefaultAuthenticationTypes.ApplicationCookie actually not needed
            Request.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
            return View("Login");
        }

    }

When logging-in, after model state validation, claims are added to the identity. After that, cookie is created by using SignIn() method of AuthenticationManager. If there were a database in behind with UserId, claims would be added in this manner:

claims.Add(new Claim(ClaimTypes.NameIdentifier, userId));
claims.Add(new Claim(ClaimTypes.Name, FullName ?? UserName));

Role is checked based on username, but usually, database check can be done. Multiple roles can be added by iterating and adding them as ClaimTypes.Role type. User panel can be seen by both User and Administrator roles, and Admin panel only by Administrator role. Because of that, both roles are redirected to User panel. Claim can be read by using the lines presented above in User panel action:

var claims = ((ClaimsIdentity)User.Identity).Claims;
claims.SingleOrDefault(k => k.Type == ClaimTypes.Role).Value

Request.GetOwinContext().Authentication.SignOut() method is used to log out. It expects multiple authentication types. Add the link to login page in /Home/Index:

@Html.ActionLink("Login", "Login", "Account")

In /Account/Login add the model:

@model ExampleProject.Models.LoginViewModel

and the form (with links to panels – for testing purposes):

@using (Html.BeginForm("Login", "Account", FormMethod.Post))
    {
        @Html.ValidationSummary()
        @Html.TextBoxFor(k => k.Username)<br />
        @Html.PasswordFor(k => k.Password)<br />
        @Html.CheckBoxFor(k => k.RememberMe)<br />
        <input type="submit" value="Login" />
    }
    <br />
    <p>@Html.ActionLink("UserPanel", "UserPanel", "Account")</p>
    <p>@Html.ActionLink("AdminPanel", "AdminPanel", "Account")</p>

In /Account/AdminPanel add output of identity name, link to user panel and logout link:

<h1>Admin panel</h1>
<p>@User.Identity.Name</p>
<p>@Html.ActionLink("User panel", "UserPanel", "Account")</p>
<p>@Html.ActionLink("Logout", "Logout", "Account")</p>

In /Account/UserPanel add output of identity name, link to admin panel and logout link:

<h1>User panel</h1>    
<p>@User.Identity.Name</p>
<p>@Html.ActionLink("Admin panel", "AdminPanel", "Account")</p>
<p>@Html.ActionLink("Logout", "Logout", "Account")</p>

There is also Part 2 of this tutorial.

 

]]>
ASP.NET MVC appSettings configuration https://pro9ramming.com/asp-net-mvc-appsettings-configuration/ Wed, 16 Nov 2016 13:39:54 +0000 http://pro9ramming.com/blog/?p=402 If you don’t want to build your application on every configuration change, saving configuration to Web.config is a good solution. WebConfigurationManager class can be used to access this data. Web.config should have these lines:

<configuration>
   <appSettings>
      <add key="configItem" value="configItemValue" />
   </appSettings>
</configuration>

Data can be accessed in the application:

string configItem = System.Web.Configuration.WebConfigurationManager.AppSettings["configItem"];

 

 

]]>
jQuery datetime picker over AJAX in ASP.NET MVC https://pro9ramming.com/jquery-datetime-picker-over-ajax-in-asp-net-mvc/ Fri, 17 Jul 2015 12:33:37 +0000 http://pro9ramming.com/blog/?p=429 Continue reading jQuery datetime picker over AJAX in ASP.NET MVC]]> jQuery UI has datepicker, but there is a separate plugin that selects both date and time here. This tutorial shows how to send datetime value over AJAX and call ASP.NET MVC action (over POST method).

First of all, a way for Date and Time formatting is needed, and excellent jQuery plugin for that can be found here. Date formatting is done by using $.format.date() function.

Following scripts (along with jQuery core library) have to be placed in HTML:

<script src="@Url.Content("~/Scripts/jquery.datetimepicker.full.js")"></script>
<script src="@Url.Content("~/Scripts/jquery-dateFormat.js")"></script>

And also CSS for datetime picker:

<link media="all" rel="stylesheet" type="text/css" href="@Url.Content("~/Content/jquery.datetimepicker.css")" />

Place HTML input and button:

<input id="datetimepicker" type="text" />
<button type="button" class="btn btn-primary" id="save-btn">Save</button>

The following JS code creates datetime picker and attaches onclick listener to button:

$('#datetimepicker').datetimepicker({ 
   inline: true, //not drop-down 
   value: new Date(), //current datetime 
   step: 30 // time by 30 minutes 
}); 

$('#save-btn').click(function () { 
var datetime = $('#datetimepicker').datetimepicker('getValue'), 
    formatted = $.format.date(datetime, 'dd.MM.yyyy HH:mm'); 
    $.post('@Url.Action("ChangeDateTime", "SomeController")', 
    { id: '@ViewBag.Id', datetime: formatted }, 
    function (result) { 
        //Completed code 
    }); 
});

Value that is sent to the “dd.MM.yyyy HH:mm” format. In ASP.NET MVC SomeController place this action:

[HttpPost]
public bool ChangeDateTime(SomeModel data)
{
   //Save to db
}

SomeModel is a POCO class which takes Id and DateTime value from client side:

public class SomeModel
{
    public string Id { get; set; }
    public string DateTime { get; set; }
}

 

]]>
ASP.NET Razor – Output all server variables https://pro9ramming.com/asp-net-razor-output-all-server-variables/ Thu, 15 Jan 2015 03:43:27 +0000 http://pro9ramming.com/blog/?p=17 Continue reading ASP.NET Razor – Output all server variables]]> Server variables are listed in collection ServerVariables by using Get() method. Collection is enumerable so it can be iterated. Example is shown by using foreach in property – value style:

<ul>
@foreach (var x in Request.ServerVariables)
{
<li><span style="font-weight:bold">@x</span> - @Request.ServerVariables.Get(x.ToString())</li>
}
</ul>

Server variables that can be listed are: ALL_HTTP, ALL_RAW, APPL_MD_PATH, APPL_PHYSICAL_PATH, AUTH_TYPE, AUTH_USER, AUTH_PASSWORD, LOGON_USER, REMOTE_USER, CERT_COOKIE, CERT_FLAGS, CERT_ISSUER, CERT_KEYSIZE, CERT_SECRETKEYSIZE, CERT_SERIALNUMBER, CERT_SERVER_ISSUER, CERT_SERVER_SUBJECT, CERT_SUBJECT, CONTENT_LENGTH, CONTENT_TYPE, GATEWAY_INTERFACE, HTTPS, HTTPS_KEYSIZE, HTTPS_SECRETKEYSIZE, HTTPS_SERVER_ISSUER, HTTPS_SERVER_SUBJECT, INSTANCE_ID, INSTANCE_META_PATH, LOCAL_ADDR, PATH_INFO, PATH_TRANSLATED, QUERY_STRING, REMOTE_ADDR, REMOTE_HOST, REMOTE_PORT, REQUEST_METHOD, SCRIPT_NAME, SERVER_NAME, SERVER_PORT, SERVER_PORT_SECURE, SERVER_PROTOCOL, SERVER_SOFTWARE, URL, HTTP_CACHE_CONTROL, HTTP_CONNECTION, HTTP_PRAGMA, HTTP_ACCEPT, HTTP_ACCEPT_ENCODING, HTTP_ACCEPT_LANGUAGE, HTTP_COOKIE, HTTP_HOST, HTTP_USER_AGENT, HTTP_UPGRADE_INSECURE_REQUESTS.

 

]]>
Install NuGet packets in Visual Studio 2012 https://pro9ramming.com/install-nuget-packets-in-visual-studio-2012/ Tue, 21 Jan 2014 14:29:09 +0000 http://pro9ramming.com/blog/?p=390 Continue reading Install NuGet packets in Visual Studio 2012]]> Working on complex project usually requires adding loads of libraries (dependencies or packages). Those libraries require other libraries which require other libraries etc. So that led to creation of package management systems or package managers such as NuGet for .NET Framework) or Maven for Java. List of all package managers can be found here.

NuGet package manager is available in Visual Studio 2012 in Project -> Manage NuGet Packages…

There you can see packages installed in current project, as well as packages which are available online, along with package info.

To install package, select it and click “Install”, and that is going to install the newest version of selected package.

If you need older (specified) version of certain package, you can install it from Package Manager Console (Tools -> Library Package Manager -> Package Manager Console). After that you have “PM>” prompt for typing commands for package manager.

Command for installing for example Version 5.0.0 of EntityFramework is :

Install-Package EntityFramework -Version 5.0.0

References

http://en.wikipedia.org/wiki/NuGet

http://en.wikipedia.org/wiki/Apache_Maven

http://stackoverflow.com/questions/5628689/download-old-version-of-package-with-nuget

]]>