• No results found

Robustness Requirements

In document Practical Microsoft Visual Studio 2015 (Page 103-106)

We ideally would prefer to design and implement highly-reliable systems. But as we create highly-

composable and highly-scalable systems, we introduce different risks for failure. Message-oriented systems, for example, introduce a dependency on a message channel (typically a message queue or a message broker) that can independently fail for reasons out of the control of the rest of the system. Highly-robust systems are systems that remain functional despite the presence of errors or failures.

Let's look at some of the practices involved in making a system robust.

Validation

Any given system has certain operating parameters; these are the scenarios in which the system is

guaranteed to work, including what is and isn't valid input. One easy way to make a system robust is to verify that only data that is known to be valid is processed. All other data can be rejected to avoid error and failure.

Retry

With highly composed and distributed systems, components within the system communicate among other components by any number of channels. A channel is a means by which information can be transferred between two components and usually performed by third-party software or hardware. That could be another process (like a RESTful interface or a message queue) or could be another piece of equipment that’s in a temporary failure state. Maybe a message queue is full or a network component is temporarily unavailable.

This can be compensated for by retrying operations. If a connection to a RESTful interface fails, for example, it can be retried in a couple of seconds with the assumption the problem is temporary and that it may eventually work. It's important to know what operations can be retried and what operations will never magically work no matter how often they are retried. With RESTful interfaces, for example, an HTTP error 503 (service unavailable) is generally temporary (and sometimes the reason and timeframe is included in the response) and retrying the request could work. Error 400, on the other hand, means the request is bad in some way and no matter how many times it is tried, it will still be wrong. There's no point in retrying operations that will never work.

Despite some operations potentially working after retrying, the application may not be able to retry forever or even after a certain amount of time. In cases like that, only a certain number of retries should be performed. Under certain circumstances the circuit breaker pattern could be used to avoid the operation in the future until it is deemed to be working again.

Traceability

In many systems, especially distribute systems, the ability to trace an action or the impetus of an action throughout the system is very important.

For example, a client application may ask a system that is proxied through one component to reach another that makes requests of several other components. It may be necessary to trace back from the action of one component to the other components involved upstream in the interaction.

This is most commonly required for detecting the root cause of failure in distributed systems. But this could also be for auditability and security. The action performed in component C may need to be traced back to the actions performed within component A—potentially a front-end interface and the person who performed the action.

93

Security

Security is a hot topic and an area of much scrutiny and research. Security is an area that has many patterns, too many patterns to discuss in a book focusing on Visual Studio. We can, however, talk about some of the areas that relate to Visual Studio application users.

There are lots of practices and patterns that anyone writing software in Visual Studio could use; let's look at some common areas related to security.

Authentication

If you're developing a Web API, web service, WCF service, or RESTful API, authentication will come up fairly early. If your API is pay-per-use or client-specific, you will quickly need to address authentication. Let's look at some of the typical practices involving authentication.

Let's focus mainly on ASP.NET. If you're reading a book on Visual Studio, you're more than likely using ASP.NET for web-based APIs. There are two types of authentication on the web. The first is intranet authentication.

Intranet authentication is when an application is logged in to a Windows computer that is connected to a private network. When the application connects to an intranet application on the local network, it can pass along a security token to the server that represents that you've been authenticated and who you are. This token can easily be populated by the framework you're using to connect to the API. For example, in .NET: WebClient c = new WebClient

{

UseDefaultCredentials = true };

var text = c.DownloadStringAsync(new Uri("http://server/customers"));

This token is typically validated before your web app gets the user information, but the existence of this user information signifies that a user has been authenticated.

The other type of authentication is Internet authentication. This can have many flavors. The simplest when talking about an API is a client ID and a client secret. Typically, an administration portal generates the client ID and client secret for a particular application that wants to access the API. The ID and secret are then passed to the API via request headers. Passing them via the request headers ensures that the data is encrypted when using an SSL or TLS channel, unlike within the URL. For example:

using (var client = new HttpClient())

using (var request = new HttpRequestMessage(HttpMethod.Get, uri)) {

request.Headers.Add("client_id", "my_client_id");

request.Headers.Add("client_secret", "my_client_secrety"); using (var response = await client.SendAsync(request)) {

Console.WriteLine(await response.Content.ReadAsStringAsync()); }

94

Those aren't your only options, they are likely just the most popular or minimal implementations. Depending on your system, you may need much more involved security. Using a client ID and secret can be a bit insecure. Applications don't have to store the secret in the clear, but they can. A more secure route is to send a client certificate that is retrieved from a local certificate store (that presumably can be denied access or cleared). For example:

using (WebRequestHandler handler = new WebRequestHandler()) {

var certificate = GetCertificate();

handler.ClientCertificates.Add(certificate); using (var client = new HttpClient(handler)) {

var response = await client.GetAsync("https://example.com/customers"); // TODO: use response, e.g.

var responseText = await response.Content.ReadAsStringAsync(); }

}

On the server side when detecting the authentication, this could take one of several forms. For the Internet authentication, it could be as simple as checking the request headers. For example:

var hasClient_id = this.Request.Headers.Keys.Cast<string>().Any(e => "my_client_id". Equals(e));

var hasClient_secret = this.Request.Headers.Keys.Cast<string>().Any(e => "my_client_secret". Equals(e));

Truth be told, you'd rarely do this in your server code; you'd likely be using an API gateway of some sort that does the authentication for you.

For the other methods, you're probably just looking for blanket authorization, whereby you authorize an API to be used by any authenticated user.

Authorization

Once authenticated, the identity of someone can be used to decide their authorization to use certain functionality. Typically, this is done by assigning roles. The identity would contain what roles the identity holds and thus what functionality was available to them.

In terms of generic .NET code, this can be done as follows: var p = (WindowsPrincipal)Thread.CurrentPrincipal;

if(!p.IsInRole("CRM Users")) throw new UnauthorizedAccessException();

If you're writing ASP.NET MVC applications or APIs, this is slightly different and simpler. You can simply authorize roles to use specific controllers. For example:

[Authorize(Roles="CRM Users")]

public class ValuesController : ApiController {

//… }

95

In document Practical Microsoft Visual Studio 2015 (Page 103-106)