API authorization through middlewares

When dealing with API authorization based on access tokens, permissions (user can see a list, can create an item, can delete one etc) and/or account types (administrator, moderator, normal user etc), I’ve seen the approach of checking requirements inside the HTTP handlers functions.

This post and attached source code do not handle security, API design, data storage patterns or any other best practices that do not aim directly at the main subject: Authorization through middlewares. All other code is just for illustrating the idea as a whole.

A classical way of dealing various authorization checks is to verify everything inside the handler function.

type User struct {
   Username    string
   Type        string
   Permissions uint8
}

var CanDoAction uint8 = 1

func tokenIsValid(token string) bool {
   // ...
   return true
}

func getUserHandler(c echo.Context) error {
   // Check authorization token
   token := c.Get("token").(string)
   if !tokenIsValid(token) {
      return c.NoContent(http.StatusUnauthorized)
   }

   user := c.Get("user").(User)

   // Check account type
   if user.Type != "admin" {
      return c.NoContent(http.StatusForbidden)
   }

   // Check permission for handler
   if user.Permissions&CanDoAction == 0 {
      return c.NoContent(http.StatusForbidden)
   }

   // Get data and send it as response
   data := struct {
      Username string `json:"username"`
   }{
      Username: user.Username,
   }

   return c.JSON(http.StatusOK, data)
}

The handler is doing more than its purpose. Continue reading API authorization through middlewares

Configurable implementation hidden behind a contract

Some concrete implementations are better to be hidden behind contracts, in a specific package. A layer of abstraction can save you later, mostly on unstable projects that often change requirements, or maybe you just want to test an idea, maybe you don’t have the necessary time to finish a task in a way you’d like to.

A good contract will save you. You can “throw” your implementation behind it and come back later to refine it. Sometimes later means in a few years. But if you’re behind a well designed contract, most probably you’re going to alter only the concrete implementation, without touching large areas of the projects.

I had to filter some user input. Some strings had to be HTML escaped, some sanitized to prevent different attacks. I’ve wrapped everything into a package, behind a contract. For escaping I’ve used Go’s html.EscapeString function, while for sanitizing I’ve found the bluemonday package, which is inspired by an OWASP sanitizier for Java. Continue reading Configurable implementation hidden behind a contract

Re: Isolation

It’s easy to talk about SOLID principles, design patterns, testing, moral expectations, self responsibility, and any other guide lines for a better world. Sometimes it’s even easy to adhere to those ideas, to put them in practice, no matter how may times some people repeat “there wasn’t/isn’t time”.

Although, there are situations that really stand against all best practices, like big legacy products which are already on the market, used by many clients, developed by many people who are managed very strict, in order to maintain… order. Or you need to get fast on the market.

The minimum that can be done is isolation. The least you can do is to throw your code in its own corner and use it only from there, then go back to change it whenever you want, without fear, with minimum cost and risk.

Isolation

Big fan of isolation here, from code libraries to apps environments.

Code should be organized in reusable and extensible units (package, library, component), isolated from other units, the interaction between them being made on APIs well described by a contract. Also, they should be easily extensible.

Apps should live in isolated environments. I’m talking about servers, virtual machines, and containers. If different services run on the same machine, you can isolate them using containers. As such, you can safely and independently deploy, make upgrades, balance traffic, move over different machines.
If you must work directly in the container (maybe perform some upgrade), and something goes wrong, you just restart the container from the original image.

I remember having some issues upgrading a Python package in an old production environment. The attempted upgrades just crashed. I knew the service could stay offline for a while, but I had to put it back up. As other services were running on that machine, I didn’t want to interfere with them. So I just set up a new environment for the Python app inside a Docker container, installed it from scratch, and it was up again.

Isolation also suites well legacy apps. You can just throw everything inside a container, and never be afraid of moving between machines, interfering with other services, or breaking things.