Developing APIs in Go is very common but so far there is no easy way to choose what to return accordingly to the user that did the request and to the action. To solve that, I created a library that allow developers to easily set permissions of the fields of a struct with Go tags.
The system uses the pex tag in each field to determine if a user has or not permission for that action in that field.
It is considered that a certain user type has permission if the permissions tag is not defined or if explicitly written in the tag that for a certain action, it has permission. Invalid actions or user types that doesn't exist in the tag are considered as not having permission.
The permission tag is a set of pairs between user type and permission like pex:"user:r,admin:rw"
.
In this case user would have permission to read while admin would have permission to read and write.
Imagine you have this two structs
type Person struct {
ID int `pex:"user:r,admin:rw"`
Name string `pex:"user:r,admin:rw" json:"full_name"`
}
type Employee struct {
Parent
Income float32 `pex:"user:,admin:rw"`
}
And you queried the database to get the employees. Now suppose you want to return (ActionRead) the result to a regular user (userType = "user"). Of course you don't want to show the income of the employees to a regular user, you have to remove it. For that you leave the permission of user empty in the income field as you can see above. Then you just have to call extract fields function.
fields := ExtractFields(employee, userType, ActionRead)
This will return an interface that contains all the fields in the struct that the user has permission. The key in the result is the JSON key if the JSON tag exists otherwise its the field name.
{
"ID": 1,
"full_name": "John Doe"
}
If the userType = admin the result has the income included
{
"ID": 1,
"full_name": "John Doe",
"Income": 1000.0
}
This can be applied to slices, pointers, maps and any kind of variables.
[
{
"ID": 1,
"full_name": "John Doe",
"Income": 1000.0
},
{
"ID": 2,
"full_name": "Jack Sparrow",
"Income": 9999.99
}
]
It is also possible to take advantage of the fields extraction to clean a struct, that is to set fields that user does not have permission to their zero values and the others to the result of the field extraction.
var cleanedObject *Employee
cleanedObject := CleanObject(employee, userType, ActionRead).(*Employee)
ActionRead
: 0
ActionWrite
: 1
PermissionNone
: Empty string
PermissionRead
: "r"
PermissionWrite
: "w"
PermissionReadWrite
: "rw"