DPS907 notes – Tue Nov 1

Security topics for web services.

 

Test today

The weekly test will be done at the beginning of the class timeslot at 11:40am.

 

Theme for today, and code examples

After learning something about secured web services, we can cover some topics that build a stronger foundation, user account initialization and management, and claims management. We’ll need some answers there before moving on to next week’s topics.

Code examples for today include:

ManageClaims – learn about a design for structured or guided claims management

ManageUserAccounts – learn how a “user accounts administrator” can manage user accounts

 

How a security-aware app loads (starts up)

At this point in time, it is useful to know a little about how a security-aware app loads (or starts up). We will cover this in more detail next week (likely), but here is the simplified sequence of steps that happen when an app loads into server memory:

  1. In the HttpApplication subclass, the Application_Start method runs
  2. (for Web API project…) In the WebApiConfig class, the Register method runs
  3. In the Startup class, the Configuration method runs, which calls the ConfigureAuth method
  4. The user account database is initialized, as are the user manager and sign in manager components

Why is this useful now?

So that we can initialize a new app with one or more user accounts, in code.

 

Initialize a new app with user accounts

The security-aware project templates, for web app and web service projects, are configured to enable user account creation, with no code changes. However, the result is unsatisfactory, because you have to really know what you’re doing, it takes some effort, and the resulting user account(s) do not have the configuration settings (e.g. claims) that are typically needed in a new app.

So, it would be nice to programmatically create new and fully-configured user accounts, when the app loads for the first time. Here’s how we will do that task.

First, recall (from above) the sequence of steps that happen when an app loads into server memory. We will plan to do our user account creation task at the end of that sequence. More about that soon.

Next, let’s create a static class that can check whether any users have been defined already. If no user accounts are found, then it will create the first user. We’ll make that user an administrative-type user, specifically a user account administrator. (That role may indeed be different from other system-related or admin-related roles.)

In the Models folder, create a new class named IdentityInitialize. Make it a static class. Add one static method, LoadUserAccounts. (We may add other methods in the future.)

Here’s some sample code. As its comment states, change any of the data below to better match your app’s needs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
// added...
using Microsoft.AspNet.Identity.EntityFramework;
using System.Security.Claims;
using System.Threading.Tasks;

namespace SecMVC2.Models
{
  public static class IdentityInitialize
  {
    // Load user accounts
    public static async void LoadUserAccounts()
    {
      // Get a reference to the objects we need
      var ds = new ApplicationDbContext();
      var userManager = new ApplicationUserManager(new UserStore<ApplicationUser>(ds));

      // Add the user(s) that the app needs when loaded for the first time
      // Change any of the data below to better match your app's needs
      if (userManager.Users.Count() == 0)
      {
        var user = new ApplicationUser { UserName = "admin@example.com", Email = "admin@example.com" };
        var result = await userManager.CreateAsync(user, "Password123!");
        if (result.Succeeded)
        {
          // Add claims
          await userManager.AddClaimAsync(user.Id, new Claim(ClaimTypes.Email, "admin@example.com"));
          await userManager.AddClaimAsync(user.Id, new Claim(ClaimTypes.Role, "UserAccountAdministrator"));
          await userManager.AddClaimAsync(user.Id, new Claim(ClaimTypes.GivenName, "User Account"));
          await userManager.AddClaimAsync(user.Id, new Claim(ClaimTypes.Surname, "Administrator"));
        }
      }
    }
  }
}

 

Notice what we just did: We created a new user, with some very specific claims. We are saying that the app will have a role claim, UserAccountAdministrator, which denotes a special role or status.

Now, open and edit the ConfigureAuth method in the startup class (in the App_Start\Startup.Auth.cs source code file).

Call the method that you wrote above, after the user and sign in managers have been initialized. For example:

