Implementing an access system in your own corporate messenger: part one
Recently, we implemented an access system in the corporate messenger of the company and would like to share our experience in a short series of articles with those who have little experience in solving such problems. For convenience of presentation, we have divided the material into two parts: in this article all the components of the system will be described in detail, and we will devote a separate post to the explanation of their interaction and principles of work in the near future.

Backend for the messenger is written in Go, so the examples will be in this language. Not wanting to reinvent the wheel, we decided to take XACML as the basis - the standard for Attribute-Based Access Control (ABAC) - and simplified it as much as possible to make it suitable for our task. We want to note that we did not set ourselves the goal of writing our own implementation of XACML. It was taken as an example of a working system from which we could extract the experience we need.
There are some excellent articles for exploring XACML and ABAC:
→ Introducing XACML, the standard for Attribute-Based Access Control
→ Approaches to access control: RBAC vs. Abac
Immediately tell about the AttributeCalculater interface , as it will be found later. In fact, it consists of three other interfaces, but for brevity, we restrict ourselves to simply writing all of its methods. It must be implemented by all objects of the application model that interact with the access system.
The system uses two main types of objects: Rules and Policies. This is enough for our task, so we did not use groups of policies as unnecessary.
A rule is the simplest object in the system that is used to describe business rules. Here is its structure:
The calculate method calculates the rule, that is, returns a value that says whether the request can be executed or not, or reports an error. To do this, the context is passed to the method, which contains all the necessary information for the calculation.
The context consists of an object - an entity that implements the AttributeCalculater interface and with which the access system performs some actions, a subject - an entity that also supports the AttributeCalculater interface and is usually a user who wants to perform a certain action in an application. The target is an enum that lists all the possible actions supported by the access system. For example: add a user to correspondence, write a message, designate a correspondence administrator, etc.
The name of the rule is needed primarily for the person who works with it - it does not affect the calculation process itself.
Effect(effect) shows how the result of calculating the condition will be processed. The effect can be permit ( permitEffect ) or deny ( denyEffect ). Say, if the result of evaluating the condition is true , and the effect is set as permitEffect , then the result of evaluating the rule will also be true . If the effect is registered as denyEffect , then the result of the rule will be false .
Condition - the most important part of the rule, which, in fact, describes and calculates the condition for it.
The calculate method calculates the condition. It is similar to a similar method for a rule, only its return values are different. The condition may be true, incorrect, or report an error.
Operator is responsible for how the two operands are compared. It takes the following values:
As can be seen from the structure, a condition can be calculated only for two operands, that is, one rule does not intend to consider complex use cases that arise when working with an access system. For this, combinations of rules are used.
The data type of the operands is an Attribute (attribute). It contains information for calculating conditions.
The getValue method returns attribute value. It is returned as an object that has the AttributeCalculater interface implemented .
NameObject (name of the object), which, when calculating the attribute, takes the value from the Field field. The object itself is in the Object field .
Policies are used to describe business rules with one or more rules.
The name of the policy, like the rule, is needed only for the person who works with it.
Targer (target) is used to search for policies. For simplicity, in our system, each goal has its own policy.
Rules is a set of simple rules that complementing each other can describe complex business rules.
The CombineAlgorithm of rules indicates how to process the results of calculating policy rules. In XACML, these algorithms can be quite complex, but in our case, all these delights are not needed. Therefore, we still have only two simple algorithms permitIfAllPermitted (allow, if all allowed) and permitIfOnePermitted(allow if one allowed). For example, if the permitIfAllPermitted algorithm is installed , then the result of calculating the policy will be positive only if all the rules in this policy also have a positive result.
These are the main parts that make up our access system. In the next article, we will take a closer look at how this all works.

