diff --git a/README.md b/README.md index ce622dd26cb..deebb9355fa 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ Tyk is a lightweight, open source API Gateway and Management Platform enables you to control who accesses your API, when they access it and how they access it. Tyk will also record detailed analytics on how your users are interacting with your API and when things go wrong. -Go version 1.8 or later is required to build `master`, the current +Go version 1.9 is required to build `master`, the current development version. Tyk is officially supported on `linux/amd64`, `linux/i386` and `linux/arm64`. +Tests are run against both Go versions 1.9 & 1.10, however at present, only Go 1.9 is officially supported. + ## What is an API Gateway? An API Gateway sits in front of your application(s) and manages the heavy lifting of authorisation, access control and throughput limiting to your services. Ideally, diff --git a/coprocess.go b/coprocess.go index 81377c41ff2..8f7f3686f8f 100644 --- a/coprocess.go +++ b/coprocess.go @@ -136,13 +136,8 @@ func (c *CoProcessor) ObjectFromRequest(r *http.Request) *coprocess.Object { if c.HookType != coprocess.HookType_Pre && c.HookType != coprocess.HookType_CustomKeyCheck { if session := ctxGetSession(r); session != nil { object.Session = ProtoSessionState(session) - // If the session contains metadata, add items to the object's metadata map: - if len(session.MetaData) > 0 { - object.Metadata = make(map[string]string) - for k, v := range session.MetaData { - object.Metadata[k] = v.(string) - } - } + // For compatibility purposes: + object.Metadata = object.Session.Metadata } } @@ -278,7 +273,20 @@ func (m *CoProcessMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Requ coProcessor.ObjectPostProcess(returnObject, r) - token := returnObject.Metadata["token"] + var token string + if returnObject.Session != nil { + // For compatibility purposes, inject coprocess.Object.Metadata fields: + if returnObject.Metadata != nil { + if returnObject.Session.Metadata == nil { + returnObject.Session.Metadata = make(map[string]string) + } + for k, v := range returnObject.Metadata { + returnObject.Session.Metadata[k] = v + } + } + + token = returnObject.Session.Metadata["token"] + } // The CP middleware indicates this is a bad auth: if returnObject.Request.ReturnOverrides.ResponseCode > 400 { diff --git a/coprocess/bindings/python/coprocess_session_state_pb2.py b/coprocess/bindings/python/coprocess_session_state_pb2.py index b313d51753c..c342655627b 100644 --- a/coprocess/bindings/python/coprocess_session_state_pb2.py +++ b/coprocess/bindings/python/coprocess_session_state_pb2.py @@ -19,7 +19,7 @@ name='coprocess_session_state.proto', package='coprocess', syntax='proto3', - serialized_pb=_b('\n\x1d\x63oprocess_session_state.proto\x12\tcoprocess\"*\n\nAccessSpec\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0f\n\x07methods\x18\x02 \x03(\t\"s\n\x10\x41\x63\x63\x65ssDefinition\x12\x10\n\x08\x61pi_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61pi_id\x18\x02 \x01(\t\x12\x10\n\x08versions\x18\x03 \x03(\t\x12+\n\x0c\x61llowed_urls\x18\x04 \x03(\x0b\x32\x15.coprocess.AccessSpec\"/\n\rBasicAuthData\x12\x10\n\x08password\x18\x01 \x01(\t\x12\x0c\n\x04hash\x18\x02 \x01(\t\"\x19\n\x07JWTData\x12\x0e\n\x06secret\x18\x01 \x01(\t\"!\n\x07Monitor\x12\x16\n\x0etrigger_limits\x18\x01 \x03(\x01\"\xa5\x07\n\x0cSessionState\x12\x12\n\nlast_check\x18\x01 \x01(\x03\x12\x11\n\tallowance\x18\x02 \x01(\x01\x12\x0c\n\x04rate\x18\x03 \x01(\x01\x12\x0b\n\x03per\x18\x04 \x01(\x01\x12\x0f\n\x07\x65xpires\x18\x05 \x01(\x03\x12\x11\n\tquota_max\x18\x06 \x01(\x03\x12\x14\n\x0cquota_renews\x18\x07 \x01(\x03\x12\x17\n\x0fquota_remaining\x18\x08 \x01(\x03\x12\x1a\n\x12quota_renewal_rate\x18\t \x01(\x03\x12@\n\raccess_rights\x18\n \x03(\x0b\x32).coprocess.SessionState.AccessRightsEntry\x12\x0e\n\x06org_id\x18\x0b \x01(\t\x12\x17\n\x0foauth_client_id\x18\x0c \x01(\t\x12:\n\noauth_keys\x18\r \x03(\x0b\x32&.coprocess.SessionState.OauthKeysEntry\x12\x31\n\x0f\x62\x61sic_auth_data\x18\x0e \x01(\x0b\x32\x18.coprocess.BasicAuthData\x12$\n\x08jwt_data\x18\x0f \x01(\x0b\x32\x12.coprocess.JWTData\x12\x14\n\x0chmac_enabled\x18\x10 \x01(\x08\x12\x13\n\x0bhmac_secret\x18\x11 \x01(\t\x12\x13\n\x0bis_inactive\x18\x12 \x01(\x08\x12\x17\n\x0f\x61pply_policy_id\x18\x13 \x01(\t\x12\x14\n\x0c\x64\x61ta_expires\x18\x14 \x01(\x03\x12#\n\x07monitor\x18\x15 \x01(\x0b\x32\x12.coprocess.Monitor\x12!\n\x19\x65nable_detailed_recording\x18\x16 \x01(\x08\x12\x10\n\x08metadata\x18\x17 \x01(\t\x12\x0c\n\x04tags\x18\x18 \x03(\t\x12\r\n\x05\x61lias\x18\x19 \x01(\t\x12\x14\n\x0clast_updated\x18\x1a \x01(\t\x12\x1d\n\x15id_extractor_deadline\x18\x1b \x01(\x03\x12\x18\n\x10session_lifetime\x18\x1c \x01(\x03\x12\x16\n\x0e\x61pply_policies\x18\x1d \x03(\t\x12\x13\n\x0b\x63\x65rtificate\x18\x1e \x01(\t\x1aP\n\x11\x41\x63\x63\x65ssRightsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x1b.coprocess.AccessDefinition:\x02\x38\x01\x1a\x30\n\x0eOauthKeysEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x62\x06proto3') + serialized_pb=_b('\n\x1d\x63oprocess_session_state.proto\x12\tcoprocess\"*\n\nAccessSpec\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0f\n\x07methods\x18\x02 \x03(\t\"s\n\x10\x41\x63\x63\x65ssDefinition\x12\x10\n\x08\x61pi_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61pi_id\x18\x02 \x01(\t\x12\x10\n\x08versions\x18\x03 \x03(\t\x12+\n\x0c\x61llowed_urls\x18\x04 \x03(\x0b\x32\x15.coprocess.AccessSpec\"/\n\rBasicAuthData\x12\x10\n\x08password\x18\x01 \x01(\t\x12\x0c\n\x04hash\x18\x02 \x01(\t\"\x19\n\x07JWTData\x12\x0e\n\x06secret\x18\x01 \x01(\t\"!\n\x07Monitor\x12\x16\n\x0etrigger_limits\x18\x01 \x03(\x01\"\xfd\x07\n\x0cSessionState\x12\x12\n\nlast_check\x18\x01 \x01(\x03\x12\x11\n\tallowance\x18\x02 \x01(\x01\x12\x0c\n\x04rate\x18\x03 \x01(\x01\x12\x0b\n\x03per\x18\x04 \x01(\x01\x12\x0f\n\x07\x65xpires\x18\x05 \x01(\x03\x12\x11\n\tquota_max\x18\x06 \x01(\x03\x12\x14\n\x0cquota_renews\x18\x07 \x01(\x03\x12\x17\n\x0fquota_remaining\x18\x08 \x01(\x03\x12\x1a\n\x12quota_renewal_rate\x18\t \x01(\x03\x12@\n\raccess_rights\x18\n \x03(\x0b\x32).coprocess.SessionState.AccessRightsEntry\x12\x0e\n\x06org_id\x18\x0b \x01(\t\x12\x17\n\x0foauth_client_id\x18\x0c \x01(\t\x12:\n\noauth_keys\x18\r \x03(\x0b\x32&.coprocess.SessionState.OauthKeysEntry\x12\x31\n\x0f\x62\x61sic_auth_data\x18\x0e \x01(\x0b\x32\x18.coprocess.BasicAuthData\x12$\n\x08jwt_data\x18\x0f \x01(\x0b\x32\x12.coprocess.JWTData\x12\x14\n\x0chmac_enabled\x18\x10 \x01(\x08\x12\x13\n\x0bhmac_secret\x18\x11 \x01(\t\x12\x13\n\x0bis_inactive\x18\x12 \x01(\x08\x12\x17\n\x0f\x61pply_policy_id\x18\x13 \x01(\t\x12\x14\n\x0c\x64\x61ta_expires\x18\x14 \x01(\x03\x12#\n\x07monitor\x18\x15 \x01(\x0b\x32\x12.coprocess.Monitor\x12!\n\x19\x65nable_detailed_recording\x18\x16 \x01(\x08\x12\x37\n\x08metadata\x18\x17 \x03(\x0b\x32%.coprocess.SessionState.MetadataEntry\x12\x0c\n\x04tags\x18\x18 \x03(\t\x12\r\n\x05\x61lias\x18\x19 \x01(\t\x12\x14\n\x0clast_updated\x18\x1a \x01(\t\x12\x1d\n\x15id_extractor_deadline\x18\x1b \x01(\x03\x12\x18\n\x10session_lifetime\x18\x1c \x01(\x03\x12\x16\n\x0e\x61pply_policies\x18\x1d \x03(\t\x12\x13\n\x0b\x63\x65rtificate\x18\x1e \x01(\t\x1aP\n\x11\x41\x63\x63\x65ssRightsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x1b.coprocess.AccessDefinition:\x02\x38\x01\x1a\x30\n\x0eOauthKeysEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x62\x06proto3') ) @@ -248,8 +248,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1120, - serialized_end=1200, + serialized_start=1159, + serialized_end=1239, ) _SESSIONSTATE_OAUTHKEYSENTRY = _descriptor.Descriptor( @@ -285,8 +285,45 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1202, - serialized_end=1250, + serialized_start=1241, + serialized_end=1289, +) + +_SESSIONSTATE_METADATAENTRY = _descriptor.Descriptor( + name='MetadataEntry', + full_name='coprocess.SessionState.MetadataEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='coprocess.SessionState.MetadataEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='coprocess.SessionState.MetadataEntry.value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1291, + serialized_end=1338, ) _SESSIONSTATE = _descriptor.Descriptor( @@ -452,8 +489,8 @@ options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( name='metadata', full_name='coprocess.SessionState.metadata', index=22, - number=23, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), + number=23, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), @@ -509,7 +546,7 @@ ], extensions=[ ], - nested_types=[_SESSIONSTATE_ACCESSRIGHTSENTRY, _SESSIONSTATE_OAUTHKEYSENTRY, ], + nested_types=[_SESSIONSTATE_ACCESSRIGHTSENTRY, _SESSIONSTATE_OAUTHKEYSENTRY, _SESSIONSTATE_METADATAENTRY, ], enum_types=[ ], options=None, @@ -519,18 +556,20 @@ oneofs=[ ], serialized_start=317, - serialized_end=1250, + serialized_end=1338, ) _ACCESSDEFINITION.fields_by_name['allowed_urls'].message_type = _ACCESSSPEC _SESSIONSTATE_ACCESSRIGHTSENTRY.fields_by_name['value'].message_type = _ACCESSDEFINITION _SESSIONSTATE_ACCESSRIGHTSENTRY.containing_type = _SESSIONSTATE _SESSIONSTATE_OAUTHKEYSENTRY.containing_type = _SESSIONSTATE +_SESSIONSTATE_METADATAENTRY.containing_type = _SESSIONSTATE _SESSIONSTATE.fields_by_name['access_rights'].message_type = _SESSIONSTATE_ACCESSRIGHTSENTRY _SESSIONSTATE.fields_by_name['oauth_keys'].message_type = _SESSIONSTATE_OAUTHKEYSENTRY _SESSIONSTATE.fields_by_name['basic_auth_data'].message_type = _BASICAUTHDATA _SESSIONSTATE.fields_by_name['jwt_data'].message_type = _JWTDATA _SESSIONSTATE.fields_by_name['monitor'].message_type = _MONITOR +_SESSIONSTATE.fields_by_name['metadata'].message_type = _SESSIONSTATE_METADATAENTRY DESCRIPTOR.message_types_by_name['AccessSpec'] = _ACCESSSPEC DESCRIPTOR.message_types_by_name['AccessDefinition'] = _ACCESSDEFINITION DESCRIPTOR.message_types_by_name['BasicAuthData'] = _BASICAUTHDATA @@ -589,6 +628,13 @@ # @@protoc_insertion_point(class_scope:coprocess.SessionState.OauthKeysEntry) )) , + + MetadataEntry = _reflection.GeneratedProtocolMessageType('MetadataEntry', (_message.Message,), dict( + DESCRIPTOR = _SESSIONSTATE_METADATAENTRY, + __module__ = 'coprocess_session_state_pb2' + # @@protoc_insertion_point(class_scope:coprocess.SessionState.MetadataEntry) + )) + , DESCRIPTOR = _SESSIONSTATE, __module__ = 'coprocess_session_state_pb2' # @@protoc_insertion_point(class_scope:coprocess.SessionState) @@ -596,10 +642,13 @@ _sym_db.RegisterMessage(SessionState) _sym_db.RegisterMessage(SessionState.AccessRightsEntry) _sym_db.RegisterMessage(SessionState.OauthKeysEntry) +_sym_db.RegisterMessage(SessionState.MetadataEntry) _SESSIONSTATE_ACCESSRIGHTSENTRY.has_options = True _SESSIONSTATE_ACCESSRIGHTSENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) _SESSIONSTATE_OAUTHKEYSENTRY.has_options = True _SESSIONSTATE_OAUTHKEYSENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) +_SESSIONSTATE_METADATAENTRY.has_options = True +_SESSIONSTATE_METADATAENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) # @@protoc_insertion_point(module_scope) diff --git a/coprocess/bindings/ruby/coprocess_session_state_pb.rb b/coprocess/bindings/ruby/coprocess_session_state_pb.rb index d9a611c4c29..7f638c3e097 100644 --- a/coprocess/bindings/ruby/coprocess_session_state_pb.rb +++ b/coprocess/bindings/ruby/coprocess_session_state_pb.rb @@ -47,7 +47,7 @@ optional :data_expires, :int64, 20 optional :monitor, :message, 21, "coprocess.Monitor" optional :enable_detailed_recording, :bool, 22 - optional :metadata, :string, 23 + map :metadata, :string, :string, 23 repeated :tags, :string, 24 optional :alias, :string, 25 optional :last_updated, :string, 26 diff --git a/coprocess/coprocess_session_state.pb.go b/coprocess/coprocess_session_state.pb.go index c69efce4210..6149fa78cad 100644 --- a/coprocess/coprocess_session_state.pb.go +++ b/coprocess/coprocess_session_state.pb.go @@ -155,7 +155,7 @@ type SessionState struct { DataExpires int64 `protobuf:"varint,20,opt,name=data_expires,json=dataExpires" json:"data_expires,omitempty"` Monitor *Monitor `protobuf:"bytes,21,opt,name=monitor" json:"monitor,omitempty"` EnableDetailedRecording bool `protobuf:"varint,22,opt,name=enable_detailed_recording,json=enableDetailedRecording" json:"enable_detailed_recording,omitempty"` - Metadata string `protobuf:"bytes,23,opt,name=metadata" json:"metadata,omitempty"` + Metadata map[string]string `protobuf:"bytes,23,rep,name=metadata" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Tags []string `protobuf:"bytes,24,rep,name=tags" json:"tags,omitempty"` Alias string `protobuf:"bytes,25,opt,name=alias" json:"alias,omitempty"` LastUpdated string `protobuf:"bytes,26,opt,name=last_updated,json=lastUpdated" json:"last_updated,omitempty"` @@ -324,11 +324,11 @@ func (m *SessionState) GetEnableDetailedRecording() bool { return false } -func (m *SessionState) GetMetadata() string { +func (m *SessionState) GetMetadata() map[string]string { if m != nil { return m.Metadata } - return "" + return nil } func (m *SessionState) GetTags() []string { @@ -392,63 +392,64 @@ func init() { func init() { proto.RegisterFile("coprocess_session_state.proto", fileDescriptor4) } var fileDescriptor4 = []byte{ - // 914 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x55, 0x6f, 0x4f, 0x1b, 0xc7, - 0x13, 0x96, 0x31, 0x60, 0x7b, 0x6c, 0x03, 0xd9, 0x40, 0xb2, 0x40, 0xf8, 0xfd, 0x8c, 0xa5, 0xa6, - 0x8e, 0x94, 0xa2, 0x96, 0xbe, 0x41, 0x51, 0xa5, 0x36, 0x0d, 0xbc, 0xa0, 0x4d, 0xd2, 0xea, 0x68, - 0xd4, 0x37, 0x95, 0x56, 0xc3, 0xdd, 0x60, 0x6f, 0xb8, 0x7f, 0xdd, 0x5d, 0x63, 0xfc, 0x55, 0xfa, - 0x29, 0xfa, 0x11, 0xab, 0x9d, 0xdb, 0x83, 0x43, 0xb4, 0xef, 0x76, 0x9e, 0xe7, 0xd9, 0xb9, 0x67, - 0x77, 0x67, 0xe6, 0xe0, 0x20, 0x2e, 0x4a, 0x53, 0xc4, 0x64, 0xad, 0xb2, 0x64, 0xad, 0x2e, 0x72, - 0x65, 0x1d, 0x3a, 0x3a, 0x2a, 0x4d, 0xe1, 0x0a, 0xd1, 0xbb, 0xa3, 0xc7, 0x27, 0x00, 0x6f, 0x63, - 0xbf, 0xba, 0x28, 0x29, 0x16, 0x5b, 0xd0, 0x9e, 0x9b, 0x54, 0xb6, 0x46, 0xad, 0x49, 0x2f, 0xf2, - 0x4b, 0x21, 0xa1, 0x93, 0x91, 0x9b, 0x15, 0x89, 0x95, 0x2b, 0xa3, 0xf6, 0xa4, 0x17, 0xd5, 0xe1, - 0xf8, 0xaf, 0x16, 0x6c, 0x55, 0x5b, 0x4f, 0xe9, 0x4a, 0xe7, 0xda, 0xe9, 0x22, 0x17, 0xbb, 0xd0, - 0xc5, 0x52, 0xab, 0x1c, 0x33, 0x0a, 0x59, 0x3a, 0x58, 0xea, 0x8f, 0x98, 0x91, 0xd8, 0x81, 0x75, - 0x4f, 0xe9, 0x44, 0xae, 0x30, 0xb1, 0x86, 0xa5, 0x3e, 0x4f, 0xc4, 0x1e, 0x74, 0x6f, 0xc8, 0x78, - 0x8b, 0x56, 0xb6, 0xf9, 0x0b, 0x77, 0xb1, 0x38, 0x81, 0x01, 0xa6, 0x69, 0xb1, 0xa0, 0x44, 0xcd, - 0x4d, 0x6a, 0xe5, 0xea, 0xa8, 0x3d, 0xe9, 0x1f, 0xef, 0x1c, 0xdd, 0xd9, 0x3f, 0xba, 0xf7, 0x1e, - 0xf5, 0x83, 0xf4, 0x93, 0x49, 0xed, 0xf8, 0x7b, 0x18, 0xfe, 0x88, 0x56, 0xc7, 0x6f, 0xe7, 0x6e, - 0x76, 0x8a, 0x0e, 0xfd, 0x67, 0x4a, 0xb4, 0x76, 0x51, 0x98, 0x24, 0x18, 0xbb, 0x8b, 0x85, 0x80, - 0xd5, 0x19, 0xda, 0x59, 0xf0, 0xc5, 0xeb, 0xf1, 0x21, 0x74, 0x7e, 0xfa, 0xfd, 0x37, 0xde, 0xfa, - 0x0c, 0xd6, 0x2d, 0xc5, 0x86, 0x5c, 0xd8, 0x18, 0xa2, 0xf1, 0xd7, 0xd0, 0xf9, 0x50, 0xe4, 0xda, - 0x15, 0x46, 0x7c, 0x01, 0x1b, 0xce, 0xe8, 0xe9, 0x94, 0x8c, 0x4a, 0x75, 0xa6, 0x9d, 0x95, 0xad, - 0x51, 0x7b, 0xd2, 0x8a, 0x86, 0x01, 0x7d, 0xcf, 0xe0, 0xf8, 0x6f, 0x80, 0xc1, 0x45, 0xf5, 0x1e, - 0x17, 0xfe, 0x39, 0xc4, 0x01, 0x40, 0x8a, 0xd6, 0xa9, 0x78, 0x46, 0xf1, 0x35, 0xa7, 0x6f, 0x47, - 0x3d, 0x8f, 0xbc, 0xf3, 0x80, 0x78, 0x01, 0x3d, 0x3e, 0x14, 0xe6, 0x31, 0xb1, 0xbb, 0x56, 0x74, - 0x0f, 0x78, 0xdb, 0x06, 0x1d, 0xc9, 0x36, 0x13, 0xbc, 0xf6, 0x0f, 0x58, 0x92, 0x91, 0xab, 0x0c, - 0xf9, 0xa5, 0x7f, 0x40, 0xba, 0x2d, 0xb5, 0x21, 0x2b, 0xd7, 0x38, 0x7f, 0x1d, 0x8a, 0x7d, 0xe8, - 0xfd, 0x39, 0x2f, 0x1c, 0xaa, 0x0c, 0x6f, 0xe5, 0x3a, 0x73, 0x5d, 0x06, 0x3e, 0xe0, 0xad, 0x38, - 0x84, 0x41, 0x45, 0x1a, 0xca, 0x69, 0x61, 0x65, 0x87, 0xf9, 0x3e, 0x63, 0x11, 0x43, 0xe2, 0x4b, - 0xd8, 0xac, 0x25, 0x19, 0xea, 0x5c, 0xe7, 0x53, 0xd9, 0x65, 0xd5, 0x46, 0x50, 0x05, 0x54, 0xbc, - 0x06, 0xd1, 0xc8, 0x85, 0xa9, 0x62, 0xdb, 0x3d, 0xd6, 0x6e, 0xdd, 0x67, 0xc4, 0x34, 0xf2, 0x47, - 0xf8, 0x08, 0x43, 0xe4, 0x57, 0x55, 0x46, 0x4f, 0x67, 0xce, 0x4a, 0xe0, 0x57, 0x7f, 0xd5, 0x78, - 0xf5, 0xe6, 0x1d, 0x86, 0x12, 0x88, 0x58, 0x7b, 0x96, 0x3b, 0xb3, 0x8c, 0x06, 0xd8, 0x80, 0x7c, - 0xdd, 0x15, 0x66, 0xea, 0xeb, 0xae, 0x5f, 0xd5, 0x5d, 0x61, 0xa6, 0xe7, 0x89, 0x78, 0x09, 0x9b, - 0x05, 0xce, 0xdd, 0x4c, 0xc5, 0xa9, 0xa6, 0xdc, 0x79, 0x7e, 0xc0, 0xfc, 0x90, 0xe1, 0x77, 0x8c, - 0x9e, 0x27, 0xe2, 0x0c, 0xa0, 0xd2, 0x5d, 0xd3, 0xd2, 0xca, 0x21, 0x7b, 0x79, 0xf9, 0x5f, 0x5e, - 0x7e, 0xf1, 0xca, 0x9f, 0x69, 0x19, 0x8c, 0xf4, 0x8a, 0x3a, 0x16, 0x3f, 0xc0, 0xe6, 0xa5, 0x2f, - 0x48, 0xc5, 0xb9, 0x12, 0x74, 0x28, 0x37, 0x46, 0xad, 0x49, 0xff, 0x58, 0x36, 0x72, 0x3d, 0x28, - 0xd9, 0x68, 0x78, 0xf9, 0xa0, 0x82, 0xbf, 0x82, 0xee, 0xe7, 0x85, 0xab, 0xb6, 0x6e, 0xf2, 0x56, - 0xd1, 0xd8, 0x1a, 0x8a, 0x35, 0xea, 0x7c, 0x5e, 0x38, 0x96, 0x1f, 0xc2, 0x60, 0x96, 0x61, 0xac, - 0x28, 0xc7, 0xcb, 0x94, 0x12, 0xb9, 0x35, 0x6a, 0x4d, 0xba, 0x51, 0xdf, 0x63, 0x67, 0x15, 0x24, - 0xfe, 0x0f, 0x1c, 0xaa, 0x50, 0xdd, 0x4f, 0xf8, 0xf8, 0xe0, 0xa1, 0x0b, 0x46, 0xbc, 0x40, 0x5b, - 0xa5, 0x73, 0x8c, 0x9d, 0xbe, 0x21, 0x29, 0x38, 0x05, 0x68, 0x7b, 0x1e, 0x10, 0x7f, 0x89, 0x58, - 0x96, 0xe9, 0x52, 0x95, 0x45, 0xaa, 0xe3, 0xa5, 0xbf, 0xc4, 0xa7, 0xd5, 0x25, 0x32, 0xfc, 0x2b, - 0xa3, 0xe7, 0x89, 0x37, 0xe3, 0x7d, 0xab, 0xba, 0x12, 0xb7, 0xab, 0x6a, 0xf2, 0xd8, 0x59, 0xa8, - 0xc6, 0xd7, 0xd0, 0xc9, 0xaa, 0x6e, 0x92, 0x3b, 0x8f, 0x4e, 0x17, 0xfa, 0x2c, 0xaa, 0x25, 0xe2, - 0x0d, 0xec, 0x56, 0x07, 0x53, 0x09, 0x39, 0xd4, 0x29, 0x25, 0xca, 0x50, 0x5c, 0x98, 0xc4, 0x57, - 0xe1, 0x33, 0xf6, 0xf9, 0xbc, 0x12, 0x9c, 0x06, 0x3e, 0xaa, 0x69, 0x3f, 0x0a, 0x32, 0x72, 0xc8, - 0x17, 0xf9, 0xbc, 0x1a, 0x05, 0x75, 0xec, 0x7b, 0xca, 0xe1, 0xd4, 0x4a, 0xc9, 0x93, 0x88, 0xd7, - 0x62, 0x1b, 0xd6, 0x30, 0xd5, 0x68, 0xe5, 0x6e, 0x98, 0x5b, 0x3e, 0xf0, 0x47, 0xe2, 0xd6, 0x9d, - 0x97, 0x09, 0x3a, 0x4a, 0xe4, 0x1e, 0x93, 0x7d, 0x8f, 0x7d, 0xaa, 0x20, 0x71, 0x0c, 0x3b, 0x3a, - 0x51, 0x74, 0xeb, 0x0c, 0xc6, 0xae, 0x30, 0x2a, 0x21, 0x4c, 0x52, 0x9d, 0x93, 0xdc, 0xe7, 0xe3, - 0x3f, 0xd5, 0xc9, 0x59, 0xcd, 0x9d, 0x06, 0x4a, 0xbc, 0x82, 0xad, 0x7a, 0x62, 0xa7, 0xfa, 0x8a, - 0x9c, 0xce, 0x48, 0xbe, 0x60, 0xf9, 0x66, 0xc0, 0xdf, 0x07, 0xd8, 0x0f, 0x9d, 0xc6, 0xe5, 0x6b, - 0xb2, 0xf2, 0x80, 0x5d, 0x37, 0xee, 0x5e, 0x93, 0x15, 0x23, 0xe8, 0xc7, 0x64, 0x9c, 0xbe, 0xd2, - 0xb1, 0x6f, 0xbb, 0xff, 0x55, 0x3e, 0x1b, 0xd0, 0xde, 0x1f, 0xf0, 0xe4, 0x51, 0x13, 0xf9, 0x49, - 0x72, 0x4d, 0xcb, 0xfa, 0x57, 0x70, 0x4d, 0x4b, 0xf1, 0x0d, 0xac, 0xdd, 0x60, 0x3a, 0xaf, 0x26, - 0x51, 0xff, 0x78, 0xff, 0xd1, 0x18, 0xbe, 0xff, 0x0f, 0x44, 0x95, 0xf2, 0xcd, 0xca, 0x49, 0x6b, - 0xef, 0x3b, 0xd8, 0x78, 0xd8, 0x16, 0xff, 0x92, 0x7a, 0xbb, 0x99, 0xba, 0xd7, 0xd8, 0x7d, 0xb9, - 0xce, 0x7f, 0xac, 0x6f, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x93, 0xd4, 0x16, 0x3a, 0xd2, 0x06, - 0x00, 0x00, + // 933 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x4f, 0x6f, 0x1b, 0xb7, + 0x13, 0x85, 0x2c, 0xdb, 0x92, 0x66, 0x25, 0x5b, 0x61, 0xec, 0x84, 0xb6, 0xe3, 0xdf, 0x4f, 0x16, + 0x90, 0x54, 0x01, 0x52, 0xa3, 0x75, 0x2f, 0x46, 0x5a, 0xa0, 0x75, 0x63, 0x1d, 0xd4, 0xc6, 0x69, + 0xb1, 0x6e, 0xd0, 0x4b, 0x01, 0x82, 0xde, 0x1d, 0x4b, 0x8c, 0xf7, 0x5f, 0x49, 0xca, 0xb2, 0xbe, + 0x47, 0x4f, 0xfd, 0xb4, 0x05, 0x67, 0xb9, 0xf2, 0x1a, 0x6e, 0x0e, 0xbd, 0x71, 0xde, 0x7b, 0x33, + 0xfb, 0xc8, 0x19, 0x72, 0xe1, 0x30, 0xca, 0x0b, 0x9d, 0x47, 0x68, 0x8c, 0x30, 0x68, 0x8c, 0xca, + 0x33, 0x61, 0xac, 0xb4, 0x78, 0x5c, 0xe8, 0xdc, 0xe6, 0xac, 0xb3, 0xa2, 0x87, 0xa7, 0x00, 0x67, + 0x91, 0x5b, 0x5d, 0x16, 0x18, 0xb1, 0x3e, 0x34, 0xe7, 0x3a, 0xe1, 0x8d, 0x41, 0x63, 0xd4, 0x09, + 0xdd, 0x92, 0x71, 0x68, 0xa5, 0x68, 0x67, 0x79, 0x6c, 0xf8, 0xda, 0xa0, 0x39, 0xea, 0x84, 0x55, + 0x38, 0xfc, 0xbb, 0x01, 0xfd, 0x32, 0xf5, 0x1c, 0xaf, 0x55, 0xa6, 0xac, 0xca, 0x33, 0xb6, 0x07, + 0x6d, 0x59, 0x28, 0x91, 0xc9, 0x14, 0x7d, 0x95, 0x96, 0x2c, 0xd4, 0x07, 0x99, 0x22, 0xdb, 0x85, + 0x4d, 0x47, 0xa9, 0x98, 0xaf, 0x11, 0xb1, 0x21, 0x0b, 0x35, 0x89, 0xd9, 0x3e, 0xb4, 0x6f, 0x51, + 0x3b, 0x8b, 0x86, 0x37, 0xe9, 0x0b, 0xab, 0x98, 0x9d, 0x42, 0x57, 0x26, 0x49, 0xbe, 0xc0, 0x58, + 0xcc, 0x75, 0x62, 0xf8, 0xfa, 0xa0, 0x39, 0x0a, 0x4e, 0x76, 0x8f, 0x57, 0xf6, 0x8f, 0xef, 0xbd, + 0x87, 0x81, 0x97, 0x7e, 0xd4, 0x89, 0x19, 0x7e, 0x0f, 0xbd, 0x1f, 0xa5, 0x51, 0xd1, 0xd9, 0xdc, + 0xce, 0xce, 0xa5, 0x95, 0xee, 0x33, 0x85, 0x34, 0x66, 0x91, 0xeb, 0xd8, 0x1b, 0x5b, 0xc5, 0x8c, + 0xc1, 0xfa, 0x4c, 0x9a, 0x99, 0xf7, 0x45, 0xeb, 0xe1, 0x11, 0xb4, 0x7e, 0xfa, 0xfd, 0x37, 0x4a, + 0x7d, 0x06, 0x9b, 0x06, 0x23, 0x8d, 0xd6, 0x27, 0xfa, 0x68, 0xf8, 0x15, 0xb4, 0x2e, 0xf2, 0x4c, + 0xd9, 0x5c, 0xb3, 0x97, 0xb0, 0x65, 0xb5, 0x9a, 0x4e, 0x51, 0x8b, 0x44, 0xa5, 0xca, 0x1a, 0xde, + 0x18, 0x34, 0x47, 0x8d, 0xb0, 0xe7, 0xd1, 0xf7, 0x04, 0x0e, 0xff, 0x0a, 0xa0, 0x7b, 0x59, 0xf6, + 0xe3, 0xd2, 0xb5, 0x83, 0x1d, 0x02, 0x24, 0xd2, 0x58, 0x11, 0xcd, 0x30, 0xba, 0xa1, 0xf2, 0xcd, + 0xb0, 0xe3, 0x90, 0x77, 0x0e, 0x60, 0x2f, 0xa0, 0x43, 0x9b, 0x92, 0x59, 0x84, 0xe4, 0xae, 0x11, + 0xde, 0x03, 0xce, 0xb6, 0x96, 0x16, 0x79, 0x93, 0x08, 0x5a, 0xbb, 0x06, 0x16, 0xa8, 0xf9, 0x3a, + 0x41, 0x6e, 0xe9, 0x1a, 0x88, 0x77, 0x85, 0xd2, 0x68, 0xf8, 0x06, 0xd5, 0xaf, 0x42, 0x76, 0x00, + 0x9d, 0x3f, 0xe7, 0xb9, 0x95, 0x22, 0x95, 0x77, 0x7c, 0x93, 0xb8, 0x36, 0x01, 0x17, 0xf2, 0x8e, + 0x1d, 0x41, 0xb7, 0x24, 0x35, 0x66, 0xb8, 0x30, 0xbc, 0x45, 0x7c, 0x40, 0x58, 0x48, 0x10, 0xfb, + 0x02, 0xb6, 0x2b, 0x49, 0x2a, 0x55, 0xa6, 0xb2, 0x29, 0x6f, 0x93, 0x6a, 0xcb, 0xab, 0x3c, 0xca, + 0xde, 0x00, 0xab, 0xd5, 0x92, 0x89, 0x20, 0xdb, 0x1d, 0xd2, 0xf6, 0xef, 0x2b, 0xca, 0x24, 0x74, + 0x5b, 0xf8, 0x00, 0x3d, 0x49, 0x5d, 0x15, 0x5a, 0x4d, 0x67, 0xd6, 0x70, 0xa0, 0xae, 0xbf, 0xae, + 0x75, 0xbd, 0x7e, 0x86, 0x7e, 0x04, 0x42, 0xd2, 0x8e, 0x33, 0xab, 0x97, 0x61, 0x57, 0xd6, 0x20, + 0x37, 0x77, 0xb9, 0x9e, 0xba, 0xb9, 0x0b, 0xca, 0xb9, 0xcb, 0xf5, 0x74, 0x12, 0xb3, 0x57, 0xb0, + 0x9d, 0xcb, 0xb9, 0x9d, 0x89, 0x28, 0x51, 0x98, 0x59, 0xc7, 0x77, 0x89, 0xef, 0x11, 0xfc, 0x8e, + 0xd0, 0x49, 0xcc, 0xc6, 0x00, 0xa5, 0xee, 0x06, 0x97, 0x86, 0xf7, 0xc8, 0xcb, 0xab, 0xcf, 0x79, + 0xf9, 0xc5, 0x29, 0x7f, 0xc6, 0xa5, 0x37, 0xd2, 0xc9, 0xab, 0x98, 0xfd, 0x00, 0xdb, 0x57, 0x6e, + 0x20, 0x05, 0xd5, 0x8a, 0xa5, 0x95, 0x7c, 0x6b, 0xd0, 0x18, 0x05, 0x27, 0xbc, 0x56, 0xeb, 0xc1, + 0xc8, 0x86, 0xbd, 0xab, 0x07, 0x13, 0xfc, 0x25, 0xb4, 0x3f, 0x2d, 0x6c, 0x99, 0xba, 0x4d, 0xa9, + 0xac, 0x96, 0xea, 0x87, 0x35, 0x6c, 0x7d, 0x5a, 0x58, 0x92, 0x1f, 0x41, 0x77, 0x96, 0xca, 0x48, + 0x60, 0x26, 0xaf, 0x12, 0x8c, 0x79, 0x7f, 0xd0, 0x18, 0xb5, 0xc3, 0xc0, 0x61, 0xe3, 0x12, 0x62, + 0xff, 0x07, 0x0a, 0x85, 0x9f, 0xee, 0x27, 0xb4, 0x7d, 0x70, 0xd0, 0x25, 0x21, 0x4e, 0xa0, 0x8c, + 0x50, 0x99, 0x8c, 0xac, 0xba, 0x45, 0xce, 0xa8, 0x04, 0x28, 0x33, 0xf1, 0x88, 0x3b, 0x44, 0x59, + 0x14, 0xc9, 0x52, 0x14, 0x79, 0xa2, 0xa2, 0xa5, 0x3b, 0xc4, 0xa7, 0xe5, 0x21, 0x12, 0xfc, 0x2b, + 0xa1, 0x93, 0xd8, 0x99, 0x71, 0xbe, 0x45, 0x35, 0x89, 0x3b, 0xe5, 0x34, 0x39, 0x6c, 0xec, 0xa7, + 0xf1, 0x0d, 0xb4, 0xd2, 0xf2, 0x36, 0xf1, 0xdd, 0x47, 0xbb, 0xf3, 0xf7, 0x2c, 0xac, 0x24, 0xec, + 0x2d, 0xec, 0x95, 0x1b, 0x13, 0x31, 0x5a, 0xa9, 0x12, 0x8c, 0x85, 0xc6, 0x28, 0xd7, 0xb1, 0x9b, + 0xc2, 0x67, 0xe4, 0xf3, 0x79, 0x29, 0x38, 0xf7, 0x7c, 0x58, 0xd1, 0xec, 0x0c, 0xda, 0x29, 0x5a, + 0x49, 0x07, 0xf9, 0x9c, 0xfa, 0xf9, 0xf2, 0x73, 0xfd, 0xbc, 0xf0, 0xba, 0xb2, 0x9d, 0xab, 0x34, + 0x77, 0xf5, 0xac, 0x9c, 0x1a, 0xce, 0xe9, 0xc1, 0xa2, 0x35, 0xdb, 0x81, 0x0d, 0x99, 0x28, 0x69, + 0xf8, 0x9e, 0x7f, 0xde, 0x5c, 0xe0, 0x76, 0x4e, 0x37, 0x7c, 0x5e, 0xc4, 0xd2, 0x62, 0xcc, 0xf7, + 0x89, 0x0c, 0x1c, 0xf6, 0xb1, 0x84, 0xd8, 0x09, 0xec, 0xaa, 0x58, 0xe0, 0x9d, 0xd5, 0x32, 0xb2, + 0xb9, 0x16, 0x31, 0xca, 0x38, 0x51, 0x19, 0xf2, 0x03, 0x3a, 0xa5, 0xa7, 0x2a, 0x1e, 0x57, 0xdc, + 0xb9, 0xa7, 0xd8, 0x6b, 0xe8, 0x57, 0x0f, 0x7b, 0xa2, 0xae, 0xd1, 0xaa, 0x14, 0xf9, 0x0b, 0x92, + 0x6f, 0x7b, 0xfc, 0xbd, 0x87, 0xdd, 0xdb, 0x54, 0xeb, 0x91, 0x42, 0xc3, 0x0f, 0xc9, 0x75, 0xad, + 0x45, 0x0a, 0x0d, 0x1b, 0x40, 0x10, 0xa1, 0xb6, 0xea, 0x5a, 0x45, 0xee, 0x76, 0xfe, 0xaf, 0xf4, + 0x59, 0x83, 0xf6, 0xff, 0x80, 0x27, 0x8f, 0xee, 0x9a, 0x7b, 0x70, 0x6e, 0x70, 0x59, 0xfd, 0x31, + 0x6e, 0x70, 0xc9, 0xbe, 0x86, 0x8d, 0x5b, 0x99, 0xcc, 0xcb, 0x07, 0x2b, 0x38, 0x39, 0x78, 0xf4, + 0x5a, 0xdf, 0xff, 0x2e, 0xc2, 0x52, 0xf9, 0x76, 0xed, 0xb4, 0xb1, 0xff, 0x1d, 0x6c, 0x3d, 0xbc, + 0x3d, 0xff, 0x52, 0x7a, 0xa7, 0x5e, 0xba, 0x53, 0xcf, 0xfe, 0x16, 0x7a, 0x0f, 0x7a, 0xf5, 0x5f, + 0x92, 0xaf, 0x36, 0xe9, 0xaf, 0xf8, 0xcd, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdc, 0xad, 0x0c, + 0x63, 0x36, 0x07, 0x00, 0x00, } diff --git a/coprocess/proto/coprocess_session_state.proto b/coprocess/proto/coprocess_session_state.proto index 36f080127e9..c045bfd10c6 100644 --- a/coprocess/proto/coprocess_session_state.proto +++ b/coprocess/proto/coprocess_session_state.proto @@ -60,7 +60,7 @@ message SessionState { bool enable_detailed_recording = 22; - string metadata = 23; + map metadata = 23; repeated string tags = 24; string alias = 25; diff --git a/coprocess/python/tyk/middleware.py b/coprocess/python/tyk/middleware.py index 44b84ec39be..5ad0cf5ec16 100644 --- a/coprocess/python/tyk/middleware.py +++ b/coprocess/python/tyk/middleware.py @@ -78,7 +78,9 @@ def process(self, handler, object): handler(object, object.spec) return elif handler.arg_count == 4: - object.request, object.session, object.metadata = handler(object.request, object.session, object.metadata, object.spec) + md = object.session.metadata + object.request, object.session, md = handler(object.request, object.session, md, object.spec) + object.session.metadata.MergeFrom(md) elif handler.arg_count == 3: object.request, object.session = handler(object.request, object.session, object.spec) return object diff --git a/coprocess_grpc_test.go b/coprocess_grpc_test.go index 135d4abb493..717568f803b 100644 --- a/coprocess_grpc_test.go +++ b/coprocess_grpc_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net" "net/http" + "strings" "testing" "context" @@ -18,6 +19,7 @@ import ( "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/coprocess" "github.com/TykTechnologies/tyk/test" + "github.com/TykTechnologies/tyk/user" ) const ( @@ -36,6 +38,56 @@ func (d *dispatcher) Dispatch(ctx context.Context, object *coprocess.Object) (*c object.Request.SetHeaders = map[string]string{ testHeaderName: testHeaderValue, } + case "testPostHook1": + testKeyValue, ok := object.Session.Metadata["testkey"] + if !ok { + object.Request.ReturnOverrides.ResponseError = "'testkey' not found in session metadata" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + jsonObject := make(map[string]string) + if err := json.Unmarshal([]byte(testKeyValue), &jsonObject); err != nil { + object.Request.ReturnOverrides.ResponseError = "couldn't decode 'testkey' nested value" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + nestedKeyValue, ok := jsonObject["nestedkey"] + if !ok { + object.Request.ReturnOverrides.ResponseError = "'nestedkey' not found in JSON object" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + if nestedKeyValue != "nestedvalue" { + object.Request.ReturnOverrides.ResponseError = "'nestedvalue' value doesn't match" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + testKey2Value, ok := object.Session.Metadata["testkey2"] + if !ok { + object.Request.ReturnOverrides.ResponseError = "'testkey' not found in session metadata" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + if testKey2Value != "testvalue" { + object.Request.ReturnOverrides.ResponseError = "'testkey2' value doesn't match" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + + // Check for compatibility (object.Metadata should contain the same keys as object.Session.Metadata) + for k, v := range object.Metadata { + sessionKeyValue, ok := object.Session.Metadata[k] + if !ok { + object.Request.ReturnOverrides.ResponseError = k + " not found in object.Session.Metadata" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + if strings.Compare(sessionKeyValue, v) != 0 { + object.Request.ReturnOverrides.ResponseError = k + " doesn't match value in object.Session.Metadata" + object.Request.ReturnOverrides.ResponseCode = 400 + break + } + } } return object, nil } @@ -50,13 +102,14 @@ func newTestGRPCServer() (s *grpc.Server) { return s } -func loadTestGRPCSpec() *APISpec { - spec := buildAndLoadAPI(func(spec *APISpec) { - spec.APIID = "999999" +func loadTestGRPCAPIs() { + buildAndLoadAPI(func(spec *APISpec) { + spec.APIID = "1" spec.OrgID = "default" spec.Auth = apidef.Auth{ AuthHeaderName: "authorization", } + spec.UseKeylessAccess = false spec.VersionData = struct { NotVersioned bool `bson:"not_versioned" json:"not_versioned"` DefaultVersion string `bson:"default_version" json:"default_version"` @@ -77,9 +130,34 @@ func loadTestGRPCSpec() *APISpec { }, Driver: apidef.GrpcDriver, } - })[0] - - return spec + }, func(spec *APISpec) { + spec.APIID = "2" + spec.OrgID = "default" + spec.Auth = apidef.Auth{ + AuthHeaderName: "authorization", + } + spec.UseKeylessAccess = false + spec.VersionData = struct { + NotVersioned bool `bson:"not_versioned" json:"not_versioned"` + DefaultVersion string `bson:"default_version" json:"default_version"` + Versions map[string]apidef.VersionInfo `bson:"versions" json:"versions"` + }{ + NotVersioned: true, + Versions: map[string]apidef.VersionInfo{ + "v1": { + Name: "v1", + }, + }, + } + spec.Proxy.ListenPath = "/grpc-test-api-2/" + spec.Proxy.StripListenPath = true + spec.CustomMiddleware = apidef.MiddlewareSection{ + Post: []apidef.MiddlewareDefinition{ + {Name: "testPostHook1"}, + }, + Driver: apidef.GrpcDriver, + } + }) } func startTykWithGRPC() (*tykTestServer, *grpc.Server) { @@ -95,8 +173,8 @@ func startTykWithGRPC() (*tykTestServer, *grpc.Server) { } ts := newTykTestServer(tykTestServerConfig{coprocessConfig: cfg}) - // Load a test API: - loadTestGRPCSpec() + // Load test APIs: + loadTestGRPCAPIs() return &ts, grpcServer } @@ -105,11 +183,20 @@ func TestGRPCDispatch(t *testing.T) { defer ts.Close() defer grpcServer.Stop() + keyID := createSession(func(s *user.SessionState) { + s.MetaData = map[string]interface{}{ + "testkey": map[string]interface{}{"nestedkey": "nestedvalue"}, + "testkey2": "testvalue", + } + }) + headers := map[string]string{"authorization": keyID} + t.Run("Pre Hook with SetHeaders", func(t *testing.T) { res, err := ts.Run(t, test.TestCase{ - Path: "/grpc-test-api/", - Method: http.MethodGet, - Code: http.StatusOK, + Path: "/grpc-test-api/", + Method: http.MethodGet, + Code: http.StatusOK, + Headers: headers, }) if err != nil { t.Fatalf("Request failed: %s", err.Error()) @@ -132,6 +219,15 @@ func TestGRPCDispatch(t *testing.T) { } }) + t.Run("Post Hook with metadata", func(t *testing.T) { + ts.Run(t, test.TestCase{ + Path: "/grpc-test-api-2/", + Method: http.MethodGet, + Code: http.StatusOK, + Headers: headers, + }) + }) + } func BenchmarkGRPCDispatch(b *testing.B) { @@ -139,14 +235,18 @@ func BenchmarkGRPCDispatch(b *testing.B) { defer ts.Close() defer grpcServer.Stop() + keyID := createSession(func(s *user.SessionState) {}) + headers := map[string]string{"authorization": keyID} + b.Run("Pre Hook with SetHeaders", func(b *testing.B) { path := "/grpc-test-api/" b.ReportAllocs() for i := 0; i < b.N; i++ { ts.Run(b, test.TestCase{ - Path: path, - Method: http.MethodGet, - Code: http.StatusOK, + Path: path, + Method: http.MethodGet, + Code: http.StatusOK, + Headers: headers, }) } }) diff --git a/coprocess_helpers.go b/coprocess_helpers.go index 64b8d9a3cd3..4361ec10a0b 100644 --- a/coprocess_helpers.go +++ b/coprocess_helpers.go @@ -5,6 +5,8 @@ package main import ( "encoding/json" + "github.com/Sirupsen/logrus" + "github.com/TykTechnologies/tyk/coprocess" "github.com/TykTechnologies/tyk/user" ) @@ -54,10 +56,9 @@ func TykSessionState(session *coprocess.SessionState) *user.SessionState { } metadata := make(map[string]interface{}) - if session.Metadata != "" { - err := json.Unmarshal([]byte(session.Metadata), &metadata) - if err != nil { - log.Error("Error interpreting metadata: ", err) + if session.Metadata != nil { + for k, v := range session.Metadata { + metadata[k] = v } } @@ -129,6 +130,25 @@ func ProtoSessionState(session *user.SessionState) *coprocess.SessionState { TriggerLimits: session.Monitor.TriggerLimits, } + metadata := make(map[string]string) + if len(session.MetaData) > 0 { + for k, v := range session.MetaData { + switch v.(type) { + case string: + metadata[k] = v.(string) + default: + jsonValue, err := json.Marshal(v) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": "coprocess", + }).WithError(err).Error("Couldn't encode session metadata") + continue + } + metadata[k] = string(jsonValue) + } + } + } + return &coprocess.SessionState{ LastCheck: session.LastCheck, Allowance: session.Allowance, @@ -152,6 +172,7 @@ func ProtoSessionState(session *user.SessionState) *coprocess.SessionState { ApplyPolicies: session.ApplyPolicies, DataExpires: session.DataExpires, Monitor: monitor, + Metadata: metadata, EnableDetailedRecording: session.EnableDetailedRecording, Tags: session.Tags, Alias: session.Alias, diff --git a/coprocess_python_test.go b/coprocess_python_test.go index 86bbcd5674c..77eaaf3ca75 100644 --- a/coprocess_python_test.go +++ b/coprocess_python_test.go @@ -9,6 +9,7 @@ import ( "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/test" + "github.com/TykTechnologies/tyk/user" ) var pythonBundleWithAuthCheck = map[string]string{ @@ -42,6 +43,51 @@ def MyAuthHook(request, session, metadata, spec): `, } +var pythonBundleWithPostHook = map[string]string{ + "manifest.json": ` + { + "file_list": [ + "middleware.py" + ], + "custom_middleware": { + "driver": "python", + "post": [{ + "name": "MyPostHook" + }] + } + } + `, + "middleware.py": ` +from tyk.decorators import * +from gateway import TykGateway as tyk +import json + +@Hook +def MyPostHook(request, session, spec): + print("called", session.metadata) + if "testkey" not in session.metadata.keys(): + request.object.return_overrides.response_code = 400 + request.object.return_overrides.response_error = "'testkey' not found in metadata" + return request, session + nested_data = json.loads(session.metadata["testkey"]) + if "nestedkey" not in nested_data: + request.object.return_overrides.response_code = 400 + request.object.return_overrides.response_error = "'nestedkey' not found in nested metadata" + return request, session + if "stringkey" not in session.metadata.keys(): + request.object.return_overrides.response_code = 400 + request.object.return_overrides.response_error = "'stringkey' not found in metadata" + return request, session + stringkey = session.metadata["stringkey"] + if stringkey != "testvalue": + request.object.return_overrides.response_code = 400 + request.object.return_overrides.response_error = "'stringkey' value doesn't match" + return request, session + return request, session + +`, +} + func TestPythonBundles(t *testing.T) { ts := newTykTestServer(tykTestServerConfig{ coprocessConfig: config.CoProcessConfig{ @@ -50,6 +96,7 @@ func TestPythonBundles(t *testing.T) { defer ts.Close() bundleID := registerBundle("python_with_auth_check", pythonBundleWithAuthCheck) + bundleID2 := registerBundle("python_with_post_hook", pythonBundleWithPostHook) t.Run("Single-file bundle with authentication hook", func(t *testing.T) { buildAndLoadAPI(func(spec *APISpec) { @@ -70,4 +117,30 @@ func TestPythonBundles(t *testing.T) { {Path: "/test-api/", Code: 403, Headers: invalidAuth}, }...) }) + + t.Run("Single-file bundle with post hook", func(t *testing.T) { + + keyID := createSession(func(s *user.SessionState) { + s.MetaData = map[string]interface{}{ + "testkey": map[string]interface{}{"nestedkey": "nestedvalue"}, + "stringkey": "testvalue", + } + }) + + buildAndLoadAPI(func(spec *APISpec) { + spec.Proxy.ListenPath = "/test-api-2/" + spec.UseKeylessAccess = false + spec.EnableCoProcessAuth = false + spec.CustomMiddlewareBundle = bundleID2 + spec.VersionData.NotVersioned = true + }) + + time.Sleep(1 * time.Second) + + auth := map[string]string{"Authorization": keyID} + + ts.Run(t, []test.TestCase{ + {Path: "/test-api-2/", Code: 200, Headers: auth}, + }...) + }) } diff --git a/mw_context_vars.go b/mw_context_vars.go index dfd111ec024..8e5aec4f575 100644 --- a/mw_context_vars.go +++ b/mw_context_vars.go @@ -23,37 +23,24 @@ func (m *MiddlewareContextVars) EnabledForSpec() bool { // ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail func (m *MiddlewareContextVars) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { + r.ParseForm() + + contextDataObject := map[string]interface{}{ + "request_data": r.Form, // Form params (map[string][]string) + "headers": map[string][]string(r.Header), + "headers_Host": r.Host, + "path_parts": strings.Split(r.URL.Path, "/"), // Path parts + "path": r.URL.Path, // path data + "remote_addr": request.RealIP(r), // IP + "request_id": uuid.NewV4().String(), //Correlation ID + } - copiedRequest := copyRequest(r) - contextDataObject := make(map[string]interface{}) - - copiedRequest.ParseForm() - - // Form params (map[string][]string) - contextDataObject["request_data"] = copiedRequest.Form - - contextDataObject["headers"] = map[string][]string(copiedRequest.Header) - - for hname, vals := range copiedRequest.Header { + for hname, vals := range r.Header { n := "headers_" + strings.Replace(hname, "-", "_", -1) contextDataObject[n] = vals[0] } - contextDataObject["headers_Host"] = copiedRequest.Host - - // Path parts - segmentedPathArray := strings.Split(copiedRequest.URL.Path, "/") - contextDataObject["path_parts"] = segmentedPathArray - - // path data - contextDataObject["path"] = copiedRequest.URL.Path - - // IP - contextDataObject["remote_addr"] = request.RealIP(copiedRequest) - - //Correlation ID - contextDataObject["request_id"] = uuid.NewV4().String() - for _, c := range copiedRequest.Cookies() { + for _, c := range r.Cookies() { name := "cookies_" + strings.Replace(c.Name, "-", "_", -1) contextDataObject[name] = c.Value } diff --git a/mw_context_vars_test.go b/mw_context_vars_test.go index 21d51b5c114..dd6871dfecc 100644 --- a/mw_context_vars_test.go +++ b/mw_context_vars_test.go @@ -1,16 +1,19 @@ package main import ( - "errors" + "io" "net/http" "net/http/httptest" + "net/url" + "reflect" + "strings" "testing" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/test" ) -func testPreparetContextVarsMiddleware() { +func testPrepareContextVarsMiddleware() { buildAndLoadAPI(func(spec *APISpec) { spec.Proxy.ListenPath = "/" spec.EnableContextVars = true @@ -32,7 +35,7 @@ func TestContextVarsMiddleware(t *testing.T) { ts := newTykTestServer() defer ts.Close() - testPreparetContextVarsMiddleware() + testPrepareContextVarsMiddleware() ts.Run(t, []test.TestCase{ {Path: "/test/path", Code: 200, BodyMatch: `"X-Remote-Addr":"127.0.0.1"`}, @@ -48,7 +51,7 @@ func BenchmarkContextVarsMiddleware(b *testing.B) { ts := newTykTestServer() defer ts.Close() - testPreparetContextVarsMiddleware() + testPrepareContextVarsMiddleware() for i := 0; i < b.N; i++ { ts.Run(b, []test.TestCase{ @@ -60,53 +63,180 @@ func BenchmarkContextVarsMiddleware(b *testing.B) { } } -func TestMiddlewareContextVars_ProcessRequest_cookies(t *testing.T) { - - req, _ := http.NewRequest(http.MethodGet, "/", nil) - res := httptest.NewRecorder() - - req.Header.Set("Cookie", "abc=123; def=456") - - err, code := (&MiddlewareContextVars{}).ProcessRequest(res, req, nil) - if err != nil { - t.Fatal(err) - } - - if code != http.StatusOK { - t.Fatal(errors.New("non 200 status code")) - } - - ctx := ctxGetData(req) - - if ctx["cookies_abc"].(string) != "123" { - t.Error("abc should be 123") - } +type testContextVarsData struct { + Method string + URL string + Data string + ExpectedCtxDataObject map[string]interface{} + Header http.Header +} - if ctx["cookies_def"].(string) != "456" { - t.Error("def should be 456") +func testPrepareTestContextVarsMiddleware() map[string]testContextVarsData { + return map[string]testContextVarsData{ + "GET with query string": { + Method: http.MethodGet, + URL: "http://abc.com/aaa/bbb/111/222?x=123&y=test", + Header: http.Header{ + "x-header-a": {"A"}, + "x-header-b": {"B"}, + "x-header-c": {"C"}, + }, + ExpectedCtxDataObject: map[string]interface{}{ + "remote_addr": "192.0.2.1", + "request_data": url.Values{ + "x": {"123"}, + "y": {"test"}, + }, + "headers": map[string][]string{ + "x-header-a": {"A"}, + "x-header-b": {"B"}, + "x-header-c": {"C"}, + }, + "headers_x_header_a": "A", + "headers_x_header_b": "B", + "headers_x_header_c": "C", + "headers_Host": "abc.com", + "path_parts": []string{"", "aaa", "bbb", "111", "222"}, + "path": "/aaa/bbb/111/222", + }, + }, + "POST with query string and encoded form data": { + Method: http.MethodPost, + URL: "http://abc.com/aaa/bbb/111/222?x=123&y=test", + Data: "i=1&j=2&str=abc", + Header: http.Header{ + "Content-Type": {"application/x-www-form-urlencoded"}, + "x-header-a": {"A"}, + "x-header-b": {"B"}, + "x-header-c": {"C"}, + }, + ExpectedCtxDataObject: map[string]interface{}{ + "remote_addr": "192.0.2.1", + "request_data": url.Values{ + "x": {"123"}, + "y": {"test"}, + "i": {"1"}, + "j": {"2"}, + "str": {"abc"}, + }, + "headers": map[string][]string{ + "x-header-a": {"A"}, + "x-header-b": {"B"}, + "x-header-c": {"C"}, + "Content-Type": {"application/x-www-form-urlencoded"}, + }, + "headers_x_header_a": "A", + "headers_x_header_b": "B", + "headers_x_header_c": "C", + "headers_Content_Type": "application/x-www-form-urlencoded", + "headers_Host": "abc.com", + "path_parts": []string{"", "aaa", "bbb", "111", "222"}, + "path": "/aaa/bbb/111/222", + }, + }, + "POST with query string and encoded form data and cookies": { + Method: http.MethodPost, + URL: "http://abc.com/aaa/bbb/111/222?x=123&y=test", + Data: "i=1&j=2&str=abc", + Header: http.Header{ + "Content-Type": {"application/x-www-form-urlencoded"}, + "Cookie": {"c-1=cookie1;c-2=cookie2"}, + "x-header-a": {"A"}, + "x-header-b": {"B"}, + "x-header-c": {"C"}, + }, + ExpectedCtxDataObject: map[string]interface{}{ + "remote_addr": "192.0.2.1", + "request_data": url.Values{ + "x": {"123"}, + "y": {"test"}, + "i": {"1"}, + "j": {"2"}, + "str": {"abc"}, + }, + "headers": map[string][]string{ + "x-header-a": {"A"}, + "x-header-b": {"B"}, + "x-header-c": {"C"}, + "Content-Type": {"application/x-www-form-urlencoded"}, + "Cookie": {"c-1=cookie1;c-2=cookie2"}, + }, + "headers_x_header_a": "A", + "headers_x_header_b": "B", + "headers_x_header_c": "C", + "headers_Content_Type": "application/x-www-form-urlencoded", + "headers_Cookie": "c-1=cookie1;c-2=cookie2", + "headers_Host": "abc.com", + "cookies_c_1": "cookie1", + "cookies_c_2": "cookie2", + "path_parts": []string{"", "aaa", "bbb", "111", "222"}, + "path": "/aaa/bbb/111/222", + }, + }, } +} - if ctx["cookies_ghi"] != nil { - t.Error("ghi should be nil") +func TestContextVarsMiddlewareProcessRequest(t *testing.T) { + mw := &MiddlewareContextVars{} + + tests := testPrepareTestContextVarsMiddleware() + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + var bodyReader io.Reader + if test.Data != "" { + bodyReader = strings.NewReader(test.Data) + } + req := httptest.NewRequest(test.Method, test.URL, bodyReader) + req.Header = test.Header + err, code := mw.ProcessRequest(nil, req, nil) + if err != nil { + t.Error(err) + } + if code != http.StatusOK { + t.Errorf("Wrong response code: %d Eexpected 200.", code) + } + + ctxDataObject := ctxGetData(req) + + // check request_id + if _, ok := ctxDataObject["request_id"].(string); !ok { + t.Error("Missing 'request_id' field") + } + + // delete request_if to do DeepEqual + delete(ctxDataObject, "request_id") + + if !reflect.DeepEqual(ctxDataObject, test.ExpectedCtxDataObject) { + t.Errorf("Expected: %v\n Got: %v\n", test.ExpectedCtxDataObject, ctxDataObject) + } + }) } } -func BenchmarkMiddlewareContextVars_ProcessRequest_cookies(b *testing.B) { - b.ReportAllocs() - - req, _ := http.NewRequest(http.MethodGet, "/", nil) - res := httptest.NewRecorder() - - req.Header.Set("Cookie", "abc=123; def=456") - - for i := 0; i < b.N; i++ { - err, code := (&MiddlewareContextVars{}).ProcessRequest(res, req, nil) - if err != nil { - b.Fatal(err) - } - if code != http.StatusOK { - b.Fatal(errors.New("non 200 status code")) - } +func BenchmarkContextVarsMiddlewareProcessRequest(b *testing.B) { + mw := &MiddlewareContextVars{} + tests := testPrepareTestContextVarsMiddleware() + var err error + var code int + for name, test := range tests { + b.Run(name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var bodyReader io.Reader + if test.Data != "" { + bodyReader = strings.NewReader(test.Data) + } + req := httptest.NewRequest(test.Method, test.URL, bodyReader) + req.Header = test.Header + err, code = mw.ProcessRequest(nil, req, nil) + if err != nil { + b.Error(err) + } + if code != http.StatusOK { + b.Errorf("Wrong response code: %d Eexpected 200.", code) + } + } + }) } - }