1// Copyright 2018 Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cel
16
17import (
18	"errors"
19	"fmt"
20	"reflect"
21	"time"
22
23	"github.com/golang/protobuf/ptypes"
24	dpb "github.com/golang/protobuf/ptypes/duration"
25	tpb "github.com/golang/protobuf/ptypes/timestamp"
26	"github.com/google/cel-go/checker/decls"
27	"github.com/google/cel-go/common/types"
28	"github.com/google/cel-go/common/types/ref"
29	"github.com/google/cel-go/common/types/traits"
30	exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
31
32	"istio.io/api/policy/v1beta1"
33	"istio.io/istio/mixer/pkg/lang"
34	"istio.io/pkg/attribute"
35)
36
37func convertType(typ v1beta1.ValueType) *exprpb.Type {
38	switch typ {
39	case v1beta1.STRING:
40		return decls.String
41	case v1beta1.INT64:
42		return decls.Int
43	case v1beta1.DOUBLE:
44		return decls.Double
45	case v1beta1.BOOL:
46		return decls.Bool
47	case v1beta1.TIMESTAMP:
48		return decls.Timestamp
49	case v1beta1.DURATION:
50		return decls.Duration
51	case v1beta1.STRING_MAP:
52		return stringMapType
53	case v1beta1.IP_ADDRESS:
54		return decls.NewObjectType(ipAddressType)
55	case v1beta1.EMAIL_ADDRESS:
56		return decls.NewObjectType(emailAddressType)
57	case v1beta1.URI:
58		return decls.NewObjectType(uriType)
59	case v1beta1.DNS_NAME:
60		return decls.NewObjectType(dnsType)
61	}
62	return &exprpb.Type{TypeKind: &exprpb.Type_Dyn{}}
63}
64
65func recoverType(typ *exprpb.Type) v1beta1.ValueType {
66	if typ == nil {
67		return v1beta1.VALUE_TYPE_UNSPECIFIED
68	}
69	switch t := typ.TypeKind.(type) {
70	case *exprpb.Type_Primitive:
71		switch t.Primitive {
72		case exprpb.Type_STRING:
73			return v1beta1.STRING
74		case exprpb.Type_INT64:
75			return v1beta1.INT64
76		case exprpb.Type_DOUBLE:
77			return v1beta1.DOUBLE
78		case exprpb.Type_BOOL:
79			return v1beta1.BOOL
80		}
81
82	case *exprpb.Type_WellKnown:
83		switch t.WellKnown {
84		case exprpb.Type_TIMESTAMP:
85			return v1beta1.TIMESTAMP
86		case exprpb.Type_DURATION:
87			return v1beta1.DURATION
88		}
89
90	case *exprpb.Type_MessageType:
91		switch t.MessageType {
92		case ipAddressType:
93			return v1beta1.IP_ADDRESS
94		case emailAddressType:
95			return v1beta1.EMAIL_ADDRESS
96		case uriType:
97			return v1beta1.URI
98		case dnsType:
99			return v1beta1.DNS_NAME
100		}
101
102	case *exprpb.Type_MapType_:
103		if reflect.DeepEqual(t.MapType.KeyType, decls.String) &&
104			reflect.DeepEqual(t.MapType.ValueType, decls.String) {
105			return v1beta1.STRING_MAP
106		}
107
108		// remaining maps are not yet supported
109	}
110	return v1beta1.VALUE_TYPE_UNSPECIFIED
111}
112
113func convertValue(typ v1beta1.ValueType, value interface{}) ref.Val {
114	switch typ {
115	case v1beta1.STRING, v1beta1.INT64, v1beta1.DOUBLE, v1beta1.BOOL:
116		return types.DefaultTypeAdapter.NativeToValue(value)
117	case v1beta1.TIMESTAMP:
118		t := value.(time.Time)
119		tproto, err := ptypes.TimestampProto(t)
120		if err != nil {
121			return types.NewErr("incorrect timestamp: %v", err)
122		}
123		return types.Timestamp{Timestamp: tproto}
124	case v1beta1.DURATION:
125		d := value.(time.Duration)
126		return types.Duration{Duration: ptypes.DurationProto(d)}
127	case v1beta1.STRING_MAP:
128		sm := value.(attribute.StringMap)
129		return stringMapValue{value: sm}
130	case v1beta1.IP_ADDRESS:
131		return wrapperValue{typ: typ, bytes: value.([]byte)}
132	case v1beta1.EMAIL_ADDRESS, v1beta1.URI, v1beta1.DNS_NAME:
133		return wrapperValue{typ: typ, s: value.(string)}
134	}
135	return types.NewErr("cannot convert value %#v of type %q", value, typ)
136}
137
138func recoverValue(value ref.Val) (interface{}, error) {
139	switch value.Type() {
140	case types.ErrType:
141		if err, ok := value.Value().(error); ok {
142			return nil, err
143		}
144		return nil, errors.New("unrecoverable error value")
145	case types.StringType, types.IntType, types.DoubleType, types.BoolType:
146		return value.Value(), nil
147	case types.TimestampType:
148		t := value.Value().(*tpb.Timestamp)
149		return ptypes.Timestamp(t)
150	case types.DurationType:
151		d := value.Value().(*dpb.Duration)
152		return ptypes.Duration(d)
153	case types.MapType:
154		size := value.(traits.Sizer).Size()
155		if size.Type() == types.IntType && size.Value().(int64) == 0 {
156			return emptyStringMap.value, nil
157		}
158		return value.Value(), nil
159	case wrapperType:
160		return value.Value(), nil
161	case types.ListType:
162		size := value.(traits.Sizer).Size()
163		if size.Type() == types.IntType && size.Value().(int64) == 0 {
164			return []string{}, nil
165		}
166		return value.Value(), nil
167	}
168	return nil, fmt.Errorf("failed to recover of type %s", value.Type())
169}
170
171var defaultValues = map[v1beta1.ValueType]ref.Val{
172	v1beta1.STRING:        types.String(""),
173	v1beta1.INT64:         types.Int(0),
174	v1beta1.DOUBLE:        types.Double(0),
175	v1beta1.BOOL:          types.Bool(false),
176	v1beta1.TIMESTAMP:     types.Timestamp{Timestamp: &tpb.Timestamp{}},
177	v1beta1.DURATION:      types.Duration{Duration: &dpb.Duration{}},
178	v1beta1.STRING_MAP:    emptyStringMap,
179	v1beta1.IP_ADDRESS:    wrapperValue{typ: v1beta1.IP_ADDRESS, bytes: []byte{}},
180	v1beta1.EMAIL_ADDRESS: wrapperValue{typ: v1beta1.EMAIL_ADDRESS, s: ""},
181	v1beta1.URI:           wrapperValue{typ: v1beta1.URI, s: ""},
182	v1beta1.DNS_NAME:      wrapperValue{typ: v1beta1.DNS_NAME, s: ""},
183}
184
185func defaultValue(typ v1beta1.ValueType) ref.Val {
186	if out, ok := defaultValues[typ]; ok {
187		return out
188	}
189	return types.NewErr("cannot provide defaults for %q", typ)
190}
191
192var (
193	stringMapType = &exprpb.Type{TypeKind: &exprpb.Type_MapType_{MapType: &exprpb.Type_MapType{
194		KeyType:   &exprpb.Type{TypeKind: &exprpb.Type_Primitive{Primitive: exprpb.Type_STRING}},
195		ValueType: &exprpb.Type{TypeKind: &exprpb.Type_Primitive{Primitive: exprpb.Type_STRING}},
196	}}}
197	emptyStringMap = stringMapValue{value: attribute.WrapStringMap(nil)}
198
199	// domain specific types do not implement any of type traits for now
200	wrapperType = types.NewTypeValue("wrapper")
201)
202
203const (
204	ipAddressType    = "istio.policy.v1beta1.IPAddress"
205	emailAddressType = "istio.policy.v1beta1.EmailAddress"
206	uriType          = "istio.policy.v1beta1.Uri"
207	dnsType          = "istio.policy.v1beta1.DNSName"
208)
209
210type stringMapValue struct {
211	value attribute.StringMap
212}
213
214func (v stringMapValue) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
215	return nil, errors.New("cannot convert stringmap to native types")
216}
217func (v stringMapValue) ConvertToType(typeValue ref.Type) ref.Val {
218	return types.NewErr("cannot convert stringmap to CEL types")
219}
220func (v stringMapValue) Equal(other ref.Val) ref.Val {
221	return types.NewErr("stringmap does not support equality")
222}
223func (v stringMapValue) Type() ref.Type {
224	return types.MapType
225}
226func (v stringMapValue) Value() interface{} {
227	return v.value
228}
229func (v stringMapValue) Get(index ref.Val) ref.Val {
230	if index.Type() != types.StringType {
231		return types.NewErr("index should be a string")
232	}
233
234	field := index.Value().(string)
235	value, found := v.value.Get(field)
236	if found {
237		return types.String(value)
238	}
239	return types.NewErr("no such key: '%s'", field)
240}
241func (v stringMapValue) Contains(index ref.Val) ref.Val {
242	if index.Type() != types.StringType {
243		return types.NewErr("index should be a string")
244	}
245
246	field := index.Value().(string)
247	_, found := v.value.Get(field)
248	return types.Bool(found)
249}
250func (v stringMapValue) Size() ref.Val {
251	return types.NewErr("size not implemented on stringmaps")
252}
253
254type wrapperValue struct {
255	typ   v1beta1.ValueType
256	bytes []byte
257	s     string
258}
259
260func (v wrapperValue) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
261	return nil, errors.New("cannot convert wrapper value to native types")
262}
263func (v wrapperValue) ConvertToType(typeValue ref.Type) ref.Val {
264	return types.NewErr("cannot convert wrapper value  to CEL types")
265}
266func (v wrapperValue) Equal(other ref.Val) ref.Val {
267	if other.Type() != wrapperType {
268		return types.NewErr("cannot compare types")
269	}
270	w, ok := other.(wrapperValue)
271	if !ok {
272		return types.NewErr("cannot compare types")
273	}
274	if v.typ != w.typ {
275		return types.NewErr("cannot compare %s and %s", v.typ, w.typ)
276	}
277	var out bool
278	var err error
279	switch v.typ {
280	case v1beta1.IP_ADDRESS:
281		out = lang.ExternIPEqual(v.bytes, w.bytes)
282	case v1beta1.DNS_NAME:
283		out, err = lang.ExternDNSNameEqual(v.s, w.s)
284	case v1beta1.EMAIL_ADDRESS:
285		out, err = lang.ExternEmailEqual(v.s, w.s)
286	case v1beta1.URI:
287		out, err = lang.ExternURIEqual(v.s, w.s)
288	}
289	if err != nil {
290		return types.NewErr(err.Error())
291	}
292	return types.Bool(out)
293}
294func (v wrapperValue) Type() ref.Type {
295	return wrapperType
296}
297func (v wrapperValue) Value() interface{} {
298	switch v.typ {
299	case v1beta1.IP_ADDRESS:
300		return v.bytes
301	default:
302		return v.s
303	}
304}
305