1
// Copyright 2019 Aporeto Inc.
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at
5
//     http://www.apache.org/licenses/LICENSE-2.0
6
// Unless required by applicable law or agreed to in writing, software
7
// distributed under the License is distributed on an "AS IS" BASIS,
8
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
// See the License for the specific language governing permissions and
10
// limitations under the License.
11

12
package elemental
13

14
import (
15
	"crypto/aes"
16
	"crypto/cipher"
17
	"crypto/rand"
18
	"encoding/base64"
19
	"fmt"
20
	"io"
21
	"reflect"
22
)
23

24
// An AttributeSpecifiable is the interface an object must implement in order to access specification of its attributes.
25
type AttributeSpecifiable interface {
26

27
	// SpecificationForAttribute returns the AttributeSpecification for
28
	// given attribute name
29
	SpecificationForAttribute(string) AttributeSpecification
30

31
	// AttributeSpecifications returns all the AttributeSpecification mapped by
32
	// attribute name
33
	AttributeSpecifications() map[string]AttributeSpecification
34

35
	// ValueForAttribute returns the value for the given attribute
36
	ValueForAttribute(name string) interface{}
37
}
38

39
// AttributeEncrypter is the interface that must be
40
// implement to manage encrypted attributes.
41
type AttributeEncrypter interface {
42

43
	// EncryptString encrypts the given string and returns the encrypted version.
44
	EncryptString(string) (string, error)
45

46
	// DecryptString decrypts the given string and returns the encrypted version.
47
	DecryptString(string) (string, error)
48
}
49

50
// An AttributeSpecification represents all the metadata of an attribute.
51
//
52
// This information is coming from the Monolithe Specifications.
53
type AttributeSpecification struct {
54

55
	// AllowedChars is a regexp that will be used to validate
56
	// what value a string attribute can take.
57
	//
58
	// This is enforced by elemental.
59
	AllowedChars string
60

61
	// AllowedChoices is a list of possible values for an attribute.
62
	//
63
	// This is enforced by elemental.
64
	AllowedChoices []string
65

66
	// Autogenerated defines if the attribute is autogenerated by the server.
67
	// It can be used in conjunction with ReadOnly.
68
	//
69
	// This is not enforced by elemental. You must write your own business logic to honor this.
70
	Autogenerated bool
71

72
	// Availability is reserved for later use.
73
	Availability string
74

75
	// BSONFieldName is the name of the field that will be used when encoding/decoding the field into Binary JSON format.
76
	BSONFieldName string
77

78
	// ConvertedName contains the name after local conversion.
79
	ConvertedName string
80

81
	// Channel is reserved for later use.
82
	Channel string
83

84
	// CreationOnly defines if the attribute can be set only during creation.
85
	//
86
	// This is not enforced by elemental. You must write your own business logic to honor this.
87
	CreationOnly bool
88

89
	// DefaultValue holds the default value declared in specification.
90
	DefaultValue interface{}
91

92
	// Deprecated defines if the attribute is deprecated.
93
	Deprecated bool
94

95
	// Description contains the description of the attribute.
96
	Description string
97

98
	// Exposed defines if the attribute is exposed through the north bound API.
99
	Exposed bool
100

101
	// Filterable defines if it is possible to filter based on this attribute.
102
	//
103
	// This is not enforced by elemental. You must write your own business logic to honor this.
104
	Filterable bool
105

106
	// ForeignKey defines if the attribute is a foreign key.
107
	ForeignKey bool
108

109
	// Getter defines if the attribute needs to define a getter method.
110
	// This is useful if you can to define an Interface based on this attribute.
111
	Getter bool
112

113
	// Identifier defines if the attribute is used the access key from the
114
	// northbound API.
115
	Identifier bool
116

117
	// Index defines if the attribute is indexed or not.
118
	//
119
	// This is not enforced by elemental. You must write your own business logic to honor this.
120
	Index bool
121

122
	// MaxLength defines what is the maximun length of the attribute.
123
	// This only makes sense if the type is a string.
124
	//
125
	// This is enforced by elemental.
126
	MaxLength uint
127

128
	// MaxValue defines what is the maximun value of the attribute.
129
	// This only makes sense if the type has a numeric type.
130
	//
131
	// This is enforced by elemental.
132
	MaxValue float64
133

134
	// MinLength defines what is the minimum length of the attribute.
135
	// This only makes sense if the type is a string.
136
	//
137
	// This is enforced by elemental.
138
	MinLength uint
139

140
	// MinValue defines what is the minimum value of the attribute.
141
	// This only makes sense if the type has a numeric type.
142
	//
143
	// This is enforced by elemental.
144
	MinValue float64
145

146
	// Name defines what is the name of the attribute.
147
	// This will be the raw Monolithe Specification name, without
148
	// Go translation.
149
	Name string
150

151
	// Orderable defines if it is possible to order based on the value of this attribute.
152
	//
153
	// This is not enforced by elemental. You must write your own business logic to honor this.
154
	Orderable bool
155

156
	// PrimaryKey defines if the attribute is used as a primary key.
157
	PrimaryKey bool
158

159
	// ReadOnly defines if the attribute is read only.
160
	//
161
	// This is not enforced by elemental. You must write your own business logic to honor this.
162
	ReadOnly bool
163

164
	// Required defines is the attribute must be set or not.
165
	//
166
	// This is enforced by elemental.
167
	Required bool
168

169
	// Secret defines if the attribute is secret.
170
	// This is useful if you can to define perform sanity check on this field to be sure it
171
	// is not sent for instance.
172
	Secret bool
173

174
	// Setter defines if the attribute needs to define a setter method.
175
	// This is useful if you can to define an Interface based on this attribute.
176
	Setter bool
177

178
	// Signed indicates if the attribute's value should be used when
179
	// generating a signature for the object.
180
	Signed bool
181

182
	// Stored defines if the attribute will be stored in the northbound API.
183
	//
184
	// If this is true, the backend tags will be generated by Monolithe.
185
	Stored bool
186

187
	// SubType defines the Monolithe Subtype.
188
	SubType string
189

190
	// Transient defines if the attributes is transient or not.
191
	//
192
	// This is not enforced by elemental. You must write your own business logic to honor this.
193
	Transient bool
194

195
	// Type defines the raw Monolithe type.
196
	Type string
197

198
	// Encrypted defines if the attribute needs encryption.
199
	Encrypted bool
200
}
201