public void ConfigureAuth(IAppBuilder app)
{
  // Configure the db context, user manager and signin manager to use a single instance per request
  app.CreatePerOwinContext(ApplicationDbContext.Create);
  app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
  // You will see the following in an app that has UI for login
  app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

  // Initialize user accounts for the app
  IdentityInitialize.LoadUserAccounts();

  // etc. (other code continues below)

 

Introduction to this week’s work with claims

Today and this week, we will break new ground in our course work. It’s based on the claims topic that was introduced before the study week break and Assignment 7: We need to design a way that we can work with and manage custom claims.

First, let’s re-visit claims, specifically the standard and well-known claims. Yes, there are claims for names and identifiers. And, we have role claims, which were created to help transition knowledge from the past.

Here’s a few suggestions, or recommendations, for designing and managing role claims:

Attempt to limit the number of roles that are created in an app. There should be a fairly small number. Over time, the number of roles should not grow too much or too frequently.

A role should be used to broadly describe a fairly large number of users who share a access- or use-oriented goal in your app. For example, a public-facing app could have an Employee role, a Customer role, and an Administrator role. Consider a role to be a coarse-grained way to design large or overview access or use categories.

Until further notice, do NOT use roles for any of the following purposes:

  • Fine-grained access control to a resource
  • Permission to perform an activity
  • Descriptive or personalization information

Instead, capture these purposes by using custom claims.

Is this a best practice?

No. This is guidance. We will experiment with this approach, and see what happens.

 

Custom claims design

It is reasonable to expect that the number of custom claims – their types and values – will be numerous in a busy app. As a result, we should attempt to introduce some structure and limits in our initial design. That way, we strike a balance between having a detailed roadmap to a destination, and having no idea where to go next.

So, let’s design an initial approach for our custom claims…

 

…for access control

In an app, we often want to control access to a resource. A high-level way is to use a role claim. However, we often need a finer-grained approach. For example, consider your role as a student here at Seneca.

If an app had resources that were intended to be accessed by School of ICT students, should we introduce a role? Probably not, for several reasons. First, we would then be tempted to create roles for each and every other School at the College, so there would be dozens. Next, it doesn’t add much to the overall conversation about “roles” and their membership.

Let’s take this example a step further: Assume that the same app had resources that were intended to be accessed only by those in the CPA and BSD academic programs. Another role? No. (The College has about 140+ academic programs. Way too many to be considered for roles.)

This scenario is ideal for using custom claims. The big initial question is “what claim types and values should we design? for this purpose?”

How about using a claim type of “OU”. We borrow this concept from directory services, and specifically an organizational unit, or OU. The concept of an OU is quite flexible, and can help solve problems from many different knowledge domains.

Looking again at the scenario above, we could create several OU values:

  • OU = FASET (Faculty of Applied Science and Engineering Technology)
  • OU = SICT (School of ICT – could be used for students, and for faculty)
  • OU = Faculty (faculty member, who would also have an “Employee” role claim)
  • OU = Advisor (for staff and faculty who do student advising in their job)
  • OU = CPA (those attached to the CPA academic program)
  • OU = Level1 (new students in level/semester 1)
  • etc.

 

…for permissions

In an app, we often want some activities to be done only by those with permission. Again, a high-level way is to use a role claim. However, a medium-sized app could have many resources with dozens of activities possible (familiar CRUD activities, as well as commands). Again, a poor match for roles.

This scenario is also ideal for using custom claims. What claim types and values should we design for this purpose?

How about using a claim type of “Task”. In Assignment 7, we used “Activity” as a claim type, but it could be argued that “Task” is a better word to use in the English language.

If we continue to use the College scenario/domain from above, we could create several “Task” values:

  • Task = TimetableView (for students, and academic advisors)
  • Task = PhotoEditor (can upload a photo for a user account)
  • Task = GradebookEdit (for faculty, when a course is running)
  • Task = GradebookView (for faculty, and for students in the course)
  • etc.

 

…for descriptions

Some apps need user account data that is not used for the access control and activity permission purposes discussed above. Maybe the data expresses a preference, maybe it’s descriptive, maybe it’s used for interaction personalization – the possibilities are many.

Again, this scenario is ideal for using custom claims. What claim types and values should we design for this purpose?

Well, we do not offer any prescriptive guidance here. However, in an app, as you gain experience, if you can blend your use of role and custom claims – including OU and Task – then the app’s reason-for-being will probably suggest some useful descriptive or personalization claim types.

 

In summary…

For access control, we suggest using a custom claim type of “OU”.

For activity permission, we suggest using a custom claim type of “Task”.

 

Claims management – getting started

At this point, assume that we agree with the design ideas discussed above. There are remaining questions:

What values are allowed for the role claims? Anything? In any format (UPPER CASE, lower, MiXeD, with typoes typos?) Or, a reasonably-limited number of values, with an agreed-upon way to manage the list of values?

Next, what types and values are allowed for custom claims? We are probably comfortable starting with OU and Task claim types, but we have the same issue with their values.

Does the claims storage infrastructure help us in any way? Maybe not. Here is the class diagram of the entities that are involved in storing user account data in an app that uses the ASP.NET Identity system (repeated from the web apps course notes from February 24, 2016, which introduced security topics). Click the image to open it full-size in a new tab/window:

identity-models

 

The red-outlined entities – AspNetUser and AspNetUserClaim – are the most important to us. A simple one-to-many association – one user can have zero or more claims.

However, there’s no “master list” of claim types or values. No restrictions. Whatever we create in code – when registering a new user account, or later – will be stored. Which may not be ideal for an app.

So, maybe we should design a “master list” of allowable claims. Then, we manage the creation of claims, during the user account creation and management activities, so that we have some guidance and best practices to rely on.

 

General approach to creating the “master list”

Open the code example ManageClaims, and follow along. The code example implements many of the tasks discussed below.

Let’s create a class named “AppClaim”. We can add its source code to the existing IdentityModels.cs file (and add its DbSet<TEntity> property in the ApplicationDbContext class).

Next, in the Controllers folder, create a source code file to hold the resource models. Create “add” and “base” resource model classes.

Install AutoMapper version 4.2.1, and add a config source code file (in the App_Start folder) to hold the create-maps.

Back in the Controllers folder, add a Manager class, and an AppClaims (Web API) controller.

The Manager class will have methods for the common CRUD tasks. In addition, it will have extra logic and some extra methods to support the claims management process.

Note… Study the Manager class in the code example to learn more. It has defined many/most of the tasks, but not all are implemented yet.

The Controller class will support the common use cases (e.g. get-all, get-one, add-new, etc.). It will work only for the “user account administrator” user – therefore, it must be protected by a suitable [Authorize] attribute.

Note… Study the AppClaimsController class in the code example to learn more. Similar to above, it implements some of the basics, but not everything yet.

At this point in time, we will have the basic ability to manage a “master list” of custom claims.

Now, we need to use that “master list”, during user account registration tasks.

 

New user account registration, with claims management

In the past, you have worked with the “account” web service controller to create/register new user accounts.

That process does not change much. Open and study the ManageClaims code example as we discuss this topic.

In summary, we must validate the requested claims, by looking at the master list. If a requested claim is NOT on the master list, then the create/register attempt will fail.

The existing code in the account controller’s Register() method shows how to get started doing this. The code will loop through the requested role claims, and validate each one against the master list.

Is the method complete? NO. It needs code that will loop through the requested custom claims. We’ll probably do that as an in-class exercise.

 

User account management introduction

The other important topic for today is user account management.

At this point in time, you have some experience with creating/registering new user accounts in a web service. And above, you just learned how to validate the requested claims against a master list of allowable claims.

Does the project template include the built-in ability to manage user accounts? No.

 

Getting-started rules and information

As you know, the ASP.NET Identity system stores user account data in the database-backed persistent store.

Therefore, it’s fair to ask whether we use the traditional data context (ApplicationDbContext) to work with user accounts.

No.

Never. (Well, almost never.)

What do we use instead?

ASP.NET Identity includes a UserManager class. It is an API to the identity management functions of the ASP.NET Identity system. Read/skim the documentation, and notice that it has functionality for almost everything that we would be interested in doing:

Properties:

  • Users collection (i.e. all user accounts)

Methods:

  • Add claim, remove claim (for a user)
  • Change password *
  • Create new user account *
  • Delete a user account
  • Find a user account *
  • Get claims for a user

The methods marked with an asterisk ( * ) are used in the account controller that’s already in the template. That controller is designed for some very specific tasks, but it was not designed as a general-purpose tool for user account administrators. Instead, it was designed to help one user create/register a user account, and then manage it (e.g. change password etc.).

 

Best practice:

ALWAYS prefer and attempt to use the ASP.NET Identity API – mostly the UserManager class – to work with user accounts and claims.

There are a few tasks that the API does not expose. In those situations – if you’re careful – you can go around the API, and work with the data context. But please prefer and attempt to use the API.

 

Adding user account management to a web service

While going through this section, open and study the ManageUserAccounts code example.

We established (above) that the existing account controller was not designed as a general-purpose tool for user account management. We could add more methods to that controller, but maybe we should create a new controller, with backing methods in the Manager class.

In addition, let’s continue to use the long-established design practices – including resource models – when we’re working with the ASP.NET Identity entities (user accounts and claims).

 

Resource models

Look again at the ASP.NET Identity entity for a user account. Its class name is ApplicationUser (as seen in the IdentityModels.cs source code file).

Use Visual Studio F12 (go to definition) to explore:

  1. ApplicationUser inherits from IdentityUser
  2. IdentityUser includes a handful of useful (to us) properties, e.g. Id, Email, UserName, and a collection of Claims

The claims collection data type is IdentityUserClaim, which has useful properties, e.g. ClaimType, ClaimValue, UserId.

One aspect of the ASP.NET Identity entity design is that the IdentityUser has a to-many association with IdentityUserClaim. This makes sense, in that a user account will have a collection of zero-or-more claims.

However, the IdentityUserClaim collection (as a whole) will actually have claims that have the same ClaimType and ClaimValue, differing only in their Id and UserId. That’s by design. So, in the future, if you’re looking for “a claim” that matches a given ClaimType and ClaimValue, there may be more than one match. In other words, the collection could hold dozens (or more) claims, for different users, with (for example) a ClaimType of “role” and a value of “student”.

When we design the resource models for user account management, let’s continue to follow a best practice that we learned when working with to-many associations:

  1. Create a “base” user account resource model class
  2. Create a “with claims” class that inherits from the base class
  3. Then, create an “add” claim resource model class
  4. Finally, create a “base” class that inherits from the add class

We will NOT create an “add” resource model for a user account. The existing RegisterBindingModel (used by the account controller’s Register() method) already fulfills that purpose.

Here’s a class diagram. Click to open the image full-size in a new tab/window.

uam_vm

 

Use cases (for controller actions and manager methods)

The ManageUserAccounts code example shows how to work with some of the getting-started use cases:

  • Get all user accounts
  • Get one by (string) identifier
  • Get one by email
  • Get all by matching surname

 

Notice that we’re typically working with string identifiers. As a result, we will be using attribute-based route definitions for most of the controller methods.

Notice also that the attribute-based route definitions embed the string identifier within the URI, instead of placing it at the end of the URI. This will lower the error and “not found” rate, because users/requestors don’t have to worry about arcane web server and runtime URI processing rules.

In the Manager class, notice the UAGetAllBySurname() method. It fetches the user accounts, from the store, into memory, before querying. That is a common pattern when using the ASP.NET Identity API. If you ever attempt to do a repeating task, or a sub-query task, and receive a “data reader still open” error message, then you can solve that by using this pattern.

 

Adding on functionality

Notice that the code example has only “get” methods, but we will want to work with claims (add, change, remove) in a real-world app.

We’ll leave that work for Assignment 8, but we will discuss it during today’s class/session.

 

 

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

%d bloggers like this: