Skip to content

Programmer's Guide

Vasili Vasilyeu edited this page Nov 28, 2017 · 13 revisions

It is an introduction to the general architecture of Themis codebase and describes how to extend it with new features.

Extending Themis

The section describes how to extend Themis with custom features like types, functions, algorithms and selectors.

Adding New Type

Themis provides a number of attribute types like boolean, string and so on. A type gathers information about possible attribute processing. The information is embedded in various places of PDP sourcecode. This section shows all the places which need modifications to add new type. integer type is used here as an example of new type.

Type Constants

First of all one need to add Type* constant and name of new type to attribute.go file:

// Type* constants represent all data types PDP can work with.
const (
    ...
    // TypeInteger is integer data type.
    TypeInteger
    ...
)
...
// Type* collections bind type names and IDs.
var (
    // TypeNames is list of humanreadable type names. The order must be kept
    // in sync with Type* constants order.
    TypeNames = []string{
    ...
    "Integer",
    ...
    }
)

Position of name in TypeNames array should be the same as position of Type* constant. PDP automatically fills TypeKeys array with all lowercase versions of type names and TypeIDs map with mapping of the lowercase type identifiers to Type* constants.

Values of New Type

PDP stores attributes using AttributeValue structure. The structure can be created from native golang value, native can be obtained back from the structure and the structure supports bunch of other operations which bound to related native type. To make AttributeValue aware of new integer type we need to define related native type. Lets take int64 for example. Having this binding we need:

  • to define MakeIntegerValue function which takes int64 value and creates instances of AttributeValue structure;
  • to add related case to MakeValueFromString function;
  • to add related case to Serialize method of AttributeValue structure;
  • to add related case to describe method of AttributeValue structure;
  • to add integer method to AttributeValue structure;
// MakeIntegerValue creates instance of integer attribute value.
func MakeIntegerValue(v int64) AttributeValue {
    return AttributeValue{
        t: TypeInteger,
        v: v}
}

// MakeValueFromString creates instance of attribute value by given type and
// string representation. The function performs necessary validation.
// No covertion defined for undefined type and collection types.
func MakeValueFromString(t int, s string) (AttributeValue, error) {
    switch t {
    ...
    case TypeInteger:
        n, err := strconv.ParseInt(s, 0, 64)
        if err != nil {
            return undefinedValue, newInvalidIntegerStringCastError(s, err)
        }

        return MakeIntegerValue(n), nil
    ...
    }

    return undefinedValue, newUnknownTypeStringCastError(t)
}

// Serialize converts attribute value to its string representation.
// No conversion defined for undefined value.
func (v AttributeValue) Serialize() (string, error) {
    switch v.t {
    ...
    case TypeInteger:
        return strconv.FormatInt(v.v.(int64), 10), nil
    ...
    }

    return "", newUnknownTypeSerializationError(v.t)
}

func (v AttributeValue) describe() string {
    switch v.t {
    ...
    case TypeInteger:
        return strconv.FormatInt(v.v.(int64), 10)
    ...
    }

    return "val(unknown type)"
}

func (v AttributeValue) integer() (int64, error) {
    err := v.typeCheck(TypeInteger)
    if err != nil {
        return 0, err
    }

    return v.v.(int64), nil
}

Functions new*Error are automatically generated from error descriptors in errors.yaml file. Error descriptor invalidIntegerStringCastError should be defined to get newInvalidIntegerStringCastError function in errors.go file (by go generate command).

Clone this wiki locally