202
// ResetSecretAttributesValues will reset any attributes marked
203
// as `secret` in the given obj if it is an elemental.Identifiable
204
// or an elemental.Identifiables.
205
// The given Identifiables must implement the elemental.AttributeSpecifiable
206
// interface or this function will have no effect.
207
//
208
// If you pass anything else, this function does nothing.
209
func ResetSecretAttributesValues(obj interface{}) {
210

211 14
	strip := func(o Identifiable) {
212

213 14
		oo := o
214 14
		if sp, ok := o.(SparseIdentifiable); ok {
215 14
			oo = sp.ToPlain()
216
		}
217

218 14
		if attrspec, ok := oo.(AttributeSpecifiable); ok {
219

220 14
			var rv, val reflect.Value
221

222
			for _, aspec := range attrspec.AttributeSpecifications() {
223

224 14
				if !aspec.Secret {
225 14
					continue
226
				}
227

228 14
				rv = reflect.Indirect(reflect.ValueOf(o))
229 14
				val = rv.FieldByName(aspec.ConvertedName)
230 14
				val.Set(reflect.Zero(val.Type()))
231
			}
232
		}
233
	}
234

235 14
	switch o := obj.(type) {
236

237 14
	case Identifiable:
238 14
		strip(o)
239

240 14
	case Identifiables:
241
		for _, i := range o.List() {
242 14
			strip(i)
243
		}
244
	}
245
}
246

247
// aesAttributeEncrypter is an elemental.AttributeEncrypter
248
// using AES encryption.
249
type aesAttributeEncrypter struct {
250
	passphrase []byte
251
}
252

253
// NewAESAttributeEncrypter returns a new elemental.AttributeEncrypter
254
// implementing AES encryption.
255
func NewAESAttributeEncrypter(passphrase string) (AttributeEncrypter, error) {
256

257 14
	passbytes := []byte(passphrase)
258 14
	if len(passbytes) != aes.BlockSize {
259 14
		return nil, fmt.Errorf("invalid passphrase: size must be exactly %d bytes", aes.BlockSize)
260
	}
261

262 14
	return &aesAttributeEncrypter{
263 14
		passphrase: passbytes,
264 14
	}, nil
265
}
266

267
// EncryptString encrypts the given string.
268
func (e *aesAttributeEncrypter) EncryptString(value string) (string, error) {
269

270 14
	if value == "" {
271 14
		return "", nil
272
	}
273

274 14
	data := []byte(value)
275

276 14
	c, err := aes.NewCipher(e.passphrase)
277 14
	if err != nil {
278 0
		return "", err
279
	}
280

281 14
	gcm, err := cipher.NewGCM(c)
282 14
	if err != nil {
283 0
		return "", err
284
	}
285

286 14
	nonce := make([]byte, gcm.NonceSize())
287 14
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
288 0
		return "", err
289
	}
290

291 14
	return base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, data, nil)), nil
292
}
293

294
// DecryptString decrypts the given string.
295
func (e *aesAttributeEncrypter) DecryptString(value string) (string, error) {
296

297 14
	if value == "" {
298 14
		return "", nil
299
	}
300

301 14
	data, err := base64.StdEncoding.DecodeString(value)
302 14
	if err != nil {
303 14
		return "", err
304
	}
305

306 14
	c, err := aes.NewCipher(e.passphrase)
307 14
	if err != nil {
308 0
		return "", err
309
	}
310

311 14
	gcm, err := cipher.NewGCM(c)
312 14
	if err != nil {
313 0
		return "", err
314
	}
315

316 14
	nonceSize := gcm.NonceSize()
317 14
	if len(data) < nonceSize {
318 14
		return "", fmt.Errorf("data is too small")
319
	}
320

321 14
	out, err := gcm.Open(nil, data[:nonceSize], data[nonceSize:], nil)
322 14
	if err != nil {
323 0
		return "", err
324
	}
325

326 14
	return string(out), nil
327
}

Read our documentation on viewing source code .

Loading