DPS907 notes – Tue Nov 15

A number of topics that enhance our web service programming skills.

 

Test today

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

Today’s test may also include questions from any of the security topics covered in weeks 7, 8, and 9. In other words, this test may help wrap up our coverage of security topics.

 

Theme for today, and code examples

We revisit the task of getting access to identity information in the controller and manager, at runtime. We look at a way to make that a better experience.

Next, we look at resource ownership, where we allow access to the owner of a piece of data, and deny others.

Finally, we will start to work with a free (no cost) Microsoft Azure services account.

Code examples for today include:

RequestUser class – Add this to any project for easy access to information about the security principal

ObjectOwner – Shows how to work with an entity collection that offers privacy to an object’s owner

 

Get identity info at runtime

In the October 18 notes, you learned how to get access to the security principal – information about the identity – at runtime, in the User property.

In a controller, it’s simply the top-level User property. Also, if you want to determine whether a request is authenticated, the Request.IsAuthenticated property will tell you.

In a manager module, it’s the HttpContext.Current.User property. Also, if you want to determine whether a request is authenticated, the HttpContext.Current.Request.IsAuthenticated property will tell you.

 

User data type is ClaimsPrincipal

In either situation, the best practice is to cast the security principal to a ClaimsPrincipal object, which exposes these top-level properties:

  • Claims (collection)
  • Identity, and its properties Name (user name) and IsAuthenticated

 

We must inspect the Claims collection to learn about surname, given name(s), role claims, and custom claims.

// Assume that we are in the Manager class...
var user = HttpContext.Current.User as ClaimsPrincipal;

// Various values...
var userName = user.Identity.Name;
var isStudent = user.IsInRole("Student");
var surname = user.Claims
  .SingleOrDefault(c => c.Type == ClaimTypes.Surname)
  .Value;
if (string.IsNullOrEmpty(surname)) { surname = "(empty surname)"; }

 

Make it easier to access User information

That’s a lot of code. Can we make it easier to access User information? Yes.

The GitHub code example repository has a source code file with a custom RequestUser class that you can add to the Controllers folder, or to the bottom of the Manager.cs source code file. Then, as its code comment states, you can add and initialize a property in the Manager class. The result will be easy-to-access information about the current security principal.

The new top-level property in the Manager class is “User”.  The code from above is now improved:

// Various values...
var userName = User.Name;
var isStudent = User.HasRoleClaim("Student");
var surname = User.Surname;

 

It gets even better when you need to need a full name (given name(s) and surname). Study the class to learn about all its properties and methods.

 

However, we must carefully configure the property’s value

Another topic that was in the October 18 notes was the “Request-processing pipeline in a secure scenario” topic. In it, you learned that the controller was initialized in step 6. In step 8, authentication happens. Finally, in step 11, the controller action is invoked/executed.

Problem? Maybe, if we attempt to set/initialize the value of the User property too early. To explain:

In step 6, the controller was initialized. Look at the controller code – it declares a Manager field (“m”), and initializes it. Therefore, when the controller is initialized, the Manager is also initialized.

Do we know the security principal at this point in time? No.

We learn later – in step 8 – what it is.

Therefore, if we attempt to set the User property value when the Manager class is initialized, the security principal will be based on the “anonymous” user. So, let’s defer the setter task. This means that we must write a standard property block, with a body for the getter code. We do not need a setter – we’ll do that task in the getter. Here’s the code:

// Backing field for the property
private RequestUser _user;

// Getter only, no setter
public RequestUser User
{
  get
  {
    // On first use, it will be null, so set its value
    if (_user == null)
    {
      _user = new RequestUser(HttpContext.Current.User as ClaimsPrincipal);
    }
    return _user;
  }
}

 

User info in a controller? Yes

You can use this new object in a controller too:

// Assume that the following code is in a controller method...
// ...and that "m" is the reference to the manager object...
var fullname = m.User.NamesFirstLast;
var isICTStudent = m.User.HasClaim("OU", "SICT");

 

The concept of resource ownership in an app

You may be familiar with this topic from your web apps programming course. If not, or if you need a refresher, continue reading…

Sometimes, an app has data that is “owned” by the app itself. In other words, if you have a user account for the app, then you can typically get access to its data.

