Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
326 lines (252 sloc) 8.81 KB
// Copyright 2019 Aporeto Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package elemental
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"reflect"
)
// An AttributeSpecifiable is the interface an object must implement in order to access specification of its attributes.
type AttributeSpecifiable interface {
// SpecificationForAttribute returns the AttributeSpecification for
// given attribute name
SpecificationForAttribute(string) AttributeSpecification
// AttributeSpecifications returns all the AttributeSpecification mapped by
// attribute name
AttributeSpecifications() map[string]AttributeSpecification
// ValueForAttribute returns the value for the given attribute
ValueForAttribute(name string) interface{}
}
// AttributeEncrypter is the interface that must be
// implement to manage encrypted attributes.
type AttributeEncrypter interface {
// EncryptString encrypts the given string and returns the encrypted version.
EncryptString(string) (string, error)
// DecryptString decrypts the given string and returns the encrypted version.
DecryptString(string) (string, error)
}
// An AttributeSpecification represents all the metadata of an attribute.
//
// This information is coming from the Monolithe Specifications.
type AttributeSpecification struct {
// AllowedChars is a regexp that will be used to validate
// what value a string attribute can take.
//
// This is enforced by elemental.
AllowedChars string
// AllowedChoices is a list of possible values for an attribute.
//
// This is enforced by elemental.
AllowedChoices []string
// Autogenerated defines if the attribute is autogenerated by the server.
// It can be used in conjunction with ReadOnly.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
Autogenerated bool
// Availability is reserved for later use.
Availability string
// ConvertedName contains the name after local conversion.
ConvertedName string
// Channel is reserved for later use.
Channel string
// CreationOnly defines if the attribute can be set only during creation.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
CreationOnly bool
// DefaultOrder defines if the attribute is used as the default ordering key.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
DefaultOrder bool
// DefaultValue holds the default value declared in specification.
DefaultValue interface{}
// Deprecated defines if the attribute is deprecated.
Deprecated bool
// Description contains the description of the attribute.
Description string
// Exposed defines if the attribute is exposed through the north bound API.
Exposed bool
// Filterable defines if it is possible to filter based on this attribute.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
Filterable bool
// ForeignKey defines if the attribute is a foreign key.
ForeignKey bool
// Getter defines if the attribute needs to define a getter method.
// This is useful if you can to define an Interface based on this attribute.
Getter bool
// Identifier defines if the attribute is used the access key from the
// northbound API.
Identifier bool
// Index defines if the attribute is indexed or not.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
Index bool
// MaxLength defines what is the maximun length of the attribute.
// This only makes sense if the type is a string.
//
// This is enforced by elemental.
MaxLength uint
// MaxValue defines what is the maximun value of the attribute.
// This only makes sense if the type has a numeric type.
//
// This is enforced by elemental.
MaxValue float64
// MinLength defines what is the minimum length of the attribute.
// This only makes sense if the type is a string.
//
// This is enforced by elemental.
MinLength uint
// MinValue defines what is the minimum value of the attribute.
// This only makes sense if the type has a numeric type.
//
// This is enforced by elemental.
MinValue float64
// Name defines what is the name of the attribute.
// This will be the raw Monolithe Specification name, without
// Go translation.
Name string
// Orderable defines if it is possible to order based on the value of this attribute.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
Orderable bool
// PrimaryKey defines if the attribute is used as a primary key.
PrimaryKey bool
// ReadOnly defines if the attribute is read only.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
ReadOnly bool
// Required defines is the attribute must be set or not.
//
// This is enforced by elemental.
Required bool
// Secret defines if the attribute is secret.
// This is useful if you can to define perform sanity check on this field to be sure it
// is not sent for instance.
Secret bool
// Setter defines if the attribute needs to define a setter method.
// This is useful if you can to define an Interface based on this attribute.
Setter bool
// Stored defines if the attribute will be stored in the northbound API.
//
// If this is true, the backend tags will be generated by Monolithe.
Stored bool
// SubType defines the Monolithe Subtype.
SubType string
// Transient defines if the attributes is transient or not.
//
// This is not enforced by elemental. You must write your own business logic to honor this.
Transient bool
// Type defines the raw Monolithe type.
Type string
// Encrypted defines if the attribute needs encryption.
Encrypted bool
}
// ResetSecretAttributesValues will reset any attributes marked
// as `secret` in the given obj if it is an elemental.Identifiable
// or an elemental.Identifiables.
// The given Identifiables must implement the elemental.AttributeSpecifiable
// interface or this function will have no effect.
//
// If you pass anything else, this function does nothing.
func ResetSecretAttributesValues(obj interface{}) {
strip := func(o Identifiable) {
oo := o
if sp, ok := o.(SparseIdentifiable); ok {
oo = sp.ToPlain()
}
if attrspec, ok := oo.(AttributeSpecifiable); ok {
var rv, val reflect.Value
for _, aspec := range attrspec.AttributeSpecifications() {
if !aspec.Secret {
continue
}
rv = reflect.Indirect(reflect.ValueOf(o))
val = rv.FieldByName(aspec.ConvertedName)
val.Set(reflect.Zero(val.Type()))
}
}
}
switch o := obj.(type) {
case Identifiable:
strip(o)
case Identifiables:
for _, i := range o.List() {
strip(i)
}
}
}
// aesAttributeEncrypter is an elemental.AttributeEncrypter
// using AES encryption.
type aesAttributeEncrypter struct {
passphrase []byte
}
// NewAESAttributeEncrypter returns a new elemental.AttributeEncrypter
// implementing AES encryption.
func NewAESAttributeEncrypter(passphrase string) (AttributeEncrypter, error) {
passbytes := []byte(passphrase)
if len(passbytes) != aes.BlockSize {
return nil, fmt.Errorf("invalid passphrase: size must be exactly %d bytes", aes.BlockSize)
}
return &aesAttributeEncrypter{
passphrase: passbytes,
}, nil
}
// EncryptString encrypts the given string.
func (e *aesAttributeEncrypter) EncryptString(value string) (string, error) {
if value == "" {
return "", nil
}
data := []byte(value)
c, err := aes.NewCipher(e.passphrase)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, data, nil)), nil
}
// DecryptString decrypts the given string.
func (e *aesAttributeEncrypter) DecryptString(value string) (string, error) {
if value == "" {
return "", nil
}
data, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return "", err
}
c, err := aes.NewCipher(e.passphrase)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return "", err
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return "", fmt.Errorf("data is too small")
}
out, err := gcm.Open(nil, data[:nonceSize], data[nonceSize:], nil)
if err != nil {
return "", err
}
return string(out), nil
}
You can’t perform that action at this time.