Incorporating GDPR compliance is something that takes time, effort and thorough understanding of the law. In this post, we are going to show you how to create a stop-gap in your .NET Core web applications until you are able to invest time and resources into understanding your application's structure, usage of personal data, and restructuring it to conform to the GDPR.
We will make a sample ASP.NET Core web application to demonstrate how to put a GDPR compliance stop-gap in place.
Within the MaxMindService, add the following code:
We are using the <summary> tag to include comments that will show up when hovering over the members of MaxMindService. This is a great practice to do which exponentially helps understanding and readability whether coding individually or on a team. Don't be afraid to use this in your code!
Using the .AddSingleton creates the service as a singleton which reuses the first instance of the service that's created for any subsequent requests (until the application shuts down).
This is all great, but you may be asking - how do we use the service? Well, if we consider the original requirement that we want to redirect users from the EU to a separate page, the best page to put code to redirect users would be before they even see a page, right? In .NET Core, this means we need to write code within a filter.
Filters can run code before controller actions are executed, and since it is in the controller action that views get generated, it is the filter we will be creating next. Create a GDPRFilter class in the MaxMind folder and add the following code to it:
What this action filter is doing is looking at the client IP address and checking it against the MaxMindService (that is injected through constructor injection) into the action filter. If the data about the client's IP address contains "EU", that means the IP is from the EU and we should direct the user elsewhere.
As you also see above, I am also assigning a ViewBag property, isEU, that is set to True or False that can be used within our .cshtml views in case we want to conditionally show or hide certain elements on the page.
We still need to create the UnavailableController, but if you've been following in this tutorial - building the solution will result in an error because we actually haven't made the UnavailableController yet. Simply comment out the if statement that contains the UnavailableController and add an UnavailableController (uncomment the if statment after you've added this controller):
While this code is good, our action filter isn't going to actually be able to use the MaxMindService because we can't pass a dependency directly into an action filter. The way to do it is to use a ServiceFilter. What this means is we need to add our scoped filter to the Startup.cs class and on our controllers:
Running the site now will show us a normal page:
But - if we happen to un-comment some code within the GDPRFilter that's commented out, we can see what users will see if they happened to visit our site from the EU:
We will make a sample ASP.NET Core web application to demonstrate how to put a GDPR compliance stop-gap in place.
Why should you care about GDPR?
Before we get into the implementation, you should first understand roughly what the GDPR is and why you need to care about it. Put simply, the GDPR are a set of regulations that give consumers rights and protections to their personal information. In order to do business with consumers in the EU, or offer any service that uses consumer data of EU residents, you must be GDPR compliant. Failure to compliant will result in fines.
Reading the GDPR primer |
Blocking EU users
To become GDPR "compliant," we are going to block any user coming from the EU. Yes - that is our stop-gap. We do not have to worry about GDPR compliance if no user from the EU can visit our website*.
*Not legal advice, I am no attorney.
We will be using MaxMind's country database to check the IP address of the user and redirect the user to an empty page (that has no third party tracking or analytics) and display to them that GDPR compliance work is still underway.
Installing the DB
Head on over to the official page and download the GeoLite2 Country MaxMind DB.
What to download on the page |
If the link is broken, you can also find the DB file in a github repo of mine. This free version of the full service has just enough what we need, detecting if the user is coming from the EU.
Building the application
Create a new project in Visual Studio and create a new .NET Core web application. Be sure to choose the MVC (Model-View-Controller) application as we do not want Razor pages.
Creating our .NET Core web application |
Add a MaxMind folder to your solution. Within that folder, add the GeoLite2-Country.mmdb file that is contained in the tar.gz file you downloaded from the MaxMind page mentioned above. To add the .mmdb file, right-click the MaxMind folder and choose Add > Existing Item...
I'd recommend 7-Zip if you don't already have it installed to unarchive the .tar.gz file.
Creating the MaxMind folder |
Right click the GeoLite2-Country.mmdb file and select Properties and change the Copy to Output Directory value to Copy Always. Doing this will always copy up the file which will save our behind in case we are using the Publish features of Visual Studio (Azure App Service, IIS, FTP, folder, etc.) to deploy our application. If we don't do this, our deployed application won't have the the DB file in the solution and we'll get an error when trying to access the DB file.
Next, go and right-click your project (GDPR in the screenshot above) and choose Manage NuGet Packages... Search for "maxmind" and install the MaxMind.Db package. If you aren't seeing any packages, be sure your Package source is set to All.
Create two files within the MaxMind folder; IMaxMindService and MaxMindService. Within IMaxMindService, add the following code:
Copy always the file |
Next, go and right-click your project (GDPR in the screenshot above) and choose Manage NuGet Packages... Search for "maxmind" and install the MaxMind.Db package. If you aren't seeing any packages, be sure your Package source is set to All.
Installing MaxMind.Db Nuget package |
Create two files within the MaxMind folder; IMaxMindService and MaxMindService. Within IMaxMindService, add the following code:
using System.Collections.Generic; namespace GDPR.MaxMind // YOURS WILL BE DIFFERENT! { public interface IMaxMindService { Dictionary<string, object> GetData(string IP); } }
Within the MaxMindService, add the following code:
using MaxMind.Db; using System; using System.Collections.Generic; using System.Net; namespace GDPR.MaxMind // YOURS WILL BE DIFFERENT! { public class MaxMindService : IMaxMindService, IDisposable { private Reader Reader { get; set; } /// <summary> /// Returns the Reader that use our DB file /// </summary> /// <returns></returns> private Reader GetReference() { if (Reader == null) { Reader = new Reader("MaxMind/GeoLite2-Country.mmdb"); } return Reader; } /// <summary> /// Returns data of a given IP address /// </summary> /// <param name="IP"></param> /// <returns></returns> public Dictionary<string, object> GetData(string IP) { return GetReference().Find<Dictionary<string, object>>(IPAddress.Parse(IP)); } /// <summary> /// Disposes the object /// </summary> public void Dispose() { if (GetReference() != null) { Reader.Dispose(); } } } }
We are using the <summary> tag to include comments that will show up when hovering over the members of MaxMindService. This is a great practice to do which exponentially helps understanding and readability whether coding individually or on a team. Don't be afraid to use this in your code!
The <summary> tag in action |
What did we just do?
We added the database file to our solution, which contains IPs and whether or not these IPs are in the EU (among other things). Next, we created a service contract with our IMaxMindService interface and created an implementation of that interface with our MaxMindService class.
Service contracts are frequently used within the realm of WCF. However - we are using them in this context to refer to a service we are going to register within the .NET Core dependency injection.
What is next?
We will register our service within the .NET Core services collection by going into Startup.cs and adding the following code (you'll need to add a using statement too at the top of your Startup.cs file as well):
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); // Added this line below! services.AddSingleton<IMaxMindService, MaxMindService>(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
Using the .AddSingleton creates the service as a singleton which reuses the first instance of the service that's created for any subsequent requests (until the application shuts down).
This is all great, but you may be asking - how do we use the service? Well, if we consider the original requirement that we want to redirect users from the EU to a separate page, the best page to put code to redirect users would be before they even see a page, right? In .NET Core, this means we need to write code within a filter.
.NET Core request pipeline |
Filters can run code before controller actions are executed, and since it is in the controller action that views get generated, it is the filter we will be creating next. Create a GDPRFilter class in the MaxMind folder and add the following code to it:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Newtonsoft.Json; using System.Collections.Generic; using System.Net; namespace GDPR.MaxMind // THIS WILL BE DIFFERENT FOR YOU { public class GDPRFilter : ActionFilterAttribute { private readonly IMaxMindService _iMaxMindService; public GDPRFilter() { } public GDPRFilter(IMaxMindService iMaxMindService) { _iMaxMindService = iMaxMindService; } public override void OnActionExecuting(ActionExecutingContext context) { // Grab the client's ip address IPAddress clientIP = context.HttpContext.Connection.RemoteIpAddress; // Localhost woes if (clientIP.ToString() == "::1" || clientIP.ToString() == "127.0.0.1") { (context.Controller as Controller).ViewBag.isEU = "False"; // Un-comment me to test redirect on localhost //if (!(context.Controller is UnavailableController)) //{ // context.Result = new RedirectToActionResult("Index", "Unavailable", null); //} } else { // See if the client is from the EU Dictionary<string, object> raw = _iMaxMindService.GetData(clientIP.ToString()); var continentData = (raw["continent"]); var json = JsonConvert.SerializeObject(continentData); var dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(json); if (dictionary["code"].ToString() == "EU") { (context.Controller as Controller).ViewBag.isEU = "True"; if (!(context.Controller is UnavailableController)) { // Redirect to our not available controller context.Result = new RedirectToActionResult("Index", "Unavailable", null); } } else { (context.Controller as Controller).ViewBag.isEU = "False"; } } } } }
What this action filter is doing is looking at the client IP address and checking it against the MaxMindService (that is injected through constructor injection) into the action filter. If the data about the client's IP address contains "EU", that means the IP is from the EU and we should direct the user elsewhere.
As you also see above, I am also assigning a ViewBag property, isEU, that is set to True or False that can be used within our .cshtml views in case we want to conditionally show or hide certain elements on the page.
We still need to create the UnavailableController, but if you've been following in this tutorial - building the solution will result in an error because we actually haven't made the UnavailableController yet. Simply comment out the if statement that contains the UnavailableController and add an UnavailableController (uncomment the if statment after you've added this controller):
using Microsoft.AspNetCore.Mvc; namespace GDPR.Controllers // YOURS WILL BE DIFFERENT { public class UnavailableController : Controller { public IActionResult Index() { return View(); } } }
The UnavailableController |
While this code is good, our action filter isn't going to actually be able to use the MaxMindService because we can't pass a dependency directly into an action filter. The way to do it is to use a ServiceFilter. What this means is we need to add our scoped filter to the Startup.cs class and on our controllers:
Adding .AddScoped<GDPRFilter>() |
Adding the ServiceFilter to our controllers |
Finishing up
We are almost done. We never actually made the view for the UnavailableController, let's do that now:
Adding the unavailable page |
Index.cshtml
@{ ViewData["Title"] = "EU!"; } <p> You are from the EU, we can't serve you now. Check back later. </p>
Running the site now will show us a normal page:
The homepage |
But - if we happen to un-comment some code within the GDPRFilter that's commented out, we can see what users will see if they happened to visit our site from the EU:
Testing the EU redirect locally |
Blocked! |
Thanks for reading
The full github repo can be found right here if you'd like to download the full source.
I really enjoyed this article. I need more information to learn so kindly update it.
ReplyDeleteDOT NET course in bangalore
DOT NET Training in Bangalore
.Net training in chennai
.net coaching centre in bangalore
dot net training institutes in bangalore
.net coaching centre in chennai
Python Training in Bangalore
Angularjs Training in Bangalore
Wonderful blog!!! Thanks for sharing this great information with us...
ReplyDeleteSEO Training in Chennai
SEO Course in Chennai
SEO Training Institute in Chennai
SEO institute in Chennai
SEO training in Tnagar
SEO training in Thiruvanmiyur
Big data training in chennai
Software testing training in chennai
Selenium Training in Chennai
JAVA Training in Chennai
you deserve for the price you pay. cursos de ti
ReplyDeleteThank you for writing this informative post. High Technologies Solutions providing best coaching classes in south delhi.Otherwise If any one who want to learn dot-net contact us on 9311002620 or visit:- https://htsindia.com/Courses/microsoft-courses/dotnettraininginstituteinsouthdelhi
ReplyDeleteAwesome blog. Thanks for sharing such a worthy information....
ReplyDeleteWhy Should Learn Python
Importance of Learning Python
An excellent article. Quality information is provided in this article. Without a doubt, I'll investigate it. Here are some incredibly helpful tips. Much appreciation. The good work must continue.
ReplyDeleteColleges In Hyderabad For B.com
This is a great website that I recently found. I was, in fact, apprehended using this resource that you provide. Big congratulations for building such an amazing blog website!Best CA Coaching Centres in Hyderabad
ReplyDeleteIt was this wonderful website that I recently came onto. I was actually apprehended using this resource that you have available. A huge congratulations for building such a wonderful blog website! Colleges In Hyderabad For B.com
ReplyDeleteThis site is fascinating and incredibly inventive. After a long search, I finally find this wonderful article with a clear description of the subject, and I find this post to be really helpful. Kindly update them so I can enjoy more original posts from you.B.Com Colleges In Hyderabad
ReplyDelete