Backend for the messenger is written in Go, so the examples will be in this language. Not wanting to reinvent the wheel, we decided to take XACML as the basis - the standard for Attribute-Based Access Control (ABAC) - and simplified it as much as possible to make it suitable for our task. We want to note that we did not set ourselves the goal of writing our own implementation of XACML. It was taken as an example of a working system from which we could extract the experience we need.
There are some excellent articles for exploring XACML and ABAC:
→ Introducing XACML, the standard for Attribute-Based Access Control
→ Approaches to access control: RBAC vs. Abac
Base system objects and AttributeCalculater interface
Immediately tell about the AttributeCalculater interface , as it will be found later. In fact, it consists of three other interfaces, but for brevity, we restrict ourselves to simply writing all of its methods. It must be implemented by all objects of the application model that interact with the access system.
type AttributeCalculater interface {
Counter() (AttributeCalculater, error)
Equally(Getter) bool
Belong(Getter) bool
GetType() string
GetValue() (AttributeCalculater, error)
GetValueField(string) (AttributeCalculater, error)
GetInt() (int, error)
GetBool() (bool, error)
GetString() (string, error)
}The system uses two main types of objects: Rules and Policies. This is enough for our task, so we did not use groups of policies as unnecessary.
Rule
A rule is the simplest object in the system that is used to describe business rules. Here is its structure:
type rule struct {
Name string
Condition condition
Effect ruleEffect
}
func (r *rule) calculate(cntx *Context) calculateResult {}
The calculate method calculates the rule, that is, returns a value that says whether the request can be executed or not, or reports an error. To do this, the context is passed to the method, which contains all the necessary information for the calculation.
type Context struct {
Object AttributeCalculater
Subject AttributeCalculater
Target Action
}The context consists of an object - an entity that implements the AttributeCalculater interface and with which the access system performs some actions, a subject - an entity that also supports the AttributeCalculater interface and is usually a user who wants to perform a certain action in an application. The target is an enum that lists all the possible actions supported by the access system. For example: add a user to correspondence, write a message, designate a correspondence administrator, etc.
The name of the rule is needed primarily for the person who works with it - it does not affect the calculation process itself.
Effect(effect) shows how the result of calculating the condition will be processed. The effect can be permit ( permitEffect ) or deny ( denyEffect ). Say, if the result of evaluating the condition is true , and the effect is set as permitEffect , then the result of evaluating the rule will also be true . If the effect is registered as denyEffect , then the result of the rule will be false .
Condition - the most important part of the rule, which, in fact, describes and calculates the condition for it.
type condition struct {
FirstOperand Attribute
SecondOperand Attribute
Operator conditionOperator
}
func (c *condition) calculate(cntx *Context) (bool, error) {}The calculate method calculates the condition. It is similar to a similar method for a rule, only its return values are different. The condition may be true, incorrect, or report an error.
Operator is responsible for how the two operands are compared. It takes the following values:
- equally - checks if the operands are equal.
- notEqually - checks if the operands are equal.
- belong - checks if there is any relation between operands. The specific logic depends on the implementation of the AttributeCalculater interface, which objects working with the access system must implement. We will tell you more about this below.
- notBelong is the opposite of belong.
As can be seen from the structure, a condition can be calculated only for two operands, that is, one rule does not intend to consider complex use cases that arise when working with an access system. For this, combinations of rules are used.
The data type of the operands is an Attribute (attribute). It contains information for calculating conditions.
type Attribute struct {
NameObject string
Field string
Type TypeAttribute
Object AttributeCalculater
}
func (a *Attribute) getValue(c *Context) (AttributeCalculater, error) {}The getValue method returns attribute value. It is returned as an object that has the AttributeCalculater interface implemented .
NameObject (name of the object), which, when calculating the attribute, takes the value from the Field field. The object itself is in the Object field .
Politics
Policies are used to describe business rules with one or more rules.
type politic struct {
Name string
Target Action
Rules []rule
CombineAlgorithm combineAlgorithm
}
func (p *politic) calculate(cntx *Context) calculateResult {}The name of the policy, like the rule, is needed only for the person who works with it.
Targer (target) is used to search for policies. For simplicity, in our system, each goal has its own policy.
Rules is a set of simple rules that complementing each other can describe complex business rules.
The CombineAlgorithm of rules indicates how to process the results of calculating policy rules. In XACML, these algorithms can be quite complex, but in our case, all these delights are not needed. Therefore, we still have only two simple algorithms permitIfAllPermitted (allow, if all allowed) and permitIfOnePermitted(allow if one allowed). For example, if the permitIfAllPermitted algorithm is installed , then the result of calculating the policy will be positive only if all the rules in this policy also have a positive result.
These are the main parts that make up our access system. In the next article, we will take a closer look at how this all works.