Yes, it’s possible that you must meet authorization tests. In other words, you may still need to have a specific role claim, or a specific custom claim (e.g. OU = SICT). But if you do meet the authorization test, you have access to all the data.

 

Shared data store, but allow a user to work only with their own data

Assume that we have an app that has many entity collections, just like the code samples that we have been working with. Some entity collections are shared, and available to all authorized users. Then, maybe some entity collections have user-specific data, which is (or should be / must be) private to the user.

note-classFor example, assume that an app enables a user to add notes (or diary entries, or journal entries).

See an example “Note” class diagram to the right.

To enable privacy, simply add an “Owner” string property.

When a new Note object is created, the logic in the manager “add new” method will write the user name into the Owner property. It gets that from the security principal.

The Identity.Name property in the security principal – which is surfaced simply as the “Name” property in the new “User” property in the Manager class – is guaranteed to be unique.

 

Fetching a user’s own data from the persistent store

In many of the manager methods that fetch data, there’s often a query condition.

For example, “get one” will use the Find() or SingleOrDefault() methods to fetch the object.

To fetch a user’s own data, add one more condition. For example:

var o = ds.Notes.SingleOrDefault(n => n.Id == id && n.Owner == User.Name);

 

The result will be an object, or null. Null will bubble up through the controller and then to the user/requestor as HTTP 404 “not found”. That may be the best answer to give, because it will not leak information about the data store. (There’s a big difference between “not found” and “unauthorized” when asking for a specific object. “Not found” will likely end the fetch attempt. In contrast, “unauthorized” may invite additional attempts to bypass security measures.)

Can you ever return HTTP 401 “unauthorized”? Maybe. How can you do it, if the situation is right? From a manager method:

throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized);

 

Related objects? They need an Owner property too

In this scenario, it is possible that an object could have related or associated objects. For example, a Note object could have a collection of attachments (photos, PDFs, etc.)

When there are related objects, and they need to be protected, add an Owner property to their classes. Doing this will enable an easier query strategy. Continuing with this example, Note one-to-many with Attachment, we can fetch a note, with its associated Attachments. Because of the Owner check on the get-one Note, we can be assured that the associated Attachments will also be the Owner’s.

Alternatively, if we need to query the Attachments first, the presence of the Owner property makes it much easier to do this directly with the Attachments entity collection. If it wasn’t there, we would have to use an awkward query strategy, which would need to fetch its associated Note object to do the Owner check.

 

Strive to do all identity checks in the Manager class

For best results, and to centralize logic, you should strive to do all the identity checks in the Manager class methods. The manager object is one step away from a controller, and therefore farther away from the user/requestor. Doing this also will keep your controller code simpler.

 

Getting started with Microsoft Azure Services

From Wikipedia:

“Microsoft Azure is a cloud computing platform and infrastructure, created by Microsoft, for building, deploying and managing applications and services through a global network of Microsoft-managed data centers. It provides SaaS, PaaS and IaaS services and supports many different programming languages, tools and frameworks, including both Microsoft-specific and third-party software and systems.”

 

We will use Azure to host our web services. Every student (in the class) can get access to a 30-day trial, at no cost.

Follow this guide to get started with Azure.

 

Step 1 – Create a new Microsoft Account

Every Seneca student already has a “Microsoft Organizational Account”, which is simply the account with the “myseneca.ca” suffix.

It may also be possible that you also have a standard “Microsoft Account”, ending in “live.ca”, “hotmail.com”, or “outlook.com”.

Please DO NOT use either of those accounts when you work with Azure.

Instead, create a new Microsoft Account, on “outlook.com”. It is likely that the lifetime of this new account will be short, perhaps only for the remaining duration of the course. That’s OK, and you can consider this to be a throw-away account.

Visit this page to create a new Microsoft Account: signup.live.com

Do you need a suggestion for an account name?

Your professor suggests using an account name that you like, with a suffix “-ws2016” (which could mean “web services in 2016”).

For example, your professor would choose “peter-ws2016” (at outlook.com).

The Microsoft Account name is not publicly visible. You must use it when you login to the management portal, and when deploying an app from Visual Studio.

 

Step 2 and beyond…

We will continue this work during our Thursday computer-lab session.

 

 

 

 

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

%d bloggers like this: