diff --git a/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go b/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go index 25abacc35..91295cdc6 100644 --- a/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go +++ b/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go @@ -544,10 +544,13 @@ type Auth struct { unknownFields protoimpl.UnknownFields // Authentication creates a JWT that uses this secret for signing - GeneratedJwsHmacSecret string `protobuf:"bytes,2,opt,name=generated_jws_hmac_secret,json=generatedJwsHmacSecret,proto3" json:"generated_jws_hmac_secret,omitempty"` - AllowList *v11.AllowList `protobuf:"bytes,3,opt,name=allow_list,json=allowList,proto3" json:"allow_list,omitempty"` - CasRobotAccountPrivateKeyPath string `protobuf:"bytes,4,opt,name=cas_robot_account_private_key_path,json=casRobotAccountPrivateKeyPath,proto3" json:"cas_robot_account_private_key_path,omitempty"` - Oidc *Auth_OIDC `protobuf:"bytes,6,opt,name=oidc,proto3" json:"oidc,omitempty"` + GeneratedJwsHmacSecret string `protobuf:"bytes,2,opt,name=generated_jws_hmac_secret,json=generatedJwsHmacSecret,proto3" json:"generated_jws_hmac_secret,omitempty"` + // Additional HMAC verification keys for the JWT + // useful for key rotation + AdditionalHmacVerificationKeys []string `protobuf:"bytes,8,rep,name=additional_hmac_verification_keys,json=additionalHmacVerificationKeys,proto3" json:"additional_hmac_verification_keys,omitempty"` + AllowList *v11.AllowList `protobuf:"bytes,3,opt,name=allow_list,json=allowList,proto3" json:"allow_list,omitempty"` + CasRobotAccountPrivateKeyPath string `protobuf:"bytes,4,opt,name=cas_robot_account_private_key_path,json=casRobotAccountPrivateKeyPath,proto3" json:"cas_robot_account_private_key_path,omitempty"` + Oidc *Auth_OIDC `protobuf:"bytes,6,opt,name=oidc,proto3" json:"oidc,omitempty"` // Generates an initial user. Use only for development purposes DevUser string `protobuf:"bytes,7,opt,name=dev_user,json=devUser,proto3" json:"dev_user,omitempty"` } @@ -591,6 +594,13 @@ func (x *Auth) GetGeneratedJwsHmacSecret() string { return "" } +func (x *Auth) GetAdditionalHmacVerificationKeys() []string { + if x != nil { + return x.AdditionalHmacVerificationKeys + } + return nil +} + func (x *Auth) GetAllowList() *v11.AllowList { if x != nil { return x.AllowList @@ -1802,89 +1812,93 @@ var file_controlplane_config_v1_conf_proto_rawDesc = []byte{ 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x49, 0x64, 0x6c, 0x65, - 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb1, 0x03, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x39, 0x0a, + 0x54, 0x69, 0x6d, 0x65, 0x22, 0xfc, 0x03, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x39, 0x0a, 0x19, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6a, 0x77, 0x73, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4a, 0x77, 0x73, 0x48, 0x6d, - 0x61, 0x63, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x22, 0x63, 0x61, - 0x73, 0x5f, 0x72, 0x6f, 0x62, 0x6f, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x63, 0x61, 0x73, 0x52, 0x6f, 0x62, 0x6f, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, - 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x35, 0x0a, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, - 0x68, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x52, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x12, 0x19, 0x0a, 0x08, - 0x64, 0x65, 0x76, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x64, 0x65, 0x76, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x8e, 0x01, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, - 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, - 0x67, 0x69, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x72, 0x6c, - 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x22, 0x57, 0x0a, 0x03, 0x54, 0x53, 0x41, 0x12, - 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, - 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, - 0x75, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, - 0x72, 0x22, 0xe4, 0x04, 0x0a, 0x02, 0x43, 0x41, 0x12, 0x3c, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x63, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x41, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x41, 0x48, 0x00, 0x52, 0x06, - 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, 0x12, 0x3d, 0x0a, 0x08, 0x65, 0x6a, 0x62, 0x63, 0x61, 0x5f, - 0x63, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x41, 0x2e, 0x45, 0x4a, 0x42, 0x43, 0x41, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6a, - 0x62, 0x63, 0x61, 0x43, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x1a, 0x5b, 0x0a, - 0x06, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x41, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x65, 0x72, 0x74, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x65, 0x72, 0x74, - 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, - 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x1a, 0xe5, 0x02, 0x0a, 0x05, 0x45, - 0x4a, 0x42, 0x43, 0x41, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x75, - 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x22, 0x0a, 0x08, - 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, - 0x12, 0x24, 0x0a, 0x09, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x63, 0x65, - 0x72, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, - 0x61, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x6f, - 0x6f, 0x74, 0x43, 0x61, 0x50, 0x61, 0x74, 0x68, 0x12, 0x41, 0x0a, 0x18, 0x63, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x16, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x17, 0x65, - 0x6e, 0x64, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x14, 0x65, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x1a, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x18, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x4e, 0x61, - 0x6d, 0x65, 0x42, 0x04, 0x0a, 0x02, 0x63, 0x61, 0x22, 0x3f, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x6d, - 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x22, 0x0a, 0x08, 0x6f, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x07, 0x6f, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x5f, 0x5a, 0x5d, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, - 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, - 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x2f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x61, 0x63, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x49, 0x0a, 0x21, 0x61, 0x64, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x08, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x1e, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x48, + 0x6d, 0x61, 0x63, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, + 0x65, 0x79, 0x73, 0x12, 0x40, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x69, 0x73, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x22, 0x63, 0x61, 0x73, 0x5f, 0x72, 0x6f, 0x62, + 0x6f, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x1d, 0x63, 0x61, 0x73, 0x52, 0x6f, 0x62, 0x6f, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, + 0x12, 0x35, 0x0a, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x4f, 0x49, 0x44, + 0x43, 0x52, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x75, + 0x73, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x76, 0x55, 0x73, + 0x65, 0x72, 0x1a, 0x8e, 0x01, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x12, 0x16, 0x0a, 0x06, 0x64, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x75, + 0x72, 0x6c, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x72, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x72, + 0x69, 0x64, 0x65, 0x22, 0x57, 0x0a, 0x03, 0x54, 0x53, 0x41, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0f, + 0x63, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x22, 0xe4, 0x04, 0x0a, + 0x02, 0x43, 0x41, 0x12, 0x3c, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x41, + 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x41, 0x48, 0x00, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x43, + 0x61, 0x12, 0x3d, 0x0a, 0x08, 0x65, 0x6a, 0x62, 0x63, 0x61, 0x5f, 0x63, 0x61, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x41, 0x2e, + 0x45, 0x4a, 0x42, 0x43, 0x41, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6a, 0x62, 0x63, 0x61, 0x43, 0x61, + 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x1a, 0x5b, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x65, + 0x43, 0x41, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, + 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, + 0x79, 0x50, 0x61, 0x73, 0x73, 0x1a, 0xe5, 0x02, 0x0a, 0x05, 0x45, 0x4a, 0x42, 0x43, 0x41, 0x12, + 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x22, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x09, 0x63, + 0x65, 0x72, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x63, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, + 0x68, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x61, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x61, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x41, 0x0a, 0x18, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x16, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x17, 0x65, 0x6e, 0x64, 0x5f, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x14, 0x65, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x1a, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x18, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x04, 0x0a, + 0x02, 0x63, 0x61, 0x22, 0x3f, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, + 0x73, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, + 0x12, 0x22, 0x0a, 0x08, 0x6f, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6f, 0x72, 0x67, + 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x5f, 0x5a, 0x5d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, + 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x76, 0x31, + 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/controlplane/internal/conf/controlplane/config/v1/conf.proto b/app/controlplane/internal/conf/controlplane/config/v1/conf.proto index 8d1c711cc..3af67e918 100644 --- a/app/controlplane/internal/conf/controlplane/config/v1/conf.proto +++ b/app/controlplane/internal/conf/controlplane/config/v1/conf.proto @@ -180,6 +180,9 @@ message Data { message Auth { // Authentication creates a JWT that uses this secret for signing string generated_jws_hmac_secret = 2; + // Additional HMAC verification keys for the JWT + // useful for key rotation + repeated string additional_hmac_verification_keys = 8; AllowList allow_list = 3; string cas_robot_account_private_key_path = 4; OIDC oidc = 6; diff --git a/app/controlplane/internal/server/grpc.go b/app/controlplane/internal/server/grpc.go index 75a0a62e3..143c446fa 100644 --- a/app/controlplane/internal/server/grpc.go +++ b/app/controlplane/internal/server/grpc.go @@ -24,23 +24,20 @@ import ( v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" conf "github.com/chainloop-dev/chainloop/app/controlplane/internal/conf/controlplane/config/v1" "github.com/chainloop-dev/chainloop/app/controlplane/internal/sentrycontext" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz" authzMiddleware "github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz/middleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" - "github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/user" "github.com/bufbuild/protovalidate-go" "github.com/chainloop-dev/chainloop/app/controlplane/internal/service" "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext" "github.com/chainloop-dev/chainloop/pkg/credentials" "github.com/getsentry/sentry-go" - "github.com/golang-jwt/jwt/v4" "github.com/go-kratos/kratos/v2/errors" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/middleware" - jwtMiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt" "github.com/go-kratos/kratos/v2/middleware/logging" "github.com/go-kratos/kratos/v2/middleware/recovery" "github.com/go-kratos/kratos/v2/middleware/selector" @@ -170,6 +167,18 @@ func craftMiddleware(opts *Opts) []middleware.Middleware { logHelper := log.NewHelper(opts.Logger) + // List of providers to verify the JWT + // We support user and API token verification + jwtVerificationProviders := []multijwtmiddleware.JWTOption{ + multijwtmiddleware.NewUserTokenProvider(opts.AuthConfig.GeneratedJwsHmacSecret), + multijwtmiddleware.NewAPITokenProvider(opts.AuthConfig.GeneratedJwsHmacSecret), + } + + // any additional verification keys will be used to verify the user token + for _, key := range opts.AuthConfig.AdditionalHmacVerificationKeys { + jwtVerificationProviders = append(jwtVerificationProviders, multijwtmiddleware.NewUserTokenProvider(key), multijwtmiddleware.NewAPITokenProvider(key)) + } + // User authentication middlewares = append(middlewares, usercontext.Prometheus(), @@ -177,10 +186,9 @@ func craftMiddleware(opts *Opts) []middleware.Middleware { selector.Server( // 1 - Extract the currentUser/API token from the JWT // NOTE: this works because both currentUser and API tokens JWT use the same signing method and secret - jwtMiddleware.Server(func(_ *jwt.Token) (interface{}, error) { - return []byte(opts.AuthConfig.GeneratedJwsHmacSecret), nil - }, - jwtMiddleware.WithSigningMethod(user.SigningMethod), + multijwtmiddleware.WithJWTMulti( + opts.Logger, + jwtVerificationProviders..., ), // 2.a - Set its API token and organization as alternative to the user usercontext.WithCurrentAPITokenAndOrgMiddleware(opts.APITokenUseCase, opts.OrganizationUseCase, logHelper), @@ -207,24 +215,16 @@ func craftMiddleware(opts *Opts) []middleware.Middleware { // if we require a robot account selector.Server( // 1 - Extract information from the JWT by using the claims - attjwtmiddleware.WithJWTMulti( + multijwtmiddleware.WithJWTMulti( opts.Logger, - // Robot account provider - attjwtmiddleware.NewRobotAccountProvider(opts.AuthConfig.GeneratedJwsHmacSecret), - // API Token provider - attjwtmiddleware.NewAPITokenProvider(opts.AuthConfig.GeneratedJwsHmacSecret), - // User Token provider - attjwtmiddleware.NewUserTokenProvider(opts.AuthConfig.GeneratedJwsHmacSecret), - // Delegated Federated provider - attjwtmiddleware.WithFederatedProvider(opts.FederatedConfig), + // It also supports federated delegation + append(jwtVerificationProviders, multijwtmiddleware.WithFederatedProvider(opts.FederatedConfig))..., ), - // 2.a - Set its workflow and organization in the context - usercontext.WithAttestationContextFromRobotAccount(opts.RobotAccountUseCase, opts.OrganizationUseCase, logHelper), - // 2.b - Set its API token and Robot Account as alternative to the user - usercontext.WithAttestationContextFromAPIToken(opts.APITokenUseCase, opts.OrganizationUseCase, logHelper), - // 2.c - Set Attestation context from user token + // 2.a - Set its API token and Robot Account as alternative to the user + usercontext.WithCurrentAPITokenAndOrgMiddleware(opts.APITokenUseCase, opts.OrganizationUseCase, logHelper), + // 2.b - Set Attestation context from user token usercontext.WithAttestationContextFromUser(opts.UserUseCase, logHelper), - // 2.d - Set its robot account from federated delegation + // 2.c - Set its robot account from federated delegation usercontext.WithAttestationContextFromFederatedInfo(opts.OrganizationUseCase, logHelper), ).Match(requireRobotAccountMatcher()).Build(), ) diff --git a/app/controlplane/internal/service/attestation.go b/app/controlplane/internal/service/attestation.go index 047185d42..802980b9b 100644 --- a/app/controlplane/internal/service/attestation.go +++ b/app/controlplane/internal/service/attestation.go @@ -26,7 +26,7 @@ import ( cpAPI "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" "github.com/chainloop-dev/chainloop/app/controlplane/internal/dispatcher" "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" casJWT "github.com/chainloop-dev/chainloop/internal/robotaccount/cas" "github.com/chainloop-dev/chainloop/pkg/attestation" @@ -98,7 +98,7 @@ func NewAttestationService(opts *NewAttestationServiceOpts) *AttestationService } func (s *AttestationService) GetContract(ctx context.Context, req *cpAPI.AttestationServiceGetContractRequest) (*cpAPI.AttestationServiceGetContractResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "neither robot account nor API token found") } @@ -129,7 +129,7 @@ func (s *AttestationService) GetContract(ctx context.Context, req *cpAPI.Attesta } func (s *AttestationService) Init(ctx context.Context, req *cpAPI.AttestationServiceInitRequest) (*cpAPI.AttestationServiceInitResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "neither robot account nor API token found") } @@ -201,7 +201,7 @@ func (s *AttestationService) Init(ctx context.Context, req *cpAPI.AttestationSer } func (s *AttestationService) Store(ctx context.Context, req *cpAPI.AttestationServiceStoreRequest) (*cpAPI.AttestationServiceStoreResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "robot account not found") } @@ -243,7 +243,7 @@ func (s *AttestationService) Store(ctx context.Context, req *cpAPI.AttestationSe } // Stores and process a DSSE Envelope with a Chainloop attestation -func (s *AttestationService) storeAttestation(ctx context.Context, envelope []byte, bundle []byte, robotAccount *usercontext.RobotAccount, wf *biz.Workflow, wfRun *biz.WorkflowRun, markAsReleased *bool) (*v1.Hash, error) { +func (s *AttestationService) storeAttestation(ctx context.Context, envelope []byte, bundle []byte, robotAccount *usercontext.AttAuth, wf *biz.Workflow, wfRun *biz.WorkflowRun, markAsReleased *bool) (*v1.Hash, error) { workflowRunID := wfRun.ID.String() casBackend := wfRun.CASBackends[0] @@ -341,7 +341,7 @@ func (s *AttestationService) storeAttestation(ctx context.Context, envelope []by } func (s *AttestationService) Cancel(ctx context.Context, req *cpAPI.AttestationServiceCancelRequest) (*cpAPI.AttestationServiceCancelResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "robot account not found") } @@ -381,7 +381,7 @@ func (s *AttestationService) Cancel(ctx context.Context, req *cpAPI.AttestationS // There is another endpoint to get credentials via casCredentialsService.Get // This one is kept since it leverages robot-accounts in the context of a workflow func (s *AttestationService) GetUploadCreds(ctx context.Context, req *cpAPI.AttestationServiceGetUploadCredsRequest) (*cpAPI.AttestationServiceGetUploadCredsResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "robot account not found") } @@ -419,7 +419,7 @@ func (s *AttestationService) GetUploadCreds(ctx context.Context, req *cpAPI.Atte } func (s *AttestationService) GetPolicy(ctx context.Context, req *cpAPI.AttestationServiceGetPolicyRequest) (*cpAPI.AttestationServiceGetPolicyResponse, error) { - token, ok := attjwtmiddleware.FromJWTAuthContext(ctx) + token, ok := multijwtmiddleware.FromJWTAuthContext(ctx) if !ok { return nil, errors.Forbidden("forbidden", "token not found") } @@ -441,7 +441,7 @@ func (s *AttestationService) GetPolicy(ctx context.Context, req *cpAPI.Attestati } func (s *AttestationService) GetPolicyGroup(ctx context.Context, req *cpAPI.AttestationServiceGetPolicyGroupRequest) (*cpAPI.AttestationServiceGetPolicyGroupResponse, error) { - token, ok := attjwtmiddleware.FromJWTAuthContext(ctx) + token, ok := multijwtmiddleware.FromJWTAuthContext(ctx) if !ok { return nil, errors.Forbidden("forbidden", "token not found") } @@ -623,13 +623,13 @@ func (s *AttestationService) findWorkflowFromTokenOrNameOrRunID(ctx context.Cont return nil, biz.NewErrValidationStr("workflowName or workflowRunId must be provided") } -func checkAuthRequirements(attToken *usercontext.RobotAccount, workflowName string) error { +func checkAuthRequirements(attToken *usercontext.AttAuth, workflowName string) error { if attToken == nil { return errors.Forbidden("forbidden", "authentication not found") } // For API tokens we do not support explicit workflowName. It is inside the token - if attToken.ProviderKey == attjwtmiddleware.APITokenProviderKey && workflowName == "" { + if attToken.ProviderKey == multijwtmiddleware.APITokenProviderKey && workflowName == "" { return errors.BadRequest("bad request", "when using an API Token, workflow name is required as parameter") } @@ -637,7 +637,7 @@ func checkAuthRequirements(attToken *usercontext.RobotAccount, workflowName stri } func (s *AttestationService) FindOrCreateWorkflow(ctx context.Context, req *cpAPI.FindOrCreateWorkflowRequest) (*cpAPI.FindOrCreateWorkflowResponse, error) { - apiToken := usercontext.CurrentRobotAccount(ctx) + apiToken := usercontext.CurrentAttestationAuth(ctx) if apiToken == nil { return nil, errors.NotFound("not found", "neither robot account nor API token found") } diff --git a/app/controlplane/internal/service/attestationstate.go b/app/controlplane/internal/service/attestationstate.go index 28bd19ee9..0057181fe 100644 --- a/app/controlplane/internal/service/attestationstate.go +++ b/app/controlplane/internal/service/attestationstate.go @@ -24,7 +24,7 @@ import ( cpAPI "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" errors "github.com/go-kratos/kratos/v2/errors" @@ -57,7 +57,7 @@ func NewAttestationStateService(opts *NewAttestationStateServiceOpt) *Attestatio } func (s *AttestationStateService) Initialized(ctx context.Context, req *cpAPI.AttestationStateServiceInitializedRequest) (*cpAPI.AttestationStateServiceInitializedResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "robot account not found") } @@ -79,7 +79,7 @@ func (s *AttestationStateService) Initialized(ctx context.Context, req *cpAPI.At } func (s *AttestationStateService) Save(ctx context.Context, req *cpAPI.AttestationStateServiceSaveRequest) (*cpAPI.AttestationStateServiceSaveResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "robot account not found") } @@ -107,7 +107,7 @@ func (s *AttestationStateService) Save(ctx context.Context, req *cpAPI.Attestati } func (s *AttestationStateService) Read(ctx context.Context, req *cpAPI.AttestationStateServiceReadRequest) (*cpAPI.AttestationStateServiceReadResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "robot account not found") } @@ -136,7 +136,7 @@ func (s *AttestationStateService) Read(ctx context.Context, req *cpAPI.Attestati } func (s *AttestationStateService) Reset(ctx context.Context, req *cpAPI.AttestationStateServiceResetRequest) (*cpAPI.AttestationStateServiceResetResponse, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return nil, errors.NotFound("not found", "robot account not found") } @@ -159,12 +159,12 @@ func (s *AttestationStateService) Reset(ctx context.Context, req *cpAPI.Attestat // NOTE: Using the robot-account as JWT is not ideal but it's a start // TODO: look into using some identifier from the actual client like machine-uuid func encryptionPassphrase(ctx context.Context) (string, error) { - robotAccount := usercontext.CurrentRobotAccount(ctx) + robotAccount := usercontext.CurrentAttestationAuth(ctx) if robotAccount == nil { return "", errors.NotFound("not found", "robot account not found") // If we are using a federated provider, we'll use the provider key as the passphrase since we can not guarantee the stability of the token // In practice this means disabling the state encryption at rest but this state in practice is a subset of the resulting attestation that we end up storing - } else if robotAccount.ProviderKey == attjwtmiddleware.FederatedProviderKey { + } else if robotAccount.ProviderKey == multijwtmiddleware.FederatedProviderKey { return robotAccount.ProviderKey, nil } diff --git a/app/controlplane/internal/service/prometheus.go b/app/controlplane/internal/service/prometheus.go index 52ed3a42c..0b86d3034 100644 --- a/app/controlplane/internal/service/prometheus.go +++ b/app/controlplane/internal/service/prometheus.go @@ -19,10 +19,10 @@ import ( "fmt" "net/http" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/apitoken" - jwtmiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt" "github.com/gorilla/mux" "github.com/prometheus/common/expfmt" ) @@ -60,13 +60,13 @@ func (p *PrometheusService) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Extracts the organization name from the request - rawClaims, ok := jwtmiddleware.FromContext(r.Context()) + rawClaims, ok := multijwtmiddleware.FromJWTAuthContext(r.Context()) if !ok { http.Error(w, "Error extracting claims from context", http.StatusInternalServerError) return } - apiTokenClaims, ok := rawClaims.(*apitoken.CustomClaims) + apiTokenClaims, ok := rawClaims.Claims.(*apitoken.CustomClaims) if !ok { http.Error(w, "Error extracting API Token claims", http.StatusInternalServerError) return diff --git a/app/controlplane/internal/service/signing.go b/app/controlplane/internal/service/signing.go index 7f1c77a3a..3bdb5996b 100644 --- a/app/controlplane/internal/service/signing.go +++ b/app/controlplane/internal/service/signing.go @@ -41,7 +41,7 @@ func NewSigningService(signing *biz.SigningUseCase, opts ...NewOpt) *SigningServ } func (s *SigningService) GenerateSigningCert(ctx context.Context, req *v1.GenerateSigningCertRequest) (*v1.GenerateSigningCertResponse, error) { - ra := usercontext.CurrentRobotAccount(ctx) + ra := usercontext.CurrentAttestationAuth(ctx) if ra == nil { return nil, errors.Unauthorized("missing org", "authentication data is required") } diff --git a/app/controlplane/internal/usercontext/apitoken_middleware.go b/app/controlplane/internal/usercontext/apitoken_middleware.go index 226aa4b01..788f91b91 100644 --- a/app/controlplane/internal/usercontext/apitoken_middleware.go +++ b/app/controlplane/internal/usercontext/apitoken_middleware.go @@ -21,15 +21,13 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" - "github.com/golang-jwt/jwt/v4" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/apitoken" "github.com/go-kratos/kratos/v2/middleware" - jwtMiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt" ) // Store the authorization subject @@ -47,48 +45,11 @@ func CurrentAuthzSubject(ctx context.Context) string { type currentAuthzSubjectKey struct{} -// Middleware that injects the API-Token + organization to the context +// WithCurrentAPITokenAndOrgMiddleware injects the API-Token, organization + robot account to the context func WithCurrentAPITokenAndOrgMiddleware(apiTokenUC *biz.APITokenUseCase, orgUC *biz.OrganizationUseCase, logger *log.Helper) middleware.Middleware { return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (interface{}, error) { - rawClaims, ok := jwtMiddleware.FromContext(ctx) - // If not found means that there is no currentUser set in the context - if !ok { - logger.Warn("couldn't extract org/user, JWT parser middleware not running before this one?") - return nil, errors.New("can't extract JWT info from the context") - } - - genericClaims, ok := rawClaims.(jwt.MapClaims) - if !ok { - return nil, errors.New("error mapping the claims") - } - - // We've received an API-token - if genericClaims.VerifyAudience(apitoken.Audience, true) { - var err error - tokenID, ok := genericClaims["jti"].(string) - if !ok || tokenID == "" { - return nil, errors.New("error mapping the API-token claims") - } - - ctx, err = setCurrentOrgAndAPIToken(ctx, apiTokenUC, orgUC, tokenID) - if err != nil { - return nil, fmt.Errorf("error setting current org and user: %w", err) - } - - logger.Infow("msg", "[authN] processed credentials", "id", tokenID, "type", "API-token") - } - - return handler(ctx, req) - } - } -} - -// WithAttestationContextFromAPIToken injects the API-Token, organization + robot account to the context -func WithAttestationContextFromAPIToken(apiTokenUC *biz.APITokenUseCase, orgUC *biz.OrganizationUseCase, logger *log.Helper) middleware.Middleware { - return func(handler middleware.Handler) middleware.Handler { - return func(ctx context.Context, req interface{}) (interface{}, error) { - authInfo, ok := attjwtmiddleware.FromJWTAuthContext(ctx) + authInfo, ok := multijwtmiddleware.FromJWTAuthContext(ctx) // If not found means that there is no currentUser set in the context if !ok { logger.Warn("couldn't extract org/user, JWT parser middleware not running before this one?") @@ -96,7 +57,7 @@ func WithAttestationContextFromAPIToken(apiTokenUC *biz.APITokenUseCase, orgUC * } // If the token is not an API token, we don't need to do anything - if authInfo.ProviderKey != attjwtmiddleware.APITokenProviderKey { + if authInfo.ProviderKey != multijwtmiddleware.APITokenProviderKey { return handler(ctx, req) } @@ -151,7 +112,7 @@ func setRobotAccountFromAPIToken(ctx context.Context, apiTokenUC *biz.APITokenUs return nil, errors.New("API token revoked") } - ctx = withRobotAccount(ctx, &RobotAccount{OrgID: token.OrganizationID.String(), ProviderKey: attjwtmiddleware.APITokenProviderKey}) + ctx = withAPIToken(ctx, &AttAuth{OrgID: token.OrganizationID.String(), ProviderKey: multijwtmiddleware.APITokenProviderKey}) return ctx, nil } @@ -194,3 +155,22 @@ func setCurrentOrgAndAPIToken(ctx context.Context, apiTokenUC *biz.APITokenUseCa return ctx, nil } + +type AttAuth struct { + ID, WorkflowID, OrgID, ProviderKey string +} + +func withAPIToken(ctx context.Context, acc *AttAuth) context.Context { + return context.WithValue(ctx, currentRobotAccountCtxKey{}, acc) +} + +func CurrentAttestationAuth(ctx context.Context) *AttAuth { + res := ctx.Value(currentRobotAccountCtxKey{}) + if res == nil { + return nil + } + + return res.(*AttAuth) +} + +type currentRobotAccountCtxKey struct{} diff --git a/app/controlplane/internal/usercontext/currentuser_middleware.go b/app/controlplane/internal/usercontext/currentuser_middleware.go index dfbb9d09a..93e7b6e88 100644 --- a/app/controlplane/internal/usercontext/currentuser_middleware.go +++ b/app/controlplane/internal/usercontext/currentuser_middleware.go @@ -23,27 +23,30 @@ import ( "github.com/go-kratos/kratos/v2/log" "github.com/golang-jwt/jwt/v4" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/user" "github.com/go-kratos/kratos/v2/middleware" - jwtMiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt" ) // Middleware that injects the current user + organization to the context func WithCurrentUserMiddleware(userUseCase biz.UserOrgFinder, logger *log.Helper) middleware.Middleware { return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (interface{}, error) { - rawClaims, ok := jwtMiddleware.FromContext(ctx) + authInfo, ok := multijwtmiddleware.FromJWTAuthContext(ctx) // If not found means that there is no currentUser set in the context if !ok { logger.Warn("couldn't extract user, JWT parser middleware not running before this one?") return nil, errors.New("can't extract JWT info from the context") } - genericClaims, ok := rawClaims.(jwt.MapClaims) + if authInfo.ProviderKey != multijwtmiddleware.UserTokenProviderKey { + return handler(ctx, req) + } + + genericClaims, ok := authInfo.Claims.(jwt.MapClaims) if !ok { return nil, errors.New("error mapping the claims") } @@ -93,19 +96,17 @@ func WithAttestationContextFromUser(userUC *biz.UserUseCase, logger *log.Helper) return func(ctx context.Context, req interface{}) (interface{}, error) { // If the token is not an user token, we don't need to do anything // note that this middleware is called by a multi-middleware that works in cascade mode - authInfo, ok := attjwtmiddleware.FromJWTAuthContext(ctx) + authInfo, ok := multijwtmiddleware.FromJWTAuthContext(ctx) // If not found means that there is no currentUser set in the context if !ok { logger.Warn("couldn't extract org/user, JWT parser middleware not running before this one?") return nil, errors.New("can't extract JWT info from the context") } - if authInfo.ProviderKey != attjwtmiddleware.UserTokenProviderKey { + if authInfo.ProviderKey != multijwtmiddleware.UserTokenProviderKey { return handler(ctx, req) } - // set the raw claims in the default context field so the user middleware can understand it - ctx = jwtMiddleware.NewContext(ctx, authInfo.Claims) // We received a user token during the attestation process // 1 - Set the current user from the user token // 2 - Set the current organization for the user token from the DB, header or default @@ -132,8 +133,8 @@ func WithAttestationContextFromUser(userUC *biz.UserUseCase, logger *log.Helper) return nil, fmt.Errorf("your user doesn't have permissions to perform attestations in this organization, role=%s, orgID=%s", subject, org.ID) } - ctx = withRobotAccount(ctx, &RobotAccount{OrgID: org.ID, ProviderKey: attjwtmiddleware.UserTokenProviderKey}) - logger.Infow("msg", "[authN] processed credentials", "type", attjwtmiddleware.UserTokenProviderKey) + ctx = withAPIToken(ctx, &AttAuth{OrgID: org.ID, ProviderKey: multijwtmiddleware.UserTokenProviderKey}) + logger.Infow("msg", "[authN] processed credentials", "type", multijwtmiddleware.UserTokenProviderKey) return handler(ctx, req) })(ctx, req) diff --git a/app/controlplane/internal/usercontext/federated_middleware.go b/app/controlplane/internal/usercontext/federated_middleware.go index 4d532fe5a..513dfd4e4 100644 --- a/app/controlplane/internal/usercontext/federated_middleware.go +++ b/app/controlplane/internal/usercontext/federated_middleware.go @@ -23,8 +23,8 @@ import ( "github.com/go-kratos/kratos/v2/log" "github.com/golang-jwt/jwt/v4" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/go-kratos/kratos/v2/middleware" ) @@ -32,7 +32,7 @@ import ( func WithAttestationContextFromFederatedInfo(orgUC *biz.OrganizationUseCase, logger *log.Helper) middleware.Middleware { return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (interface{}, error) { - authInfo, ok := attjwtmiddleware.FromJWTAuthContext(ctx) + authInfo, ok := multijwtmiddleware.FromJWTAuthContext(ctx) // If not found means that there is no currentUser set in the context if !ok { logger.Warn("couldn't extract org/user, JWT parser middleware not running before this one?") @@ -40,7 +40,7 @@ func WithAttestationContextFromFederatedInfo(orgUC *biz.OrganizationUseCase, log } // If the token is not an API token, we don't need to do anything - if authInfo.ProviderKey != attjwtmiddleware.FederatedProviderKey { + if authInfo.ProviderKey != multijwtmiddleware.FederatedProviderKey { return handler(ctx, req) } @@ -51,7 +51,7 @@ func WithAttestationContextFromFederatedInfo(orgUC *biz.OrganizationUseCase, log orgID := (*claims)["orgId"].(string) - ctx = withRobotAccount(ctx, &RobotAccount{OrgID: orgID, ProviderKey: attjwtmiddleware.FederatedProviderKey}) + ctx = withAPIToken(ctx, &AttAuth{OrgID: orgID, ProviderKey: multijwtmiddleware.FederatedProviderKey}) // Find the associated organization org, err := orgUC.FindByID(ctx, orgID) if err != nil { diff --git a/app/controlplane/internal/usercontext/attjwtmiddleware/attmiddleware.go b/app/controlplane/internal/usercontext/multijwtmiddleware/multijwtmiddleware.go similarity index 99% rename from app/controlplane/internal/usercontext/attjwtmiddleware/attmiddleware.go rename to app/controlplane/internal/usercontext/multijwtmiddleware/multijwtmiddleware.go index c24297ad1..15203b2de 100644 --- a/app/controlplane/internal/usercontext/attjwtmiddleware/attmiddleware.go +++ b/app/controlplane/internal/usercontext/multijwtmiddleware/multijwtmiddleware.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package attjwtmiddleware +package multijwtmiddleware import ( "bytes" diff --git a/app/controlplane/internal/usercontext/attjwtmiddleware/attmiddleware_test.go b/app/controlplane/internal/usercontext/multijwtmiddleware/multijwtmiddleware_test.go similarity index 87% rename from app/controlplane/internal/usercontext/attjwtmiddleware/attmiddleware_test.go rename to app/controlplane/internal/usercontext/multijwtmiddleware/multijwtmiddleware_test.go index 10a66eb3d..2e4d414a4 100644 --- a/app/controlplane/internal/usercontext/attjwtmiddleware/attmiddleware_test.go +++ b/app/controlplane/internal/usercontext/multijwtmiddleware/multijwtmiddleware_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package attjwtmiddleware_test +package multijwtmiddleware_test import ( "context" @@ -21,7 +21,7 @@ import ( "net/http" "testing" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/multijwtmiddleware" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport" "github.com/stretchr/testify/assert" @@ -89,33 +89,33 @@ func TestAttestationAPITokenProvider(t *testing.T) { tokenHeader *headerCarrier wantErr bool expectedError string - tokenProviders []attjwtmiddleware.JWTOption + tokenProviders []multijwtmiddleware.JWTOption }{ { name: "invalid audience", wantErr: true, expectedError: "unexpected token, invalid audience", tokenHeader: newTokenHeader("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3NDcxMjY4OTUsImV4cCI6bnVsbCwiYXVkIjoicmFuZG9tLWF1ZGllbmNlIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.nhs12KaDj0vHuR6nbBD_Qo4cPE-nXNFoWskEJNNXOys"), - tokenProviders: []attjwtmiddleware.JWTOption{attjwtmiddleware.NewAPITokenProvider(signingKey)}, + tokenProviders: []multijwtmiddleware.JWTOption{multijwtmiddleware.NewAPITokenProvider(signingKey)}, }, { name: "invalid token", wantErr: true, expectedError: "signature is invalid", tokenHeader: newTokenHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImV4cCI6MTcxNTMzMjUwOSwiaWF0IjoxNzE1MzMyNTA5fQ.41X6FyZ5xo0ckpkOkQbe2wLpFZ4Emtb8aMy_-3ZFs6Y"), - tokenProviders: []attjwtmiddleware.JWTOption{attjwtmiddleware.NewAPITokenProvider(signingKey)}, + tokenProviders: []multijwtmiddleware.JWTOption{multijwtmiddleware.NewAPITokenProvider(signingKey)}, }, { name: "valid api token", tokenHeader: newTokenHeader("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3NDcxMjY4OTUsImV4cCI6bnVsbCwiYXVkIjoiYXBpLXRva2VuLWF1dGguY2hhaW5sb29wIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.8O872KxwVpC8ErjOiioo-rdoV_tQgOyGDTbmC4bbHbo"), - tokenProviders: []attjwtmiddleware.JWTOption{attjwtmiddleware.NewAPITokenProvider(signingKey)}, + tokenProviders: []multijwtmiddleware.JWTOption{multijwtmiddleware.NewAPITokenProvider(signingKey)}, }, { name: "token validates when multiple providers are set", tokenHeader: newTokenHeader("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3NDcxMjY4OTUsImV4cCI6bnVsbCwiYXVkIjoiYXBpLXRva2VuLWF1dGguY2hhaW5sb29wIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.8O872KxwVpC8ErjOiioo-rdoV_tQgOyGDTbmC4bbHbo"), - tokenProviders: []attjwtmiddleware.JWTOption{ - attjwtmiddleware.NewRobotAccountProvider(signingKey), - attjwtmiddleware.NewAPITokenProvider(signingKey), + tokenProviders: []multijwtmiddleware.JWTOption{ + multijwtmiddleware.NewRobotAccountProvider(signingKey), + multijwtmiddleware.NewAPITokenProvider(signingKey), }, }, } @@ -125,7 +125,7 @@ func TestAttestationAPITokenProvider(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ctx := transport.NewServerContext(context.Background(), &mockTransport{reqHeader: tc.tokenHeader}) - m := attjwtmiddleware.WithJWTMulti(logger, tc.tokenProviders...) + m := multijwtmiddleware.WithJWTMulti(logger, tc.tokenProviders...) _, err := m(emptyHandler)(ctx, nil) if tc.wantErr { diff --git a/app/controlplane/internal/usercontext/robotaccount_middleware.go b/app/controlplane/internal/usercontext/robotaccount_middleware.go deleted file mode 100644 index 2b394124d..000000000 --- a/app/controlplane/internal/usercontext/robotaccount_middleware.go +++ /dev/null @@ -1,131 +0,0 @@ -// -// Copyright 2023 The Chainloop Authors. -// -// 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 usercontext - -import ( - "context" - "errors" - "fmt" - - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware" - "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities" - "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" - "github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/robotaccount" - "github.com/go-kratos/kratos/v2/log" - "github.com/go-kratos/kratos/v2/middleware" -) - -type RobotAccount struct { - ID, WorkflowID, OrgID, ProviderKey string -} - -func withRobotAccount(ctx context.Context, acc *RobotAccount) context.Context { - return context.WithValue(ctx, currentRobotAccountCtxKey{}, acc) -} - -func CurrentRobotAccount(ctx context.Context) *RobotAccount { - res := ctx.Value(currentRobotAccountCtxKey{}) - if res == nil { - return nil - } - - return res.(*RobotAccount) -} - -type currentRobotAccountCtxKey struct{} - -// WithAttestationContextFromRobotAccount Middleware that injects the current user to the context -func WithAttestationContextFromRobotAccount(robotAccountUseCase *biz.RobotAccountUseCase, orgUseCase *biz.OrganizationUseCase, logger *log.Helper) middleware.Middleware { - return func(handler middleware.Handler) middleware.Handler { - return func(ctx context.Context, req interface{}) (interface{}, error) { - authInfo, ok := attjwtmiddleware.FromJWTAuthContext(ctx) - // If not found means that there is no currentUser - if !ok { - logger.Warn("couldn't extract robot account, JWT parser middleware not running before this one?") - return nil, errors.New("can't extract info from the token") - } - - // If the token is not a robot account token, we don't need to do anything - if authInfo.ProviderKey != attjwtmiddleware.RobotAccountProviderKey { - return handler(ctx, req) - } - - claims, ok := authInfo.Claims.(*robotaccount.CustomClaims) - if !ok { - return nil, errors.New("error mapping the claims") - } - - // Do not accept tokens that are crafted for a different audience in this system - // NOTE: we allow deprecated audience to not to break compatibility with previously issued robot-accounts - if !claims.VerifyAudience(robotaccount.Audience, true) && !claims.VerifyAudience(robotaccount.DeprecatedAudience, true) { - return nil, errors.New("unexpected token, invalid audience") - } - - // Extract account ID - robotAccountID := claims.ID - if robotAccountID == "" { - return nil, errors.New("error retrieving the key ID from the auth token") - } - - // Check that the robot account exists and is not revoked - account, err := robotAccountUseCase.FindByID(ctx, robotAccountID) - if err != nil { - return nil, err - } - - if account == nil { - logger.Infof("robot account not found with id %s", robotAccountID) - return nil, errors.New("robot account not found") - } - - if account.RevokedAt != nil { - logger.Infof("robot account revoked %s", robotAccountID) - return nil, errors.New("robot account revoked") - } - - workflowID := claims.WorkflowID - if workflowID == "" { - return nil, errors.New("error retrieving the workflow from the auth token") - } - - orgID := claims.OrgID - if orgID == "" { - return nil, errors.New("error retrieving the organization from the auth token") - } - - org, err := orgUseCase.FindByID(ctx, orgID) - if err != nil { - return nil, fmt.Errorf("error retrieving the organization: %w", err) - } - - ctx = entities.WithCurrentOrg(ctx, &entities.Org{Name: org.Name, ID: org.ID, CreatedAt: org.CreatedAt}) - - // Check that the encoded workflow ID is the one associated with the robot account - // NOTE: This in theory should not be necessary since currently we allow a robot account to be attached to ONLY ONE workflowID - if account.WorkflowID.String() != workflowID { - logger.Info("workflow mismatch") - return nil, errors.New("workflow mismatch") - } - - // Set the robot account in the context - ctx = withRobotAccount(ctx, &RobotAccount{ - ID: account.ID.String(), WorkflowID: workflowID, OrgID: orgID, ProviderKey: authInfo.ProviderKey, - }) - - return handler(ctx, req) - } - } -}