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
	"fmt"
16
	"net/http"
17
	"reflect"
18
)
19

20
const (
21
	readOnlyErrorTitle     = "Read Only Error"
22
	creationOnlyErrorTitle = "Creation Only Error"
23
)
24

25
// ValidateAdvancedSpecification verifies advanced specifications attributes like ReadOnly and CreationOnly.
26
//
27
// For instance, it will check if the given Manipulable has field marked as
28
// readonly, that it has not changed according to the db.
29
func ValidateAdvancedSpecification(obj AttributeSpecifiable, pristine AttributeSpecifiable, op Operation) error {
30

31 14
	errors := NewErrors()
32

33
	for _, field := range extractFieldNames(obj) {
34

35 14
		spec := obj.SpecificationForAttribute(field)
36

37
		// If the field is not exposed, we don't enforce anything.
38 14
		if !spec.Exposed || spec.Transient {
39 14
			continue
40
		}
41

42 14
		switch op {
43 14
		case OperationCreate:
44 14
			if spec.ReadOnly && !isFieldValueZero(field, obj) && !areFieldsValueEqualValue(field, obj, spec.DefaultValue) {
45

46
				// Special case here. If we have a pristine object, and the fields are equal, it is fine.
47 14
				if pristine != nil && areFieldValuesEqual(field, obj, pristine) {
48 14
					continue
49
				}
50

51 14
				e := NewError(
52 14
					readOnlyErrorTitle,
53 14
					fmt.Sprintf("Field %s is read only. You cannot set its value.", spec.Name),
54 14
					"elemental",
55 14
					http.StatusUnprocessableEntity,
56 14
				)
57 14
				e.Data = map[string]string{"attribute": spec.Name}
58 14
				errors = append(errors, e)
59
			}
60

61 14
		case OperationUpdate:
62 14
			if !spec.CreationOnly && spec.ReadOnly && !areFieldValuesEqual(field, obj, pristine) {
63 14
				e := NewError(
64 14
					readOnlyErrorTitle,
65 14
					fmt.Sprintf("Field %s is read only. You cannot modify its value.", spec.Name),
66 14
					"elemental",
67 14
					http.StatusUnprocessableEntity,
68 14
				)
69 14
				e.Data = map[string]string{"attribute": spec.Name}
70 14
				errors = append(errors, e)
71
			}
72

73 14
			if spec.CreationOnly && !areFieldValuesEqual(field, obj, pristine) {
74 14
				e := NewError(
75 14
					creationOnlyErrorTitle,
76 14
					fmt.Sprintf("Field %s can only be set during creation. You cannot modify its value.", spec.Name),
77 14
					"elemental",
78 14
					http.StatusUnprocessableEntity,
79 14
				)
80 14
				e.Data = map[string]string{"attribute": spec.Name}
81 14
				errors = append(errors, e)
82
			}
83
		}
84
	}
85

86 14
	if len(errors) > 0 {
87 14
		return errors
88
	}
89

90 14
	return nil
91
}
92

93
// BackportUnexposedFields copy the values of unexposed fields from src to dest.
94
func BackportUnexposedFields(src, dest AttributeSpecifiable) {
95

96
	for _, field := range extractFieldNames(src) {
97

98 14
		spec := src.SpecificationForAttribute(field)
99

100 14
		if !spec.Exposed {
101 14
			reflect.Indirect(reflect.ValueOf(dest)).FieldByName(field).Set(reflect.Indirect(reflect.ValueOf(src)).FieldByName(field))
102
		}
103

104 14
		if spec.Secret && isFieldValueZero(field, dest) {
105 14
			reflect.Indirect(reflect.ValueOf(dest)).FieldByName(field).Set(reflect.Indirect(reflect.ValueOf(src)).FieldByName(field))
106
		}
107
	}
108
}
109

110
// ResetDefaultForZeroValues reset the default value from the specification when a field is Zero.
111
// If the given object is not an elemental.AttributeSpecifiable this function
112
// does nothing.
113
func ResetDefaultForZeroValues(obj interface{}) {
114

115 14
	o, ok := obj.(AttributeSpecifiable)
116 14
	if !ok {
117 0
		return
118
	}
119

120
	for _, field := range extractFieldNames(o) {
121

122 14
		spec := o.SpecificationForAttribute(field)
123

124 14
		if spec.DefaultValue == nil || !isFieldValueZero(field, o) {
125 14
			continue
126
		}
127

128 14
		reflect.Indirect(reflect.ValueOf(o)).FieldByName(field).Set(reflect.ValueOf(spec.DefaultValue))
129
	}
130
}
131

132
// ResetMaps recursively empty all kinds of maps in the given
133
// reflect.Value.
134
func ResetMaps(v reflect.Value) {
135

136 14
	indirect := func(vv reflect.Value) reflect.Value {
137
		for ; vv.Kind() == reflect.Ptr; vv = vv.Elem() {
138
		}
139 14
		return vv
140
	}
141

142 14
	v = indirect(v)
143

144 14
	if !v.IsValid() {
145 14
		return
146
	}
147

148 14
	reset := func(f reflect.Value) {
149

150 14
		switch f.Kind() {
151 14
		case reflect.Map:
152

153 14
			if f.IsNil() {
154 14
				return
155
			}
156

157
			for _, k := range f.MapKeys() {
158 14
				f.SetMapIndex(k, reflect.Value{})
159
			}
160

161 14
		case reflect.Struct, reflect.Slice:
162 14
			ResetMaps(f)
163
		}
164
	}
165

166 14
	switch v.Kind() {
167

168 14
	case reflect.Map:
169 14
		reset(v)
170

171 14
	case reflect.Slice:
172
		for i := 0; i < v.Len(); i++ {
173 14
			reset(indirect(v.Index(i)))
174
		}
175

176 14
	case reflect.Struct:
177
		for i := 0; i < v.NumField(); i++ {
178 14
			reset(indirect(v.Field(i)))
179
		}
180
	}
181
}

Read our documentation on viewing source code .

Loading