From 5b4a18bf24e869bdc81cfe861137a236ae34bb52 Mon Sep 17 00:00:00 2001 From: rosstimothy <39066650+rosstimothy@users.noreply.github.com> Date: Thu, 26 May 2022 18:55:47 -0400 Subject: [PATCH 1/2] Add tracing service and configuration (#12699) * Add tracing service and configuration Provides a new tracing configuration block, which can be used to configure if and how spans are exported to a telemetry backend. In the example below, the tracing service is enabled and will export spans to `collector.example.com:4317` via gRPC with mTLS enabled. ```yaml tracing_service: enabled: yes exporter_url: collector.example.com:4317 sampling_rate_per_million: 1000000 ca_certs: - /certs/rootCA.pem keypairs: - key_file: /certs/example.com-client-key.pem cert_file: /certs/example.com-client.pem ``` This configuration ends up being consumed by the `TeleportProcess` and passed to `tracing.NewTraceProvider` which sets up the OpenTelemetry Exporter, TracerProvider, Propagator and Sampler. In order for spans to be exported, the `tracing_service` must be enabled **and** have a `sampling_rate_per_million` value > 0. --- constants.go | 6 + go.mod | 18 +- go.sum | 41 +- lib/config/configuration.go | 46 ++ lib/config/configuration_test.go | 25 + lib/config/fileconf.go | 33 ++ lib/observability/tracing/tracing.go | 244 +++++++++ lib/observability/tracing/tracing_test.go | 575 ++++++++++++++++++++++ lib/service/cfg.go | 23 + lib/service/service.go | 104 +++- 10 files changed, 1098 insertions(+), 17 deletions(-) create mode 100644 lib/observability/tracing/tracing.go create mode 100644 lib/observability/tracing/tracing_test.go diff --git a/constants.go b/constants.go index 2fef60c7e9e4b..5ec0cf525a286 100644 --- a/constants.go +++ b/constants.go @@ -203,6 +203,9 @@ const ( // and vice versa. ComponentKeepAlive = "keepalive" + // ComponentTeleport is the "teleport" binary. + ComponentTeleport = "teleport" + // ComponentTSH is the "tsh" binary. ComponentTSH = "tsh" @@ -237,6 +240,9 @@ const ( // ComponentWindowsDesktop is a Windows desktop access server. ComponentWindowsDesktop = "windows_desktop" + // ComponentTracing is a tracing exporter + ComponentTracing = "tracing" + // DebugEnvVar tells tests to use verbose debug output DebugEnvVar = "DEBUG" diff --git a/go.mod b/go.mod index ce945667fb923..733391d7f465b 100644 --- a/go.mod +++ b/go.mod @@ -92,20 +92,27 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.31.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0 - go.opentelemetry.io/otel v1.6.1 + go.opentelemetry.io/otel v1.7.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 + go.opentelemetry.io/otel/sdk v1.7.0 + go.opentelemetry.io/otel/trace v1.7.0 + go.opentelemetry.io/proto/otlp v0.16.0 go.uber.org/atomic v1.7.0 golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 golang.org/x/mod v0.4.2 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/text v0.3.7 golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff google.golang.org/api v0.65.0 google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5 - google.golang.org/grpc v1.45.0 - google.golang.org/protobuf v1.27.1 + google.golang.org/grpc v1.46.0 + google.golang.org/protobuf v1.28.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/ini.v1 v1.62.0 gopkg.in/square/go-jose.v2 v2.5.1 @@ -144,6 +151,7 @@ require ( github.com/aws/smithy-go v1.8.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 // indirect @@ -181,6 +189,7 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2.0.20220308023801-e4a6915ea237 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/imdario/mergo v0.3.5 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -246,12 +255,11 @@ require ( github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect go.opencensus.io v0.23.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect go.opentelemetry.io/otel/metric v0.28.0 // indirect - go.opentelemetry.io/otel/trace v1.6.1 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.0 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 2c0f788483b59..8cfaecc2f8f12 100644 --- a/go.sum +++ b/go.sum @@ -179,7 +179,10 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -202,6 +205,7 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= @@ -265,6 +269,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= @@ -380,6 +385,8 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2V github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -550,7 +557,10 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2.0.20220308023801-e4a github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -1070,21 +1080,36 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0 h1:woM+Mb4 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0/go.mod h1:PFmBsWbldL1kiWZk9+0LBZz2brhByaGsvp6pRICMlPE= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.6.0/go.mod h1:bfJD2DZVw0LBxghOTlgnlI0CV3hLDu9XF/QKOUXMTQQ= -go.opentelemetry.io/otel v1.6.1 h1:6r1YrcTenBvYa1x491d0GGpTVBsNECmrc/K6b+zDeis= go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ= +go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 h1:7Yxsak1q4XrJ5y7XBnNwqWx9amMZvoidCctv62XOQ6Y= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 h1:cMDtmgJ5FpRvqx9x2Aq+Mm0O6K/zcUkH73SFz20TuBw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 h1:MFAyzUPrTwLOwCi+cltN0ZVyy4phU41lwH+lyMyQTS4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 h1:pLP0MH4MAqeTEV0g/4flxw9O8Is48uAIauAnjznbW50= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0/go.mod h1:aFXT9Ng2seM9eizF+LfKiyPBGy8xIZKwhusC1gIu3hA= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/metric v0.28.0 h1:o5YNh+jxACMODoAo1bI7OES0RUW4jAMae0Vgs2etWAQ= go.opentelemetry.io/otel/metric v0.28.0/go.mod h1:TrzsfQAmQaB1PDcdhBauLMk7nyyg9hm+GoQq/ekE9Iw= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= +go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.6.0/go.mod h1:qs7BrU5cZ8dXQHBGxHMOxwME/27YH2qEp4/+tZLLwJE= -go.opentelemetry.io/otel/trace v1.6.1 h1:f8c93l5tboBYZna1nWk0W9DYyMzJXDWdZcJZ0Kb400U= go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0= +go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.16.0 h1:WHzDWdXUvbc5bG2ObdrGfaNpQz7ft7QN9HHmJlbiB1E= +go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1093,8 +1118,9 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -1156,7 +1182,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1329,6 +1354,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1597,9 +1623,11 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/examples v0.0.0-20200723182653-9106c3fff523/go.mod h1:5j1uub0jRGhRiSghIlrThmBUgcgLXOVJQ/l1getT4uo= google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= @@ -1615,8 +1643,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 60fc6576ab2b4..50b18bfb761e1 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -436,6 +436,11 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error { return trace.Wrap(err) } } + if fc.Tracing.Enabled() { + if err := applyTracingConfig(fc, cfg); err != nil { + return trace.Wrap(err) + } + } return nil } @@ -1409,6 +1414,47 @@ func applyWindowsDesktopConfig(fc *FileConfig, cfg *service.Config) error { return nil } +// applyTracingConfig applies file configuration for the "tracing_service" section. +func applyTracingConfig(fc *FileConfig, cfg *service.Config) error { + // Tracing is enabled. + cfg.Tracing.Enabled = true + + if fc.Tracing.ExporterURL == "" { + return trace.BadParameter("tracing_service is enabled but no exporter_url is specified") + } + + cfg.Tracing.ExporterURL = fc.Tracing.ExporterURL + cfg.Tracing.SamplingRate = float64(fc.Tracing.SamplingRatePerMillion) / 1_000_000.0 + + for _, p := range fc.Tracing.KeyPairs { + // Check that the certificate exists on disk. This exists to provide the + // user a sensible error message. + if !utils.FileExists(p.PrivateKey) { + return trace.NotFound("tracing_service private key does not exist: %s", p.PrivateKey) + } + if !utils.FileExists(p.Certificate) { + return trace.NotFound("tracing_service cert does not exist: %s", p.Certificate) + } + + cfg.Tracing.KeyPairs = append(cfg.Tracing.KeyPairs, service.KeyPairPath{ + PrivateKey: p.PrivateKey, + Certificate: p.Certificate, + }) + } + + for _, caCert := range fc.Tracing.CACerts { + // Check that the certificate exists on disk. This exists to provide the + // user a sensible error message. + if !utils.FileExists(caCert) { + return trace.NotFound("tracing_service ca cert does not exist: %s", caCert) + } + + cfg.Tracing.CACerts = append(cfg.Tracing.CACerts, caCert) + } + + return nil +} + // parseAuthorizedKeys parses keys in the authorized_keys format and // returns a types.CertAuthority. func parseAuthorizedKeys(bytes []byte, allowedLogins []string) (types.CertAuthority, types.Role, error) { diff --git a/lib/config/configuration_test.go b/lib/config/configuration_test.go index 7945280b37294..06baf15600e1c 100644 --- a/lib/config/configuration_test.go +++ b/lib/config/configuration_test.go @@ -442,6 +442,18 @@ func TestConfigReading(t *testing.T) { PublicAddr: apiutils.Strings([]string{"winsrv.example.com:3028", "no-port.winsrv.example.com"}), Hosts: apiutils.Strings([]string{"win.example.com:3389", "no-port.win.example.com"}), }, + Tracing: TracingService{ + EnabledFlag: "yes", + ExporterURL: "https://localhost:4318", + KeyPairs: []KeyPair{ + { + PrivateKey: "/etc/teleport/exporter.key", + Certificate: "/etc/teleport/exporter.crt", + }, + }, + CACerts: []string{"/etc/teleport/exporter.crt"}, + SamplingRatePerMillion: 10, + }, }, cmp.AllowUnexported(Service{}))) require.True(t, conf.Auth.Configured()) require.True(t, conf.Auth.Enabled()) @@ -459,6 +471,7 @@ func TestConfigReading(t *testing.T) { require.True(t, conf.Metrics.Enabled()) require.True(t, conf.WindowsDesktop.Configured()) require.True(t, conf.WindowsDesktop.Enabled()) + require.True(t, conf.Tracing.Enabled()) // good config from file conf, err = ReadFromFile(testConfigs.configFileStatic) @@ -1193,6 +1206,18 @@ func makeConfigFixture() string { Hosts: apiutils.Strings([]string{"win.example.com:3389", "no-port.win.example.com"}), } + // Tracing service. + conf.Tracing.EnabledFlag = "yes" + conf.Tracing.ExporterURL = "https://localhost:4318" + conf.Tracing.SamplingRatePerMillion = 10 + conf.Tracing.CACerts = []string{"/etc/teleport/exporter.crt"} + conf.Tracing.KeyPairs = []KeyPair{ + { + PrivateKey: "/etc/teleport/exporter.key", + Certificate: "/etc/teleport/exporter.crt", + }, + } + return conf.DebugDumpToYAML() } diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index 6dd9078a31b1f..d1458a8196fa1 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -86,6 +86,9 @@ type FileConfig struct { // WindowsDesktop is the "windows_desktop_service" that defines the // configuration for Windows Desktop Access. WindowsDesktop WindowsDesktopService `yaml:"windows_desktop_service,omitempty"` + + // Tracing is the "tracing_service" section in Teleport configuration file + Tracing TracingService `yaml:"tracing_service,omitempty"` } // ReadFromFile reads Teleport configuration from a file. Currently only YAML @@ -1631,3 +1634,33 @@ type LDAPConfig struct { // DEREncodedCAFile is the filepath to an optional DER encoded CA cert to be used for verification (if InsecureSkipVerify is set to false). DEREncodedCAFile string `yaml:"der_ca_file,omitempty"` } + +// TracingService contains configuration for the tracing_service. +type TracingService struct { + // Enabled turns the tracing service role on or off for this process + EnabledFlag string `yaml:"enabled,omitempty"` + + // ExporterURL is the OTLP exporter URL to send spans to + ExporterURL string `yaml:"exporter_url"` + + // KeyPairs is a list of x509 serving key pairs used for mTLS. + KeyPairs []KeyPair `yaml:"keypairs,omitempty"` + + // CACerts are the exporter ca certs to use + CACerts []string `yaml:"ca_certs,omitempty"` + + // SamplingRatePerMillion is the sampling rate for the exporter. + // 1_000_000 means all spans will be sampled and 0 means none are sampled. + SamplingRatePerMillion int `yaml:"sampling_rate_per_million"` +} + +func (s *TracingService) Enabled() bool { + if s.EnabledFlag == "" { + return false + } + v, err := apiutils.ParseBool(s.EnabledFlag) + if err != nil { + return false + } + return v +} diff --git a/lib/observability/tracing/tracing.go b/lib/observability/tracing/tracing.go new file mode 100644 index 0000000000000..a6eba669be0c3 --- /dev/null +++ b/lib/observability/tracing/tracing.go @@ -0,0 +1,244 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 tracing + +import ( + "context" + "crypto/tls" + "errors" + "net" + "net/url" + "time" + + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + oteltrace "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/gravitational/teleport" +) + +const ( + // DefaultExporterDialTimeout is the default timeout for dialing the exporter. + DefaultExporterDialTimeout = 5 * time.Second + + // VersionKey is the attribute key for the teleport version. + VersionKey = "teleport.version" + + // ProcessIDKey is attribute key for the process ID. + ProcessIDKey = "teleport.process.id" + + // HostnameKey is the attribute key for the hostname. + HostnameKey = "teleport.host.name" + + // HostIDKey is the attribute key for the host UUID. + HostIDKey = "teleport.host.uuid" +) + +// Config used to set up the tracing exporter and provider +type Config struct { + // Service is the name of the service that will be reported to the tracing system. + Service string + // Attributes is a set of key value pairs that will be added to all spans. + Attributes []attribute.KeyValue + // ExporterURL is the URL of the exporter. + ExporterURL string + // SamplingRate determines how many spans are recorded and exported + SamplingRate float64 + // TLSCert is the TLS configuration to use for the exporter. + TLSConfig *tls.Config + // DialTimeout is the timeout for dialing the exporter. + DialTimeout time.Duration + // Logger is the logger to use. + Logger logrus.FieldLogger + + exporterURL *url.URL +} + +// CheckAndSetDefaults checks the config and sets default values. +func (c *Config) CheckAndSetDefaults() error { + if c.Service == "" { + return trace.BadParameter("service name cannot be empty") + } + + if c.ExporterURL == "" { + return trace.BadParameter("exporter URL cannot be empty") + } + + // first check if a network address is specified, if it was, default + // to using grpc. If provided a URL, ensure that it is valid + _, _, err := net.SplitHostPort(c.ExporterURL) + if err == nil { + c.exporterURL = &url.URL{ + Scheme: "grpc", + Host: c.ExporterURL, + } + } else { + exporterURL, err := url.Parse(c.ExporterURL) + if err != nil { + return trace.BadParameter("failed to parse exporter URL: %v", err) + } + c.exporterURL = exporterURL + + } + + if c.DialTimeout <= 0 { + c.DialTimeout = DefaultExporterDialTimeout + } + + if c.Logger == nil { + c.Logger = logrus.WithField(trace.Component, teleport.ComponentTracing) + } + + return nil +} + +// NewExporter returns a new exporter that is configured per the provided Config. +func NewExporter(ctx context.Context, cfg Config) (sdktrace.SpanExporter, error) { + if err := cfg.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + + var httpOptions []otlptracehttp.Option + grpcOptions := []otlptracegrpc.Option{otlptracegrpc.WithDialOption(grpc.WithBlock())} + + if cfg.TLSConfig != nil { + httpOptions = append(httpOptions, otlptracehttp.WithTLSClientConfig(cfg.TLSConfig.Clone())) + grpcOptions = append(grpcOptions, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(cfg.TLSConfig.Clone()))) + } else { + httpOptions = append(httpOptions, otlptracehttp.WithInsecure()) + grpcOptions = append(grpcOptions, otlptracegrpc.WithInsecure()) + } + + var traceClient otlptrace.Client + switch cfg.exporterURL.Scheme { + case "http", "https": + httpOptions = append(httpOptions, otlptracehttp.WithEndpoint(cfg.ExporterURL[len(cfg.exporterURL.Scheme)+3:])) + traceClient = otlptracehttp.NewClient(httpOptions...) + case "grpc": + grpcOptions = append(grpcOptions, otlptracegrpc.WithEndpoint(cfg.ExporterURL[len(cfg.exporterURL.Scheme)+3:])) + traceClient = otlptracegrpc.NewClient(grpcOptions...) + default: + return nil, trace.BadParameter("unsupported exporter scheme: %q", cfg.exporterURL.Scheme) + } + + ctx, cancel := context.WithTimeout(ctx, cfg.DialTimeout) + defer cancel() + exporter, err := otlptrace.New(ctx, traceClient) + switch { + case errors.Is(err, context.DeadlineExceeded): + return nil, trace.ConnectionProblem(err, "failed to connect to tracing exporter %s: %v", cfg.ExporterURL, err) + case err != nil: + return nil, trace.NewAggregate(err, traceClient.Stop(context.Background())) + } + + return exporter, nil +} + +// Provider wraps the OpenTelemetry tracing provider to provide common tags for all tracers. +type Provider struct { + provider *sdktrace.TracerProvider +} + +// Tracer returns a Tracer with the given name and options. If a Tracer for +// the given name and options does not exist it is created, otherwise the +// existing Tracer is returned. +// +// If name is empty, DefaultTracerName is used instead. +// +// This method is safe to be called concurrently. +func (p *Provider) Tracer(instrumentationName string, opts ...oteltrace.TracerOption) oteltrace.Tracer { + opts = append(opts, oteltrace.WithInstrumentationVersion(teleport.Version)) + + return p.provider.Tracer(instrumentationName, opts...) +} + +// Shutdown shuts down the span processors in the order they were registered. +func (p *Provider) Shutdown(ctx context.Context) error { + return trace.NewAggregate(p.provider.ForceFlush(ctx), p.provider.Shutdown(ctx)) +} + +// NoopProvider creates a new Provider that never samples any spans. +func NoopProvider() *Provider { + return &Provider{provider: sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.NeverSample()), + )} +} + +// NewTraceProvider creates a new Provider that is configured per the provided Config. +func NewTraceProvider(ctx context.Context, cfg Config) (*Provider, error) { + if err := cfg.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + + exporter, err := NewExporter(ctx, cfg) + if err != nil { + return nil, trace.Wrap(err) + } + + attrs := []attribute.KeyValue{ + // the service name used to display traces in backends + semconv.ServiceNameKey.String(cfg.Service), + attribute.String(VersionKey, teleport.Version), + } + attrs = append(attrs, cfg.Attributes...) + + res, err := resource.New(ctx, + resource.WithFromEnv(), + resource.WithProcess(), + resource.WithProcessPID(), + resource.WithProcessExecutableName(), + resource.WithProcessExecutablePath(), + resource.WithProcessOwner(), + resource.WithProcessRuntimeName(), + resource.WithProcessRuntimeVersion(), + resource.WithProcessRuntimeDescription(), + resource.WithTelemetrySDK(), + resource.WithHost(), + resource.WithAttributes(attrs...), + ) + if err != nil { + return nil, trace.Wrap(err) + } + + // set global propagator, the default is no-op. + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) + + // override the global logging handled with one that uses the + // configured logger instead + otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) { + cfg.Logger.WithError(err).Warnf("failed to export traces.") + })) + + // set global provider to our provider wrapper to have all tracers use the common TracerOptions + provider := &Provider{provider: sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(cfg.SamplingRate))), + sdktrace.WithResource(res), + sdktrace.WithSpanProcessor(sdktrace.NewBatchSpanProcessor(exporter)), + )} + otel.SetTracerProvider(provider) + + return provider, nil +} diff --git a/lib/observability/tracing/tracing_test.go b/lib/observability/tracing/tracing_test.go new file mode 100644 index 0000000000000..53a01795a3d29 --- /dev/null +++ b/lib/observability/tracing/tracing_test.go @@ -0,0 +1,575 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 tracing + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "io" + "math/big" + "net" + "net/http" + "net/url" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" + otlp "go.opentelemetry.io/proto/otlp/trace/v1" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/proto" +) + +func generateTLSCertificate() (tls.Certificate, error) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return tls.Certificate{}, err + } + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{Organization: []string{"Test CA"}}, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{"localhost"}, + IPAddresses: []net.IP{net.IPv6loopback, net.IPv4(127, 0, 0, 1)}, + } + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return tls.Certificate{}, err + } + var certificateBuffer bytes.Buffer + if err := pem.Encode(&certificateBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { + return tls.Certificate{}, err + } + privDERBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return tls.Certificate{}, err + } + var privBuffer bytes.Buffer + if err := pem.Encode(&privBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: privDERBytes}); err != nil { + return tls.Certificate{}, err + } + + tlsCertificate, err := tls.X509KeyPair(certificateBuffer.Bytes(), privBuffer.Bytes()) + if err != nil { + return tls.Certificate{}, err + } + return tlsCertificate, nil +} + +type collector struct { + grpcLn net.Listener + httpLn net.Listener + grpcServer *grpc.Server + httpServer *http.Server + + coltracepb.TraceServiceServer + spans []*otlp.ScopeSpans +} + +type collectorConfig struct { + WithTLS bool +} + +func newCollector(cfg collectorConfig) (*collector, error) { + grpcLn, err := net.Listen("tcp4", "") + if err != nil { + return nil, trace.Wrap(err) + } + + httpLn, err := net.Listen("tcp4", "") + if err != nil { + return nil, trace.Wrap(err) + } + + var tlsConfig *tls.Config + creds := insecure.NewCredentials() + if cfg.WithTLS { + tlsCertificate, err := generateTLSCertificate() + if err != nil { + return nil, err + } + tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{tlsCertificate}, + } + creds = credentials.NewTLS(tlsConfig) + } + + c := &collector{ + grpcLn: grpcLn, + httpLn: httpLn, + grpcServer: grpc.NewServer(grpc.Creds(creds)), + } + + c.httpServer = &http.Server{Handler: c, TLSConfig: tlsConfig} + + coltracepb.RegisterTraceServiceServer(c.grpcServer, c) + + return c, nil +} + +func (c *collector) ClientTLSConfig() *tls.Config { + if c.httpServer.TLSConfig == nil { + return nil + } + + return &tls.Config{ + InsecureSkipVerify: true, + } +} + +func (c collector) GRPCAddr() string { + return "grpc://" + c.grpcLn.Addr().String() +} + +func (c collector) HTTPAddr() string { + return "http://" + c.httpLn.Addr().String() +} + +func (c collector) HTTPSAddr() string { + return "https://" + c.httpLn.Addr().String() +} + +func (c collector) Start() error { + var g errgroup.Group + + g.Go(func() error { + if c.httpServer.TLSConfig != nil { + return c.httpServer.ServeTLS(c.httpLn, "", "") + } + return c.httpServer.Serve(c.httpLn) + }) + + g.Go(func() error { + return c.grpcServer.Serve(c.grpcLn) + }) + + return g.Wait() +} + +func (c collector) Shutdown(ctx context.Context) error { + c.grpcServer.Stop() + return c.httpServer.Shutdown(ctx) +} + +func (c *collector) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/v1/traces" { + response := coltracepb.ExportTraceServiceResponse{} + rawResponse, err := proto.Marshal(&response) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + rawRequest, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + req := &coltracepb.ExportTraceServiceRequest{} + if err := proto.Unmarshal(rawRequest, req); err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + for _, span := range req.ResourceSpans { + c.spans = append(c.spans, span.ScopeSpans...) + } + + w.WriteHeader(http.StatusOK) + _, _ = w.Write(rawResponse) + } +} + +func (c *collector) Export(ctx context.Context, req *coltracepb.ExportTraceServiceRequest) (*coltracepb.ExportTraceServiceResponse, error) { + for _, span := range req.ResourceSpans { + c.spans = append(c.spans, span.ScopeSpans...) + } + + return &coltracepb.ExportTraceServiceResponse{}, nil +} + +func TestNewExporter(t *testing.T) { + t.Parallel() + c, err := newCollector(collectorConfig{ + WithTLS: false, + }) + require.NoError(t, err) + + t.Cleanup(func() { + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 3*time.Second) + defer shutdownCancel() + require.NoError(t, c.Shutdown(shutdownCtx)) + }) + go func() { + c.Start() + }() + + cases := []struct { + name string + config Config + errAssertion require.ErrorAssertionFunc + exporterAssertion require.ValueAssertionFunc + }{ + { + name: "invalid config", + errAssertion: func(t require.TestingT, err error, i ...interface{}) { + require.Error(t, err, i...) + require.True(t, trace.IsBadParameter(err), i...) + }, + exporterAssertion: require.Nil, + }, + { + name: "invalid exporter url", + config: Config{ + Service: "test", + ExporterURL: "tcp://localhost:123", + }, + errAssertion: func(t require.TestingT, err error, i ...interface{}) { + require.Error(t, err, i...) + require.True(t, trace.IsBadParameter(err), i...) + }, + exporterAssertion: require.Nil, + }, + { + name: "connection timeout", + config: Config{ + Service: "test", + ExporterURL: "localhost:123", + DialTimeout: time.Millisecond, + }, + errAssertion: func(t require.TestingT, err error, i ...interface{}) { + require.Error(t, err, i...) + require.True(t, trace.IsConnectionProblem(err), i...) + }, + exporterAssertion: require.Nil, + }, + { + name: "successful explicit grpc exporter", + config: Config{ + Service: "test", + ExporterURL: c.GRPCAddr(), + DialTimeout: time.Second, + }, + errAssertion: require.NoError, + exporterAssertion: require.NotNil, + }, + { + name: "successful inferred grpc exporter", + config: Config{ + Service: "test", + ExporterURL: c.GRPCAddr()[len("grpc://"):], + DialTimeout: time.Second, + }, + errAssertion: require.NoError, + exporterAssertion: require.NotNil, + }, + { + name: "successful http exporter", + config: Config{ + Service: "test", + ExporterURL: c.HTTPAddr(), + DialTimeout: time.Second, + }, + errAssertion: require.NoError, + exporterAssertion: require.NotNil, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + exporter, err := NewExporter(context.Background(), tt.config) + tt.errAssertion(t, err) + tt.exporterAssertion(t, exporter) + }) + } +} + +func TestTraceProvider(t *testing.T) { + t.Parallel() + const spansCreated = 4 + cases := []struct { + name string + config func(c *collector) Config + errAssertion require.ErrorAssertionFunc + providerAssertion require.ValueAssertionFunc + collectedLen int + withTLS bool + }{ + { + name: "not sampling prevents exporting", + config: func(c *collector) Config { + return Config{ + Service: "test", + ExporterURL: c.GRPCAddr(), + DialTimeout: time.Second, + TLSConfig: c.ClientTLSConfig(), + } + }, + errAssertion: require.NoError, + providerAssertion: require.NotNil, + collectedLen: 0, + }, + { + name: "spans exported with gRPC+TLS", + config: func(c *collector) Config { + return Config{ + Service: "test", + SamplingRate: 1.0, + ExporterURL: c.GRPCAddr(), + DialTimeout: time.Second, + TLSConfig: c.ClientTLSConfig(), + } + }, + errAssertion: require.NoError, + providerAssertion: require.NotNil, + collectedLen: spansCreated, + withTLS: true, + }, + { + name: "spans exported with gRPC", + config: func(c *collector) Config { + return Config{ + Service: "test", + SamplingRate: 0.5, + ExporterURL: c.GRPCAddr(), + DialTimeout: time.Second, + TLSConfig: c.ClientTLSConfig(), + } + }, + errAssertion: require.NoError, + providerAssertion: require.NotNil, + collectedLen: spansCreated / 2, + }, + { + name: "spans exported with HTTP", + config: func(c *collector) Config { + return Config{ + Service: "test", + SamplingRate: 1.0, + ExporterURL: c.HTTPAddr(), + DialTimeout: time.Second, + TLSConfig: c.ClientTLSConfig(), + } + }, + errAssertion: require.NoError, + providerAssertion: require.NotNil, + collectedLen: spansCreated, + }, + { + name: "spans exported with HTTPS", + config: func(c *collector) Config { + return Config{ + Service: "test", + SamplingRate: 1.0, + ExporterURL: c.HTTPSAddr(), + DialTimeout: time.Second, + TLSConfig: c.ClientTLSConfig(), + } + }, + errAssertion: require.NoError, + providerAssertion: require.NotNil, + collectedLen: spansCreated, + withTLS: true, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + collector, err := newCollector(collectorConfig{ + WithTLS: tt.withTLS, + }) + require.NoError(t, err) + + t.Cleanup(func() { + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 3*time.Second) + defer shutdownCancel() + require.NoError(t, collector.Shutdown(shutdownCtx)) + }) + go func() { + collector.Start() + }() + + ctx := context.Background() + provider, err := NewTraceProvider(ctx, tt.config(collector)) + tt.errAssertion(t, err) + tt.providerAssertion(t, provider) + + if err != nil { + return + } + + for i := 0; i < spansCreated; i++ { + _, span := provider.Tracer("test").Start(ctx, fmt.Sprintf("test%d", i)) + span.End() + } + + shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + require.NoError(t, provider.Shutdown(shutdownCtx)) + require.LessOrEqual(t, len(collector.spans), tt.collectedLen) + require.GreaterOrEqual(t, len(collector.spans), 0) + }) + } +} + +func TestConfig_CheckAndSetDefaults(t *testing.T) { + cases := []struct { + name string + cfg Config + errorAssertion require.ErrorAssertionFunc + expectedCfg Config + expectedURL *url.URL + }{ + { + name: "valid config", + cfg: Config{ + Service: "test", + SamplingRate: 1.0, + ExporterURL: "http://localhost:8080", + DialTimeout: time.Millisecond, + }, + errorAssertion: require.NoError, + expectedCfg: Config{ + Service: "test", + ExporterURL: "http://localhost:8080", + SamplingRate: 1.0, + DialTimeout: time.Millisecond, + }, + expectedURL: &url.URL{ + Scheme: "http", + Host: "localhost:8080", + }, + }, + { + name: "invalid service", + cfg: Config{ + Service: "", + SamplingRate: 1.0, + ExporterURL: "http://localhost:8080", + }, + errorAssertion: require.Error, + expectedCfg: Config{ + Service: "test", + ExporterURL: "http://localhost:8080", + SamplingRate: 1.0, + DialTimeout: time.Millisecond, + }, + }, + { + name: "invalid exporter url", + cfg: Config{ + Service: "test", + ExporterURL: "", + }, + errorAssertion: require.Error, + }, + { + name: "network address defaults to grpc", + cfg: Config{ + Service: "test", + SamplingRate: 1.0, + ExporterURL: "localhost:8080", + DialTimeout: time.Millisecond, + }, + errorAssertion: require.NoError, + expectedCfg: Config{ + Service: "test", + ExporterURL: "localhost:8080", + SamplingRate: 1.0, + DialTimeout: time.Millisecond, + }, + expectedURL: &url.URL{ + Scheme: "grpc", + Host: "localhost:8080", + }, + }, + { + name: "empty scheme defaults to grpc", + cfg: Config{ + Service: "test", + SamplingRate: 1.0, + ExporterURL: "exporter.example.com:4317", + DialTimeout: time.Millisecond, + }, + errorAssertion: require.NoError, + expectedCfg: Config{ + Service: "test", + ExporterURL: "exporter.example.com:4317", + SamplingRate: 1.0, + DialTimeout: time.Millisecond, + }, + expectedURL: &url.URL{ + Scheme: "grpc", + Host: "exporter.example.com:4317", + }, + }, + { + name: "timeout defaults to DefaultExporterDialTimeout", + cfg: Config{ + Service: "test", + SamplingRate: 1.0, + ExporterURL: "https://localhost:8080", + }, + errorAssertion: require.NoError, + expectedCfg: Config{ + Service: "test", + ExporterURL: "https://localhost:8080", + SamplingRate: 1.0, + DialTimeout: DefaultExporterDialTimeout, + }, + expectedURL: &url.URL{ + Scheme: "https", + Host: "localhost:8080", + }, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + err := tt.cfg.CheckAndSetDefaults() + tt.errorAssertion(t, err) + if err != nil { + return + } + require.Empty(t, cmp.Diff(tt.expectedCfg, tt.cfg, + cmpopts.IgnoreUnexported(Config{}), + cmpopts.IgnoreInterfaces(struct{ logrus.FieldLogger }{})), + ) + require.NotNil(t, tt.cfg.Logger) + }) + } +} diff --git a/lib/service/cfg.go b/lib/service/cfg.go index 867dd9a8091e8..2ae57ff4d1af9 100644 --- a/lib/service/cfg.go +++ b/lib/service/cfg.go @@ -128,6 +128,9 @@ type Config struct { // WindowsDesktop defines the Windows desktop service configuration. WindowsDesktop WindowsDesktopConfig + // Tracing defines the tracing service configuration. + Tracing TracingConfig + // Keygen points to a key generator implementation Keygen sshca.Authority @@ -967,6 +970,26 @@ type MetricsConfig struct { GRPCClientLatency bool } +// TracingConfig specifies the configuration for the tracing service +type TracingConfig struct { + // Enabled turns the tracing service role on or off for this process. + Enabled bool + + // ExporterURL is the OTLP exporter URL to send spans to. + ExporterURL string + + // KeyPairs are the paths for key and certificate pairs that the tracing + // service will use for outbound TLS connections. + KeyPairs []KeyPairPath + + // CACerts are the paths to the CA certs used to validate the collector. + CACerts []string + + // SamplingRate is the sampling rate for the exporter. + // 1.0 will record and export all spans and 0.0 won't record any spans. + SamplingRate float64 +} + // WindowsDesktopConfig specifies the configuration for the Windows Desktop // Access service. type WindowsDesktopConfig struct { diff --git a/lib/service/service.go b/lib/service/service.go index 7d87da891811e..2ce82c1c7287a 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -42,12 +42,6 @@ import ( "sync/atomic" "time" - "github.com/gravitational/trace" - "golang.org/x/crypto/acme" - "golang.org/x/crypto/acme/autocert" - "golang.org/x/crypto/ssh" - "google.golang.org/grpc" - "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/constants" @@ -78,6 +72,7 @@ import ( "github.com/gravitational/teleport/lib/limiter" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/multiplexer" + "github.com/gravitational/teleport/lib/observability/tracing" "github.com/gravitational/teleport/lib/plugin" restricted "github.com/gravitational/teleport/lib/restrictedsession" "github.com/gravitational/teleport/lib/reversetunnel" @@ -97,9 +92,15 @@ import ( "github.com/google/uuid" "github.com/gravitational/roundtrip" + "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel/attribute" + "golang.org/x/crypto/acme" + "golang.org/x/crypto/acme/autocert" + "golang.org/x/crypto/ssh" + "google.golang.org/grpc" ) const ( @@ -180,6 +181,10 @@ const ( // service is ready to start accepting connections. WindowsDesktopReady = "WindowsDesktopReady" + // TracingReady is generated when the Teleport tracing service is ready to + // start exporting spans. + TracingReady = "TracingReady" + // TeleportExitEvent is generated when the Teleport process begins closing // all listening sockets and exiting. TeleportExitEvent = "TeleportExit" @@ -329,6 +334,10 @@ type TeleportProcess struct { // clusterFeatures contain flags for supported and unsupported features. clusterFeatures proto.Features + + // TracingProvider is the provider to be used for exporting traces. In the event + // that tracing is disabled this will be a no-op provider that drops all spans. + TracingProvider *tracing.Provider } type keyPairKey struct { @@ -748,6 +757,7 @@ func NewTeleport(cfg *Config) (*TeleportProcess, error) { id: processID, keyPairs: make(map[keyPairKey]KeyPair), appDependCh: make(chan Event, 1024), + TracingProvider: tracing.NoopProvider(), } process.registerAppDepend() @@ -766,6 +776,12 @@ func NewTeleport(cfg *Config) (*TeleportProcess, error) { warnOnErr(process.closeImportedDescriptors(teleport.ComponentDiagnostic), process.log) } + if cfg.Tracing.Enabled { + if err := process.initTracingService(); err != nil { + return nil, trace.Wrap(err) + } + } + // Create a process wide key generator that will be shared. This is so the // key generator can pre-generate keys and share these across services. if cfg.Keygen == nil { @@ -801,6 +817,9 @@ func NewTeleport(cfg *Config) (*TeleportProcess, error) { if cfg.WindowsDesktop.Enabled { eventMapping.In = append(eventMapping.In, WindowsDesktopReady) } + if cfg.Tracing.Enabled { + eventMapping.In = append(eventMapping.In, TracingReady) + } process.RegisterEventMapping(eventMapping) if cfg.Auth.Enabled { @@ -2377,6 +2396,79 @@ func (process *TeleportProcess) initDiagnosticService() error { return nil } +func (process *TeleportProcess) initTracingService() error { + log := process.log.WithField(trace.Component, teleport.Component(teleport.ComponentTracing, process.id)) + log.Info("Initializing tracing provider and exporter.") + + attrs := []attribute.KeyValue{ + attribute.String(tracing.ProcessIDKey, process.id), + attribute.String(tracing.HostnameKey, process.Config.Hostname), + attribute.String(tracing.HostIDKey, process.Config.HostUUID), + } + + traceConf := tracing.Config{ + Service: teleport.ComponentTeleport, + Attributes: attrs, + ExporterURL: process.Config.Tracing.ExporterURL, + SamplingRate: process.Config.Tracing.SamplingRate, + Logger: log, + } + + tlsConfig := &tls.Config{} + // if a custom CA is specified, use a custom cert pool + if len(process.Config.Tracing.CACerts) > 0 { + pool := x509.NewCertPool() + for _, caCertPath := range process.Config.Tracing.CACerts { + caCert, err := os.ReadFile(caCertPath) + if err != nil { + return trace.Wrap(err, "failed to read tracing CA certificate %+v", caCertPath) + } + + if !pool.AppendCertsFromPEM(caCert) { + return trace.BadParameter("failed to parse tracing CA certificate: %+v", caCertPath) + } + } + tlsConfig.ClientCAs = pool + tlsConfig.RootCAs = pool + } + + // add any custom certificates for mTLS + if len(process.Config.Tracing.KeyPairs) > 0 { + for _, pair := range process.Config.Tracing.KeyPairs { + certificate, err := tls.LoadX509KeyPair(pair.Certificate, pair.PrivateKey) + if err != nil { + return trace.Wrap(err, "failed to read keypair: %+v", err) + } + tlsConfig.Certificates = append(tlsConfig.Certificates, certificate) + } + } + + if len(process.Config.Tracing.CACerts) > 0 || len(process.Config.Tracing.KeyPairs) > 0 { + traceConf.TLSConfig = tlsConfig + } + + provider, err := tracing.NewTraceProvider(process.ExitContext(), traceConf) + if err != nil { + return trace.Wrap(err) + } + process.TracingProvider = provider + + process.OnExit("tracing.shutdown", func(payload interface{}) { + if payload == nil { + log.Info("Shutting down immediately.") + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + warnOnErr(provider.Shutdown(ctx), log) + } else { + log.Infof("Shutting down gracefully.") + ctx := payloadContext(payload, log) + warnOnErr(provider.Shutdown(ctx), log) + } + process.log.Info("Exited.") + }) + return nil +} + // getAdditionalPrincipals returns a list of additional principals to add // to role's service certificates. func (process *TeleportProcess) getAdditionalPrincipals(role types.SystemRole) ([]string, []string, error) { From 9377f7c681cf5d17a3cdf1d496ba46638b648fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Skrz=C4=99tnicki?= Date: Fri, 27 May 2022 01:26:35 +0200 Subject: [PATCH 2/2] New commands: `tctl sso test`, `tctl sso configure` for GitHub (#12783) * Implement `tctl sso` commands for GitHub auth. * Mark RFDs as implemented. --- api/types/github.go | 22 +- api/types/types.pb.go | 2391 +++++++++++------ api/types/types.proto | 34 + lib/auth/apiserver.go | 11 +- lib/auth/auth_test.go | 10 +- lib/auth/auth_with_roles.go | 23 +- lib/auth/auth_with_roles_test.go | 177 +- lib/auth/clt.go | 21 +- lib/auth/github.go | 269 +- lib/auth/github_test.go | 94 +- lib/auth/oidc.go | 8 +- lib/auth/saml.go | 12 +- lib/auth/saml_test.go | 4 +- lib/services/identity.go | 16 +- lib/services/identity_test.go | 106 + lib/services/local/users.go | 11 +- lib/web/apiserver.go | 15 +- lib/web/apiserver_test.go | 10 +- lib/web/resources_test.go | 33 +- rfd/0070-tctl-sso-configure-command.md | 8 +- rfd/0071-tctl-sso-test-command.md | 2 +- tool/tctl/main.go | 4 + tool/tctl/sso/configure/command.go | 74 + tool/tctl/sso/configure/github.go | 161 ++ tool/tctl/sso/configure/teams_to_logins.go | 66 + .../sso/configure/teams_to_logins_test.go | 89 + tool/tctl/sso/tester/command.go | 282 ++ tool/tctl/sso/tester/format.go | 64 + tool/tctl/sso/tester/format_test.go | 250 ++ tool/tctl/sso/tester/github.go | 102 + tool/tctl/sso/tester/utils.go | 32 + 31 files changed, 3403 insertions(+), 998 deletions(-) create mode 100644 tool/tctl/sso/configure/command.go create mode 100644 tool/tctl/sso/configure/github.go create mode 100644 tool/tctl/sso/configure/teams_to_logins.go create mode 100644 tool/tctl/sso/configure/teams_to_logins_test.go create mode 100644 tool/tctl/sso/tester/command.go create mode 100644 tool/tctl/sso/tester/format.go create mode 100644 tool/tctl/sso/tester/format_test.go create mode 100644 tool/tctl/sso/tester/github.go create mode 100644 tool/tctl/sso/tester/utils.go diff --git a/api/types/github.go b/api/types/github.go index 1a6ad75377444..411705880207f 100644 --- a/api/types/github.go +++ b/api/types/github.go @@ -69,16 +69,6 @@ func NewGithubConnector(name string, spec GithubConnectorSpecV3) (GithubConnecto return c, nil } -// GithubClaims represents Github user information obtained during OAuth2 flow -type GithubClaims struct { - // Username is the user's username - Username string - // OrganizationToTeams is the user's organization and team membership - OrganizationToTeams map[string][]string - // Teams is the users team membership - Teams []string -} - // GetVersion returns resource version func (c *GithubConnectorV3) GetVersion() string { return c.Version @@ -161,6 +151,18 @@ func (c *GithubConnectorV3) CheckAndSetDefaults() error { if err := c.Metadata.CheckAndSetDefaults(); err != nil { return trace.Wrap(err) } + + // make sure claim mappings have either roles or a role template + for i, v := range c.Spec.TeamsToLogins { + if v.Team == "" { + return trace.BadParameter("team_to_logins mapping #%v is invalid, team is empty.", i+1) + } + } + + if len(c.Spec.TeamsToLogins) == 0 { + return trace.BadParameter("team_to_logins mapping is invalid, no mappings defined.") + } + return nil } diff --git a/api/types/types.pb.go b/api/types/types.pb.go index 69a8442f3aee2..8a244c9d8dfc8 100644 --- a/api/types/types.pb.go +++ b/api/types/types.pb.go @@ -7960,9 +7960,15 @@ type SSODiagnosticInfo struct { OIDCTraitsFromClaims github_com_gravitational_teleport_api_types_wrappers.Traits `protobuf:"bytes,24,opt,name=OIDCTraitsFromClaims,proto3,customtype=github.com/gravitational/teleport/api/types/wrappers.Traits" json:"oidc_traits_from_claims,omitempty"` // OIDCConnectorTraitMapping represents connector-specific trait mapping. OIDCConnectorTraitMapping []TraitMapping `protobuf:"bytes,25,rep,name=OIDCConnectorTraitMapping,proto3" json:"oidc_connector_trait_mapping,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // GithubClaims represents Github user information obtained during OAuth2 flow. + GithubClaims *GithubClaims `protobuf:"bytes,30,opt,name=GithubClaims,proto3" json:"github_claims,omitempty"` + // GithubTeamsToLogins is TeamsToLogins mapping from Github connector used in the SSO flow. + GithubTeamsToLogins []TeamMapping `protobuf:"bytes,31,rep,name=GithubTeamsToLogins,proto3" json:"github_teams_to_logins,omitempty"` + // GithubTokenInfo stores diagnostic info about Github OAuth2 token obtained during SSO flow. + GithubTokenInfo *GithubTokenInfo `protobuf:"bytes,32,opt,name=GithubTokenInfo,proto3" json:"github_token_info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *SSODiagnosticInfo) Reset() { *m = SSODiagnosticInfo{} } @@ -7998,6 +8004,96 @@ func (m *SSODiagnosticInfo) XXX_DiscardUnknown() { var xxx_messageInfo_SSODiagnosticInfo proto.InternalMessageInfo +// GithubTokenInfo stores diagnostic info about Github OAuth2 token obtained during SSO flow. +// The token itself is secret and therefore not included. +type GithubTokenInfo struct { + TokenType string `protobuf:"bytes,1,opt,name=TokenType,proto3" json:"token_type"` + Expires int64 `protobuf:"varint,2,opt,name=Expires,proto3" json:"expires"` + Scope string `protobuf:"bytes,3,opt,name=Scope,proto3" json:"scope"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GithubTokenInfo) Reset() { *m = GithubTokenInfo{} } +func (m *GithubTokenInfo) String() string { return proto.CompactTextString(m) } +func (*GithubTokenInfo) ProtoMessage() {} +func (*GithubTokenInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_d938547f84707355, []int{151} +} +func (m *GithubTokenInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GithubTokenInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GithubTokenInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GithubTokenInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_GithubTokenInfo.Merge(m, src) +} +func (m *GithubTokenInfo) XXX_Size() int { + return m.Size() +} +func (m *GithubTokenInfo) XXX_DiscardUnknown() { + xxx_messageInfo_GithubTokenInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_GithubTokenInfo proto.InternalMessageInfo + +// GithubClaims represents Github user information obtained during OAuth2 flow +type GithubClaims struct { + // Username is the user's username + Username string `protobuf:"bytes,1,opt,name=Username,proto3" json:"username"` + // OrganizationToTeams is the user's organization and team membership + OrganizationToTeams github_com_gravitational_teleport_api_types_wrappers.Traits `protobuf:"bytes,2,opt,name=OrganizationToTeams,proto3,customtype=github.com/gravitational/teleport/api/types/wrappers.Traits" json:"organization_to_teams"` + // Teams is the users team membership + Teams []string `protobuf:"bytes,3,rep,name=Teams,proto3" json:"teams"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GithubClaims) Reset() { *m = GithubClaims{} } +func (m *GithubClaims) String() string { return proto.CompactTextString(m) } +func (*GithubClaims) ProtoMessage() {} +func (*GithubClaims) Descriptor() ([]byte, []int) { + return fileDescriptor_d938547f84707355, []int{152} +} +func (m *GithubClaims) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GithubClaims) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GithubClaims.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GithubClaims) XXX_Merge(src proto.Message) { + xxx_messageInfo_GithubClaims.Merge(m, src) +} +func (m *GithubClaims) XXX_Size() int { + return m.Size() +} +func (m *GithubClaims) XXX_DiscardUnknown() { + xxx_messageInfo_GithubClaims.DiscardUnknown(m) +} + +var xxx_messageInfo_GithubClaims proto.InternalMessageInfo + // TeamMapping represents a single team membership mapping. type TeamMapping struct { // Organization is a Github organization a user belongs to. @@ -8019,7 +8115,7 @@ func (m *TeamMapping) Reset() { *m = TeamMapping{} } func (m *TeamMapping) String() string { return proto.CompactTextString(m) } func (*TeamMapping) ProtoMessage() {} func (*TeamMapping) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{151} + return fileDescriptor_d938547f84707355, []int{153} } func (m *TeamMapping) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8068,7 +8164,7 @@ type TrustedClusterV2 struct { func (m *TrustedClusterV2) Reset() { *m = TrustedClusterV2{} } func (*TrustedClusterV2) ProtoMessage() {} func (*TrustedClusterV2) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{152} + return fileDescriptor_d938547f84707355, []int{154} } func (m *TrustedClusterV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8110,7 +8206,7 @@ func (m *TrustedClusterV2List) Reset() { *m = TrustedClusterV2List{} } func (m *TrustedClusterV2List) String() string { return proto.CompactTextString(m) } func (*TrustedClusterV2List) ProtoMessage() {} func (*TrustedClusterV2List) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{153} + return fileDescriptor_d938547f84707355, []int{155} } func (m *TrustedClusterV2List) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8166,7 +8262,7 @@ func (m *TrustedClusterSpecV2) Reset() { *m = TrustedClusterSpecV2{} } func (m *TrustedClusterSpecV2) String() string { return proto.CompactTextString(m) } func (*TrustedClusterSpecV2) ProtoMessage() {} func (*TrustedClusterSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{154} + return fileDescriptor_d938547f84707355, []int{156} } func (m *TrustedClusterSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8219,7 +8315,7 @@ func (m *LockV2) Reset() { *m = LockV2{} } func (m *LockV2) String() string { return proto.CompactTextString(m) } func (*LockV2) ProtoMessage() {} func (*LockV2) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{155} + return fileDescriptor_d938547f84707355, []int{157} } func (m *LockV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8265,7 +8361,7 @@ func (m *LockSpecV2) Reset() { *m = LockSpecV2{} } func (m *LockSpecV2) String() string { return proto.CompactTextString(m) } func (*LockSpecV2) ProtoMessage() {} func (*LockSpecV2) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{156} + return fileDescriptor_d938547f84707355, []int{158} } func (m *LockSpecV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8320,7 +8416,7 @@ type LockTarget struct { func (m *LockTarget) Reset() { *m = LockTarget{} } func (*LockTarget) ProtoMessage() {} func (*LockTarget) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{157} + return fileDescriptor_d938547f84707355, []int{159} } func (m *LockTarget) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8364,7 +8460,7 @@ func (m *AddressCondition) Reset() { *m = AddressCondition{} } func (m *AddressCondition) String() string { return proto.CompactTextString(m) } func (*AddressCondition) ProtoMessage() {} func (*AddressCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{158} + return fileDescriptor_d938547f84707355, []int{160} } func (m *AddressCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8407,7 +8503,7 @@ func (m *NetworkRestrictionsSpecV4) Reset() { *m = NetworkRestrictionsSp func (m *NetworkRestrictionsSpecV4) String() string { return proto.CompactTextString(m) } func (*NetworkRestrictionsSpecV4) ProtoMessage() {} func (*NetworkRestrictionsSpecV4) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{159} + return fileDescriptor_d938547f84707355, []int{161} } func (m *NetworkRestrictionsSpecV4) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8460,7 +8556,7 @@ func (m *NetworkRestrictionsV4) Reset() { *m = NetworkRestrictionsV4{} } func (m *NetworkRestrictionsV4) String() string { return proto.CompactTextString(m) } func (*NetworkRestrictionsV4) ProtoMessage() {} func (*NetworkRestrictionsV4) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{160} + return fileDescriptor_d938547f84707355, []int{162} } func (m *NetworkRestrictionsV4) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8504,7 +8600,7 @@ func (m *WindowsDesktopServiceV3) Reset() { *m = WindowsDesktopServiceV3 func (m *WindowsDesktopServiceV3) String() string { return proto.CompactTextString(m) } func (*WindowsDesktopServiceV3) ProtoMessage() {} func (*WindowsDesktopServiceV3) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{161} + return fileDescriptor_d938547f84707355, []int{163} } func (m *WindowsDesktopServiceV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8550,7 +8646,7 @@ func (m *WindowsDesktopServiceSpecV3) Reset() { *m = WindowsDesktopServi func (m *WindowsDesktopServiceSpecV3) String() string { return proto.CompactTextString(m) } func (*WindowsDesktopServiceSpecV3) ProtoMessage() {} func (*WindowsDesktopServiceSpecV3) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{162} + return fileDescriptor_d938547f84707355, []int{164} } func (m *WindowsDesktopServiceSpecV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8594,7 +8690,7 @@ func (m *WindowsDesktopFilter) Reset() { *m = WindowsDesktopFilter{} } func (m *WindowsDesktopFilter) String() string { return proto.CompactTextString(m) } func (*WindowsDesktopFilter) ProtoMessage() {} func (*WindowsDesktopFilter) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{163} + return fileDescriptor_d938547f84707355, []int{165} } func (m *WindowsDesktopFilter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8638,7 +8734,7 @@ func (m *WindowsDesktopV3) Reset() { *m = WindowsDesktopV3{} } func (m *WindowsDesktopV3) String() string { return proto.CompactTextString(m) } func (*WindowsDesktopV3) ProtoMessage() {} func (*WindowsDesktopV3) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{164} + return fileDescriptor_d938547f84707355, []int{166} } func (m *WindowsDesktopV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8684,7 +8780,7 @@ func (m *WindowsDesktopSpecV3) Reset() { *m = WindowsDesktopSpecV3{} } func (m *WindowsDesktopSpecV3) String() string { return proto.CompactTextString(m) } func (*WindowsDesktopSpecV3) ProtoMessage() {} func (*WindowsDesktopSpecV3) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{165} + return fileDescriptor_d938547f84707355, []int{167} } func (m *WindowsDesktopSpecV3) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8750,7 +8846,7 @@ func (m *RegisterUsingTokenRequest) Reset() { *m = RegisterUsingTokenReq func (m *RegisterUsingTokenRequest) String() string { return proto.CompactTextString(m) } func (*RegisterUsingTokenRequest) ProtoMessage() {} func (*RegisterUsingTokenRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{166} + return fileDescriptor_d938547f84707355, []int{168} } func (m *RegisterUsingTokenRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8804,7 +8900,7 @@ func (m *RecoveryCodesV1) Reset() { *m = RecoveryCodesV1{} } func (m *RecoveryCodesV1) String() string { return proto.CompactTextString(m) } func (*RecoveryCodesV1) ProtoMessage() {} func (*RecoveryCodesV1) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{167} + return fileDescriptor_d938547f84707355, []int{169} } func (m *RecoveryCodesV1) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8849,7 +8945,7 @@ func (m *RecoveryCodesSpecV1) Reset() { *m = RecoveryCodesSpecV1{} } func (m *RecoveryCodesSpecV1) String() string { return proto.CompactTextString(m) } func (*RecoveryCodesSpecV1) ProtoMessage() {} func (*RecoveryCodesSpecV1) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{168} + return fileDescriptor_d938547f84707355, []int{170} } func (m *RecoveryCodesSpecV1) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8893,7 +8989,7 @@ func (m *RecoveryCode) Reset() { *m = RecoveryCode{} } func (m *RecoveryCode) String() string { return proto.CompactTextString(m) } func (*RecoveryCode) ProtoMessage() {} func (*RecoveryCode) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{169} + return fileDescriptor_d938547f84707355, []int{171} } func (m *RecoveryCode) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8937,7 +9033,7 @@ func (m *SessionTrackerV1) Reset() { *m = SessionTrackerV1{} } func (m *SessionTrackerV1) String() string { return proto.CompactTextString(m) } func (*SessionTrackerV1) ProtoMessage() {} func (*SessionTrackerV1) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{170} + return fileDescriptor_d938547f84707355, []int{172} } func (m *SessionTrackerV1) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9027,7 +9123,7 @@ func (m *SessionTrackerSpecV1) Reset() { *m = SessionTrackerSpecV1{} } func (m *SessionTrackerSpecV1) String() string { return proto.CompactTextString(m) } func (*SessionTrackerSpecV1) ProtoMessage() {} func (*SessionTrackerSpecV1) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{171} + return fileDescriptor_d938547f84707355, []int{173} } func (m *SessionTrackerSpecV1) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9074,7 +9170,7 @@ func (m *SessionTrackerPolicySet) Reset() { *m = SessionTrackerPolicySet func (m *SessionTrackerPolicySet) String() string { return proto.CompactTextString(m) } func (*SessionTrackerPolicySet) ProtoMessage() {} func (*SessionTrackerPolicySet) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{172} + return fileDescriptor_d938547f84707355, []int{174} } func (m *SessionTrackerPolicySet) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9122,7 +9218,7 @@ func (m *Participant) Reset() { *m = Participant{} } func (m *Participant) String() string { return proto.CompactTextString(m) } func (*Participant) ProtoMessage() {} func (*Participant) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{173} + return fileDescriptor_d938547f84707355, []int{175} } func (m *Participant) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9166,7 +9262,7 @@ func (m *SortBy) Reset() { *m = SortBy{} } func (m *SortBy) String() string { return proto.CompactTextString(m) } func (*SortBy) ProtoMessage() {} func (*SortBy) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{174} + return fileDescriptor_d938547f84707355, []int{176} } func (m *SortBy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -9373,6 +9469,8 @@ func init() { proto.RegisterType((*SSOWarnings)(nil), "types.SSOWarnings") proto.RegisterType((*CreateUserParams)(nil), "types.CreateUserParams") proto.RegisterType((*SSODiagnosticInfo)(nil), "types.SSODiagnosticInfo") + proto.RegisterType((*GithubTokenInfo)(nil), "types.GithubTokenInfo") + proto.RegisterType((*GithubClaims)(nil), "types.GithubClaims") proto.RegisterType((*TeamMapping)(nil), "types.TeamMapping") proto.RegisterType((*TrustedClusterV2)(nil), "types.TrustedClusterV2") proto.RegisterType((*TrustedClusterV2List)(nil), "types.TrustedClusterV2List") @@ -9402,799 +9500,810 @@ func init() { func init() { proto.RegisterFile("types.proto", fileDescriptor_d938547f84707355) } var fileDescriptor_d938547f84707355 = []byte{ - // 12664 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x7d, 0x6d, 0x6c, 0x1c, 0x49, - 0x76, 0x98, 0x7a, 0x66, 0x48, 0x0e, 0x1f, 0x87, 0xe4, 0xb0, 0x48, 0x49, 0x94, 0x56, 0xbb, 0xa3, - 0xed, 0xdd, 0xd5, 0x6a, 0xb5, 0xbb, 0xd2, 0x89, 0xba, 0xdd, 0xf3, 0xde, 0x7e, 0xce, 0x90, 0x94, - 0xc4, 0x15, 0x45, 0x72, 0x7b, 0xf8, 0x71, 0xe7, 0xbb, 0x73, 0xbb, 0x39, 0x5d, 0x24, 0x7b, 0x39, - 0x33, 0x3d, 0xd7, 0xdd, 0x23, 0x89, 0x76, 0x0c, 0xdb, 0x08, 0x2e, 0x86, 0x61, 0xf8, 0xce, 0x17, - 0x9c, 0x63, 0x3b, 0x70, 0x60, 0xc7, 0x88, 0x93, 0x38, 0xc1, 0x19, 0x81, 0x1d, 0x20, 0x09, 0x12, - 0x38, 0x30, 0x10, 0x18, 0x87, 0x20, 0x41, 0xfc, 0x2f, 0xf0, 0x25, 0x60, 0xe2, 0x3b, 0xff, 0x22, - 0x10, 0x20, 0x80, 0x7f, 0xf9, 0x12, 0x03, 0x41, 0xbd, 0xaa, 0xea, 0xae, 0xea, 0xe9, 0x21, 0x87, - 0x2b, 0x2d, 0x62, 0xed, 0x2f, 0x72, 0x5e, 0xbd, 0xf7, 0xaa, 0xba, 0xea, 0xd5, 0xab, 0x57, 0xaf, - 0x5e, 0xbd, 0x82, 0xb1, 0xe8, 0xa0, 0x43, 0xc3, 0xeb, 0x9d, 0xc0, 0x8f, 0x7c, 0x32, 0x84, 0x3f, - 0x2e, 0xce, 0xec, 0xfa, 0xbb, 0x3e, 0x42, 0x6e, 0xb0, 0xff, 0x78, 0xe1, 0xc5, 0xca, 0xae, 0xef, - 0xef, 0x36, 0xe9, 0x0d, 0xfc, 0xb5, 0xdd, 0xdd, 0xb9, 0x11, 0x79, 0x2d, 0x1a, 0x46, 0x4e, 0xab, - 0x23, 0x10, 0xe6, 0x77, 0xbd, 0x68, 0xaf, 0xbb, 0x7d, 0xbd, 0xe1, 0xb7, 0x6e, 0xec, 0x06, 0xce, - 0x03, 0x2f, 0x72, 0x22, 0xcf, 0x6f, 0x3b, 0xcd, 0x1b, 0x11, 0x6d, 0xd2, 0x8e, 0x1f, 0x44, 0x37, - 0x9c, 0x8e, 0x77, 0x03, 0xeb, 0xb8, 0xf1, 0x30, 0x70, 0x3a, 0x1d, 0x1a, 0x24, 0xff, 0x70, 0x26, - 0xe6, 0x3f, 0xcc, 0xc3, 0xe8, 0x3d, 0x4a, 0x3b, 0xd5, 0xa6, 0xf7, 0x80, 0x92, 0x17, 0xa0, 0xb0, - 0xe2, 0xb4, 0xe8, 0xac, 0x71, 0xd9, 0xb8, 0x3a, 0x5a, 0x9b, 0x3c, 0x3a, 0xac, 0x8c, 0x85, 0x34, - 0x78, 0x40, 0x03, 0xbb, 0xed, 0xb4, 0xa8, 0x85, 0x85, 0xe4, 0x55, 0x18, 0x65, 0x7f, 0xc3, 0x8e, - 0xd3, 0xa0, 0xb3, 0x39, 0xc4, 0x1c, 0x3f, 0x3a, 0xac, 0x8c, 0xb6, 0x25, 0xd0, 0x4a, 0xca, 0xc9, - 0x15, 0x18, 0x59, 0xa6, 0x4e, 0x48, 0x97, 0x16, 0x66, 0xf3, 0x97, 0x8d, 0xab, 0xf9, 0x5a, 0xe9, - 0xe8, 0xb0, 0x52, 0x6c, 0x32, 0x90, 0xed, 0xb9, 0x96, 0x2c, 0x24, 0x4b, 0x30, 0xb2, 0xf8, 0xa8, - 0xe3, 0x05, 0x34, 0x9c, 0x2d, 0x5c, 0x36, 0xae, 0x8e, 0xcd, 0x5d, 0xbc, 0xce, 0xbf, 0xff, 0xba, - 0xfc, 0xfe, 0xeb, 0xeb, 0xf2, 0xfb, 0x6b, 0xd3, 0xdf, 0x3b, 0xac, 0x9c, 0x39, 0x3a, 0xac, 0x8c, - 0x50, 0x4e, 0xf2, 0x2b, 0xff, 0xa3, 0x62, 0x58, 0x92, 0x9e, 0xbc, 0x03, 0x85, 0xf5, 0x83, 0x0e, - 0x9d, 0x1d, 0xbd, 0x6c, 0x5c, 0x9d, 0x98, 0x7b, 0xee, 0x3a, 0xef, 0xf1, 0xf8, 0x23, 0x93, 0xff, - 0x18, 0x56, 0xad, 0x78, 0x74, 0x58, 0x29, 0x30, 0x14, 0x0b, 0xa9, 0xc8, 0xeb, 0x30, 0x7c, 0xd7, - 0x0f, 0xa3, 0xa5, 0x85, 0x59, 0xc0, 0x4f, 0x3b, 0x7b, 0x74, 0x58, 0x99, 0xda, 0xf3, 0xc3, 0xc8, - 0xf6, 0xdc, 0xd7, 0xfc, 0x96, 0x17, 0xd1, 0x56, 0x27, 0x3a, 0xb0, 0x04, 0x92, 0xb9, 0x0d, 0xe3, - 0x1a, 0x3f, 0x32, 0x06, 0x23, 0x1b, 0x2b, 0xf7, 0x56, 0x56, 0xb7, 0x56, 0xca, 0x67, 0x48, 0x11, - 0x0a, 0x2b, 0xab, 0x0b, 0x8b, 0x65, 0x83, 0x8c, 0x40, 0xbe, 0xba, 0xb6, 0x56, 0xce, 0x91, 0x12, - 0x14, 0x17, 0xaa, 0xeb, 0xd5, 0x5a, 0xb5, 0xbe, 0x58, 0xce, 0x93, 0x69, 0x98, 0xdc, 0x5a, 0x5a, - 0x59, 0x58, 0xdd, 0xaa, 0xdb, 0x0b, 0x8b, 0xf5, 0x7b, 0xeb, 0xab, 0x6b, 0xe5, 0x02, 0x99, 0x00, - 0xb8, 0xb7, 0x51, 0x5b, 0xb4, 0x56, 0x16, 0xd7, 0x17, 0xeb, 0xe5, 0x21, 0xf3, 0x17, 0xf2, 0x50, - 0xbc, 0x4f, 0x23, 0xc7, 0x75, 0x22, 0x87, 0x5c, 0xd2, 0x86, 0x08, 0x5b, 0xaf, 0x8c, 0xcd, 0x0b, - 0xbd, 0x63, 0x33, 0x74, 0x74, 0x58, 0x31, 0x5e, 0x57, 0xc7, 0xe4, 0x6d, 0x18, 0x5b, 0xa0, 0x61, - 0x23, 0xf0, 0x3a, 0x4c, 0x5e, 0x70, 0x5c, 0x46, 0x6b, 0x17, 0x8e, 0x0e, 0x2b, 0x67, 0xdd, 0x04, - 0xac, 0x7c, 0xab, 0x8a, 0x4d, 0x96, 0x60, 0x78, 0xd9, 0xd9, 0xa6, 0xcd, 0x70, 0x76, 0xe8, 0x72, - 0xfe, 0xea, 0xd8, 0xdc, 0x33, 0xa2, 0x7f, 0x65, 0x03, 0xaf, 0xf3, 0xd2, 0xc5, 0x76, 0x14, 0x1c, - 0xd4, 0x66, 0x8e, 0x0e, 0x2b, 0xe5, 0x26, 0x02, 0xd4, 0xbe, 0xe3, 0x28, 0xa4, 0x9e, 0x8c, 0xf9, - 0xf0, 0x89, 0x63, 0xfe, 0xec, 0xf7, 0x0e, 0x2b, 0x06, 0x1b, 0x0b, 0x31, 0xe6, 0x09, 0x3f, 0x7d, - 0xf4, 0x2f, 0x43, 0x6e, 0x69, 0x61, 0x76, 0x04, 0x65, 0xad, 0x7c, 0x74, 0x58, 0x29, 0x69, 0xc3, - 0x96, 0x5b, 0x5a, 0xb8, 0xf8, 0x16, 0x8c, 0x29, 0x6d, 0x24, 0x65, 0xc8, 0xef, 0xd3, 0x03, 0xde, - 0x9f, 0x16, 0xfb, 0x97, 0xcc, 0xc0, 0xd0, 0x03, 0xa7, 0xd9, 0x15, 0x1d, 0x68, 0xf1, 0x1f, 0x5f, - 0xcc, 0xfd, 0x98, 0x61, 0xfe, 0xdd, 0x02, 0x14, 0x2d, 0x9f, 0xcf, 0x33, 0xf2, 0x0a, 0x0c, 0xd5, - 0x23, 0x27, 0x92, 0x43, 0x31, 0x7d, 0x74, 0x58, 0x99, 0x0c, 0x19, 0x40, 0xa9, 0x8f, 0x63, 0x30, - 0xd4, 0xb5, 0x3d, 0x27, 0x94, 0x43, 0x82, 0xa8, 0x1d, 0x06, 0x50, 0x51, 0x11, 0x83, 0x5c, 0x81, - 0xc2, 0x7d, 0xdf, 0xa5, 0x62, 0x54, 0xc8, 0xd1, 0x61, 0x65, 0xa2, 0xe5, 0xbb, 0x2a, 0x22, 0x96, - 0x93, 0xd7, 0x60, 0x74, 0xbe, 0x1b, 0x04, 0xb4, 0xcd, 0x44, 0xb5, 0x80, 0xc8, 0x13, 0x47, 0x87, - 0x15, 0x68, 0x70, 0x20, 0x9b, 0x5c, 0x09, 0x02, 0xeb, 0xea, 0x7a, 0xe4, 0x04, 0x11, 0x75, 0x67, - 0x87, 0x06, 0xea, 0x6a, 0x36, 0xbd, 0xa6, 0x42, 0x4e, 0x92, 0xee, 0x6a, 0xc1, 0x89, 0xdc, 0x85, - 0xb1, 0x3b, 0x81, 0xd3, 0xa0, 0x6b, 0x34, 0xf0, 0x7c, 0x17, 0xc7, 0x30, 0x5f, 0xbb, 0x72, 0x74, - 0x58, 0x39, 0xb7, 0xcb, 0xc0, 0x76, 0x07, 0xe1, 0x09, 0xf5, 0x8f, 0x0e, 0x2b, 0xc5, 0x85, 0x6e, - 0x80, 0xbd, 0x67, 0xa9, 0xa4, 0xe4, 0x27, 0xd9, 0x90, 0x84, 0x11, 0x76, 0x2d, 0x75, 0x71, 0xf4, - 0x8e, 0x6f, 0xa2, 0x29, 0x9a, 0x78, 0xae, 0xe9, 0x84, 0x91, 0x1d, 0x70, 0xba, 0x54, 0x3b, 0x55, - 0x96, 0x64, 0x15, 0x8a, 0xf5, 0xc6, 0x1e, 0x75, 0xbb, 0x4d, 0x3a, 0x5b, 0x44, 0xf6, 0xe7, 0x85, - 0xe0, 0xca, 0xf1, 0x94, 0xc5, 0xb5, 0x8b, 0x82, 0x37, 0x09, 0x05, 0x44, 0xe9, 0xfb, 0x98, 0xc9, - 0x17, 0x8b, 0xbf, 0xfe, 0xdb, 0x95, 0x33, 0x3f, 0xf7, 0xdf, 0x2f, 0x9f, 0x31, 0xff, 0x55, 0x0e, - 0xca, 0x69, 0x26, 0x64, 0x07, 0xc6, 0x37, 0x3a, 0xae, 0x13, 0xd1, 0xf9, 0xa6, 0x47, 0xdb, 0x51, - 0x88, 0x42, 0x72, 0xfc, 0x37, 0xbd, 0x28, 0xea, 0x9d, 0xed, 0x22, 0xa1, 0xdd, 0xe0, 0x94, 0xa9, - 0xaf, 0xd2, 0xd9, 0x26, 0xf5, 0xd4, 0x51, 0x4f, 0x87, 0x28, 0x61, 0xa7, 0xab, 0x87, 0x6b, 0xf8, - 0x3e, 0xf5, 0x08, 0xb6, 0x42, 0x80, 0xda, 0xee, 0xf6, 0x01, 0x4a, 0xe6, 0xe0, 0x02, 0xc4, 0x48, - 0x32, 0x04, 0x88, 0x81, 0xcd, 0xbf, 0x30, 0x60, 0xc2, 0xa2, 0xa1, 0xdf, 0x0d, 0x1a, 0xf4, 0x2e, - 0x75, 0x5c, 0x1a, 0x30, 0xf1, 0xbf, 0xe7, 0xb5, 0x5d, 0x31, 0xa7, 0x50, 0xfc, 0xf7, 0xbd, 0xb6, - 0x3a, 0x85, 0xb1, 0x9c, 0x7c, 0x0e, 0x46, 0xea, 0xdd, 0x6d, 0x44, 0xe5, 0x73, 0xea, 0x1c, 0x8e, - 0x58, 0x77, 0xdb, 0x4e, 0xa1, 0x4b, 0x34, 0x72, 0x03, 0x46, 0x36, 0x69, 0x10, 0x26, 0x1a, 0x0f, - 0x35, 0xfb, 0x03, 0x0e, 0x52, 0x09, 0x04, 0x16, 0xb9, 0x93, 0x68, 0x5d, 0xb1, 0x26, 0x4d, 0xa6, - 0x74, 0x5d, 0x22, 0x2a, 0x2d, 0x01, 0x51, 0x45, 0x45, 0x62, 0x99, 0xdf, 0xce, 0x41, 0x79, 0xc1, - 0x89, 0x9c, 0x6d, 0x27, 0x14, 0xfd, 0xb9, 0x79, 0x8b, 0xe9, 0x71, 0xe5, 0x43, 0x51, 0x8f, 0xb3, - 0x96, 0x7f, 0xe2, 0xcf, 0x7b, 0x29, 0xfd, 0x79, 0x63, 0x6c, 0x81, 0x14, 0x9f, 0x97, 0x7c, 0xd4, - 0xbb, 0x27, 0x7f, 0x54, 0x59, 0x7c, 0x54, 0x51, 0x7e, 0x54, 0xf2, 0x29, 0xe4, 0x5d, 0x28, 0xd4, - 0x3b, 0xb4, 0x21, 0x94, 0x88, 0xd4, 0xfd, 0xfa, 0xc7, 0x31, 0x84, 0xcd, 0x5b, 0xb5, 0x92, 0x60, - 0x53, 0x08, 0x3b, 0xb4, 0x61, 0x21, 0x99, 0x32, 0x69, 0xbe, 0x33, 0x0c, 0x33, 0x59, 0x64, 0xe4, - 0x5d, 0x7d, 0x71, 0xe2, 0xdd, 0xf3, 0x4c, 0xdf, 0xc5, 0x69, 0xd6, 0xd0, 0x97, 0xa7, 0x6b, 0x50, - 0x5c, 0x63, 0x02, 0xd9, 0xf0, 0x9b, 0xa2, 0xe7, 0x98, 0x56, 0x2c, 0x76, 0x24, 0xcc, 0xb0, 0xe2, - 0x72, 0xf2, 0x0c, 0xe4, 0x37, 0xac, 0x25, 0xd1, 0x5d, 0xa3, 0x47, 0x87, 0x95, 0x7c, 0x37, 0xf0, - 0x66, 0x0d, 0x8b, 0x41, 0xc9, 0x0d, 0x18, 0x9e, 0xaf, 0xce, 0xd3, 0x20, 0xc2, 0x6e, 0x2a, 0xd5, - 0xce, 0x33, 0x69, 0x69, 0x38, 0x76, 0x83, 0x06, 0x91, 0x56, 0xbd, 0x40, 0x23, 0xaf, 0x42, 0xbe, - 0xba, 0x55, 0x17, 0x3d, 0x03, 0xa2, 0x67, 0xaa, 0x5b, 0xf5, 0xda, 0xb8, 0xe8, 0x88, 0xbc, 0xf3, - 0x30, 0x64, 0xdc, 0xab, 0x5b, 0x75, 0x75, 0xb4, 0x86, 0x8f, 0x19, 0xad, 0xab, 0x50, 0x64, 0x76, - 0x06, 0x5b, 0xe0, 0x51, 0x29, 0x8e, 0x72, 0xf3, 0x69, 0x4f, 0xc0, 0xac, 0xb8, 0x94, 0xbc, 0x10, - 0x9b, 0x2d, 0xc5, 0x84, 0x9f, 0x30, 0x5b, 0xa4, 0xb1, 0x42, 0x1e, 0xc1, 0xf8, 0xc2, 0x41, 0xdb, - 0x69, 0x79, 0x0d, 0xb1, 0x84, 0x8f, 0xe2, 0x12, 0x7e, 0xfd, 0x98, 0x61, 0xbc, 0xae, 0x11, 0xf0, - 0x55, 0x5d, 0x2a, 0xdf, 0x59, 0x97, 0x97, 0xd9, 0xe9, 0x15, 0x7e, 0xd6, 0xb0, 0xf4, 0x8a, 0xd8, - 0x5c, 0x92, 0x2a, 0x12, 0xed, 0xaa, 0x44, 0xec, 0x24, 0x38, 0x99, 0x4b, 0x81, 0x80, 0xa8, 0x73, - 0x29, 0x5e, 0x74, 0xdf, 0x85, 0xfc, 0x9d, 0xf9, 0xb5, 0xd9, 0x31, 0xe4, 0x41, 0x04, 0x8f, 0x3b, - 0xf3, 0x6b, 0xf3, 0x4d, 0xbf, 0xeb, 0xd6, 0x3f, 0x5a, 0xae, 0x9d, 0x17, 0x6c, 0xc6, 0x77, 0x1b, - 0x1d, 0xad, 0x45, 0x8c, 0x8e, 0x2c, 0x42, 0x51, 0x7e, 0xe5, 0x6c, 0x09, 0x79, 0x4c, 0xa5, 0x3e, - 0x7e, 0xf3, 0x16, 0x9f, 0x6b, 0xae, 0xf8, 0xad, 0xb6, 0x42, 0xe2, 0x5c, 0xdc, 0x02, 0xd2, 0xdb, - 0x2f, 0x19, 0x96, 0xc4, 0xab, 0xaa, 0x25, 0x31, 0x36, 0x77, 0x56, 0xd4, 0x35, 0xef, 0xb7, 0x5a, - 0x4e, 0xdb, 0x45, 0xda, 0xcd, 0x39, 0xd5, 0xc0, 0xa8, 0xc2, 0x44, 0xd2, 0x90, 0x65, 0x2f, 0x8c, - 0xc8, 0x0d, 0x18, 0x95, 0x10, 0xb6, 0x88, 0xe4, 0x33, 0x9b, 0x6c, 0x25, 0x38, 0xe6, 0x9f, 0xe4, - 0x00, 0x92, 0x92, 0xa7, 0x54, 0xcf, 0x7c, 0x41, 0xd3, 0x33, 0x67, 0xd3, 0x02, 0xda, 0x57, 0xc3, - 0x90, 0xf7, 0x61, 0x98, 0x99, 0x5c, 0x5d, 0x69, 0x52, 0x9e, 0x4f, 0x93, 0x62, 0xe1, 0xe6, 0xad, - 0xda, 0x84, 0x20, 0x1e, 0x0e, 0x11, 0x62, 0x09, 0x32, 0x45, 0x45, 0xfd, 0xc1, 0x50, 0x32, 0x18, - 0x42, 0x39, 0x5d, 0x55, 0xb4, 0x8b, 0x91, 0xcc, 0x47, 0xa9, 0x5d, 0x14, 0xdd, 0x72, 0x81, 0xeb, - 0x16, 0xde, 0xa9, 0x23, 0x42, 0xb7, 0xa4, 0x35, 0x0b, 0xef, 0xc0, 0x13, 0x35, 0x4b, 0x27, 0x3d, - 0x6d, 0x0b, 0x28, 0x06, 0x57, 0x33, 0x7b, 0x25, 0x6b, 0xc2, 0x5e, 0x3e, 0x69, 0xc2, 0xa6, 0xa7, - 0xeb, 0xad, 0x7e, 0xba, 0xec, 0xac, 0x9c, 0x5d, 0xce, 0x43, 0x95, 0x1c, 0x75, 0xda, 0xdb, 0x7c, - 0x6a, 0x0e, 0xf7, 0x9d, 0x9a, 0x67, 0x33, 0xa7, 0x26, 0x9f, 0x98, 0x6f, 0xc3, 0x50, 0xf5, 0xa7, - 0xba, 0x01, 0x15, 0xb6, 0x5f, 0x49, 0xd6, 0xc9, 0x60, 0xf1, 0x9c, 0x9e, 0x74, 0xd8, 0x4f, 0xd5, - 0x66, 0xc6, 0x72, 0x56, 0xf3, 0xfa, 0x72, 0x5d, 0xd8, 0x75, 0x24, 0xd5, 0x2d, 0xeb, 0xcb, 0x4a, - 0xb3, 0x23, 0xed, 0xab, 0x19, 0x15, 0xb9, 0x01, 0xb9, 0xea, 0x02, 0x6e, 0x16, 0xc7, 0xe6, 0x46, - 0x65, 0xb5, 0x0b, 0xb5, 0x19, 0x41, 0x52, 0x72, 0xb4, 0xfd, 0x43, 0x75, 0x81, 0xd4, 0x60, 0xe8, - 0xfe, 0x41, 0xfd, 0xa3, 0x65, 0xa1, 0xc8, 0xa6, 0xa5, 0x5c, 0x33, 0xd8, 0x2a, 0xae, 0x42, 0x61, - 0xd2, 0xe2, 0xd6, 0x41, 0xf8, 0xf5, 0xa6, 0xda, 0x62, 0x44, 0xfb, 0xf4, 0x14, 0xc8, 0xbf, 0x30, - 0x14, 0x5b, 0x43, 0xc8, 0x3a, 0xdb, 0xd3, 0x0a, 0x89, 0x33, 0x12, 0xcb, 0xa7, 0x47, 0xe2, 0x62, - 0x79, 0x7b, 0x85, 0x8f, 0x7e, 0xae, 0x67, 0xf4, 0xc7, 0x94, 0x95, 0x8c, 0x8f, 0x79, 0xdc, 0x17, - 0xf9, 0x4f, 0xdc, 0x17, 0xe6, 0x1f, 0xe5, 0xb0, 0x3e, 0xf2, 0x1a, 0x0c, 0x5b, 0x74, 0x37, 0x59, - 0xf4, 0x71, 0xf3, 0x18, 0x20, 0x44, 0x6d, 0x24, 0xc7, 0xc1, 0x15, 0x85, 0xba, 0xe1, 0x9e, 0xb7, - 0x13, 0x89, 0x96, 0xc6, 0x2b, 0x8a, 0x00, 0x2b, 0x2b, 0x8a, 0x80, 0x68, 0x2b, 0x8a, 0x80, 0x31, - 0x59, 0xb7, 0x16, 0xea, 0xe2, 0x03, 0xe4, 0xd7, 0x5a, 0x0b, 0x8a, 0xd0, 0x04, 0xae, 0x26, 0x34, - 0xd6, 0x42, 0x9d, 0xbc, 0x09, 0xa3, 0xd5, 0x46, 0xc3, 0xef, 0x2a, 0xbb, 0xaf, 0xd9, 0xa3, 0xc3, - 0xca, 0x8c, 0xc3, 0x81, 0xba, 0xaf, 0x20, 0x41, 0x25, 0x75, 0x18, 0x5b, 0x64, 0x5b, 0x16, 0x6f, - 0xde, 0x69, 0xec, 0x51, 0x31, 0xc1, 0xa4, 0xc4, 0x2a, 0x25, 0xb1, 0x09, 0x7d, 0x96, 0x22, 0xb0, - 0xc1, 0x80, 0xea, 0x96, 0x5c, 0xc1, 0x35, 0x6b, 0x49, 0x57, 0xb0, 0x86, 0xcd, 0x37, 0xbb, 0x61, - 0x44, 0x83, 0xa5, 0x05, 0xd1, 0x8f, 0xd8, 0xb0, 0x06, 0x07, 0xa6, 0x1a, 0x16, 0xa3, 0x9a, 0xff, - 0xcd, 0xc0, 0x6e, 0x20, 0x6f, 0x01, 0x2c, 0xb5, 0x99, 0xd9, 0xde, 0xa0, 0x31, 0x03, 0x74, 0x0d, - 0x78, 0x02, 0xaa, 0x73, 0x50, 0x90, 0xf5, 0xaa, 0x73, 0x03, 0x57, 0xcd, 0xaa, 0x94, 0x9b, 0x00, - 0xe1, 0x25, 0x12, 0x55, 0x06, 0x02, 0x9a, 0xaa, 0x32, 0x41, 0x26, 0x57, 0x60, 0x64, 0xa9, 0x7a, - 0xbf, 0xda, 0x8d, 0xf6, 0x70, 0x10, 0x8a, 0x5c, 0x1d, 0x7b, 0x4e, 0xcb, 0x76, 0xba, 0xd1, 0x9e, - 0x25, 0x0b, 0xcd, 0xff, 0x98, 0xd3, 0xfa, 0x9d, 0x58, 0x40, 0x2c, 0xda, 0x69, 0x7a, 0x0d, 0x34, - 0x2a, 0xee, 0x04, 0x7e, 0xb7, 0x13, 0x7f, 0xad, 0x79, 0x74, 0x58, 0x79, 0x2e, 0x48, 0x4a, 0xed, - 0x5d, 0x56, 0xac, 0xb7, 0x21, 0x83, 0x9a, 0x7c, 0x00, 0xa5, 0x8d, 0x90, 0x06, 0xe2, 0x27, 0xdb, - 0x88, 0xe5, 0xaf, 0x8e, 0xd6, 0x2e, 0xe1, 0x46, 0x2b, 0xa4, 0x41, 0xcc, 0x46, 0x95, 0x25, 0x8d, - 0x82, 0xb8, 0x30, 0xbb, 0x1e, 0x38, 0xed, 0xd0, 0x8b, 0x16, 0xdb, 0x8d, 0xe0, 0x00, 0xa7, 0xcf, - 0x62, 0xdb, 0xd9, 0x6e, 0x52, 0x17, 0xbb, 0xa5, 0x58, 0xbb, 0x7a, 0x74, 0x58, 0x79, 0x31, 0xe2, - 0x38, 0x36, 0x8d, 0x91, 0x6c, 0xca, 0xb1, 0x14, 0xce, 0x7d, 0x39, 0x91, 0xf7, 0xa1, 0xb4, 0xd8, - 0x76, 0x3b, 0xbe, 0xd7, 0x8e, 0xd0, 0x4d, 0x56, 0x88, 0x2d, 0xec, 0xf3, 0x54, 0xc0, 0x6d, 0x26, - 0x8f, 0x6a, 0x33, 0x55, 0x02, 0xf3, 0xe7, 0x0c, 0x18, 0x53, 0xd4, 0x3a, 0x1b, 0xf7, 0xb5, 0xc0, - 0xff, 0x98, 0x36, 0x22, 0x5d, 0xe4, 0x3a, 0x1c, 0x98, 0x1a, 0xf7, 0x18, 0x35, 0x25, 0x6a, 0xb9, - 0x53, 0x88, 0x9a, 0x79, 0x43, 0xac, 0x16, 0x6c, 0xbb, 0xa8, 0x78, 0xc3, 0x70, 0xbb, 0xc8, 0xcc, - 0x61, 0x75, 0xbb, 0xc8, 0xca, 0xcd, 0xdf, 0x33, 0x98, 0x96, 0x27, 0x37, 0x00, 0xee, 0xd1, 0x83, - 0xc8, 0xd9, 0xbe, 0xed, 0x35, 0x35, 0x2f, 0xe7, 0x3e, 0x42, 0xed, 0x1d, 0xaf, 0x49, 0x2d, 0x05, - 0x85, 0xdc, 0x82, 0xe2, 0xbd, 0x60, 0xfb, 0x0d, 0x44, 0xcf, 0xc5, 0xab, 0xf5, 0xf4, 0x7e, 0xb0, - 0xfd, 0x06, 0x22, 0xab, 0x1a, 0x45, 0x22, 0x12, 0x13, 0x86, 0x17, 0xfc, 0x96, 0xe3, 0x49, 0x0b, - 0x09, 0x98, 0x99, 0xe1, 0x22, 0xc4, 0x12, 0x25, 0xcc, 0x3e, 0xa8, 0xaf, 0xad, 0x88, 0xce, 0x47, - 0xfb, 0x20, 0xec, 0xb4, 0x2d, 0x06, 0x33, 0xbf, 0x6b, 0xc0, 0x98, 0xb2, 0x78, 0x91, 0xcf, 0x0b, - 0x8f, 0x90, 0x81, 0xfe, 0xcc, 0x73, 0xbd, 0xcb, 0x1b, 0x2b, 0xe5, 0x96, 0x5d, 0xcb, 0x77, 0xa9, - 0xf0, 0x0f, 0x25, 0x3a, 0x3f, 0x37, 0x88, 0xce, 0x7f, 0x0b, 0x80, 0x9b, 0xfd, 0xd8, 0x9d, 0xca, - 0x24, 0x54, 0xfc, 0xbf, 0xea, 0x60, 0x24, 0xc8, 0xa6, 0x05, 0x25, 0x55, 0xdf, 0x93, 0x1a, 0x8c, - 0x8b, 0x5d, 0xae, 0xb0, 0x13, 0x79, 0x3f, 0xe3, 0x4c, 0x10, 0xdc, 0x7a, 0x77, 0xdd, 0x3a, 0x89, - 0xf9, 0xf3, 0x39, 0x28, 0x0a, 0xc8, 0xdc, 0x53, 0x6a, 0xc2, 0xbe, 0xa1, 0x99, 0xb0, 0x72, 0x65, - 0x54, 0xf6, 0x56, 0x73, 0x27, 0x6c, 0x91, 0xdf, 0x82, 0x92, 0xec, 0x02, 0xdc, 0x09, 0xbc, 0x02, - 0x23, 0xd2, 0xc9, 0xc3, 0xf7, 0x01, 0x93, 0x1a, 0xcf, 0xcd, 0x39, 0x4b, 0x96, 0x9b, 0xdf, 0x1e, - 0x92, 0xb4, 0xbc, 0x26, 0xd6, 0x85, 0x55, 0xd7, 0x0d, 0xd4, 0x2e, 0x74, 0x5c, 0x37, 0xb0, 0x10, - 0xca, 0x06, 0x7f, 0xad, 0xbb, 0xdd, 0xf4, 0x1a, 0x88, 0xa3, 0xcc, 0xc4, 0x0e, 0x42, 0x6d, 0x86, - 0xaa, 0x0e, 0x7e, 0x82, 0xac, 0xed, 0x50, 0xf3, 0xc7, 0xee, 0x50, 0x7f, 0x02, 0x46, 0xe7, 0x5b, - 0xae, 0x66, 0xc1, 0x9a, 0x19, 0x9d, 0x72, 0x3d, 0x46, 0xe2, 0xb6, 0xeb, 0x25, 0xd1, 0x47, 0x33, - 0x8d, 0x96, 0xdb, 0x6b, 0xb7, 0x26, 0x2c, 0xb5, 0x2d, 0xe6, 0xd0, 0xe3, 0x6c, 0x31, 0xdf, 0x84, - 0xd1, 0x8d, 0x90, 0xae, 0x77, 0xdb, 0x6d, 0xda, 0x44, 0x6b, 0xb6, 0xc8, 0xf5, 0x59, 0x37, 0xa4, - 0x76, 0x84, 0x50, 0xb5, 0x01, 0x31, 0xaa, 0x2a, 0x56, 0x23, 0xc7, 0x88, 0xd5, 0xe7, 0xa1, 0x50, - 0xed, 0x74, 0xe4, 0xde, 0x3b, 0x36, 0xaf, 0x3a, 0x1d, 0x34, 0x78, 0x26, 0x9c, 0x4e, 0x47, 0xdf, - 0x49, 0x23, 0x36, 0xa1, 0x40, 0xee, 0x75, 0xb7, 0x69, 0xd0, 0xa6, 0x11, 0x0d, 0xc5, 0xda, 0x19, - 0xce, 0x02, 0xf2, 0x98, 0x95, 0x47, 0x1c, 0x69, 0x04, 0xae, 0xd5, 0xf7, 0xbb, 0xdb, 0xd4, 0x16, - 0x8b, 0xb0, 0xda, 0x77, 0x19, 0x0c, 0x2f, 0xd6, 0x61, 0x42, 0xef, 0xff, 0x27, 0x60, 0x93, 0x7e, - 0x58, 0x28, 0x16, 0xcb, 0xa3, 0xe6, 0x2f, 0xe4, 0x60, 0xac, 0xda, 0xe9, 0x3c, 0xe5, 0x0e, 0xb0, - 0x1f, 0xd3, 0x66, 0xf5, 0xb9, 0x64, 0xf4, 0x4e, 0xe1, 0xfb, 0xfa, 0x2b, 0x03, 0x26, 0x53, 0x14, - 0x6a, 0xeb, 0x8d, 0x01, 0x1d, 0x42, 0xb9, 0x01, 0x1d, 0x42, 0xf9, 0xfe, 0x0e, 0x21, 0x75, 0xce, - 0x14, 0x1e, 0x67, 0xce, 0xbc, 0x0c, 0xf9, 0x6a, 0xa7, 0x23, 0x7a, 0xa5, 0x94, 0xf4, 0xca, 0xe6, - 0x2d, 0xbe, 0xb8, 0x39, 0x9d, 0x8e, 0xc5, 0x30, 0xcc, 0xd7, 0x61, 0x14, 0xc1, 0xa8, 0xd1, 0x2e, - 0x8b, 0xa9, 0xc0, 0xd5, 0x99, 0x46, 0xc6, 0xc5, 0xde, 0xfc, 0x3f, 0x06, 0x0c, 0xe1, 0xef, 0xa7, - 0x54, 0x5c, 0xe6, 0x34, 0x71, 0x29, 0x2b, 0xe2, 0x32, 0x88, 0xa0, 0xfc, 0x41, 0x1e, 0x7b, 0x4b, - 0x88, 0x88, 0x70, 0x29, 0x18, 0x19, 0x2e, 0x85, 0xc7, 0x50, 0xe0, 0xfb, 0x69, 0xe7, 0x42, 0x1e, - 0x07, 0xe3, 0x85, 0x74, 0x53, 0x9f, 0x88, 0x5f, 0xe1, 0x2e, 0x90, 0xa5, 0x76, 0x48, 0x1b, 0xdd, - 0x80, 0xd6, 0xf7, 0xbd, 0xce, 0x26, 0x0d, 0xbc, 0x9d, 0x03, 0x61, 0xba, 0xa3, 0x8e, 0xf5, 0x44, - 0xa9, 0x1d, 0xee, 0x7b, 0x1d, 0x66, 0x26, 0x78, 0x3b, 0x07, 0x56, 0x06, 0x0d, 0x79, 0x1f, 0x46, - 0x2c, 0xfa, 0x30, 0xf0, 0x22, 0xb9, 0x89, 0x9a, 0x88, 0x77, 0x7f, 0x08, 0xe5, 0xf6, 0x4e, 0xc0, - 0x7f, 0xa8, 0xe3, 0x2f, 0xca, 0x3f, 0xbd, 0x1d, 0xf8, 0x77, 0x86, 0x70, 0x2e, 0x9c, 0x70, 0x50, - 0x7b, 0x8c, 0x7f, 0x48, 0x1f, 0xcc, 0xfc, 0x69, 0x06, 0x73, 0x13, 0x4a, 0x6c, 0xd3, 0x9f, 0x72, - 0x14, 0x5d, 0x4a, 0xc6, 0xf2, 0xba, 0x5a, 0x7c, 0xdc, 0x19, 0xad, 0xc6, 0x87, 0xd8, 0x69, 0x21, - 0xe1, 0x67, 0xbf, 0xcf, 0x2a, 0x8c, 0x33, 0xc4, 0x23, 0x56, 0x1d, 0x0d, 0xde, 0x59, 0xa7, 0x16, - 0x8c, 0xe1, 0xc7, 0x13, 0x8c, 0x91, 0x4f, 0x22, 0x18, 0xe9, 0xd3, 0xf1, 0xe2, 0x69, 0x4e, 0xc7, - 0x2f, 0xbe, 0x0f, 0x53, 0x3d, 0x3d, 0x7c, 0x9a, 0x13, 0xe6, 0x4f, 0x4f, 0x2c, 0x7f, 0x26, 0xee, - 0x17, 0x32, 0x87, 0xfe, 0x02, 0x2f, 0xa0, 0x8d, 0x08, 0x55, 0xaf, 0xd0, 0x96, 0x81, 0x80, 0xa5, - 0xbc, 0x24, 0x08, 0x23, 0xef, 0xc1, 0x08, 0x3f, 0xa1, 0xe3, 0x1b, 0xdb, 0xb1, 0xb9, 0x71, 0x51, - 0x23, 0x87, 0x8a, 0x30, 0x09, 0x8e, 0xa1, 0xf6, 0xaa, 0x20, 0x32, 0xef, 0xc0, 0xb0, 0x38, 0xe1, - 0x3b, 0x7e, 0x5e, 0x54, 0x60, 0x68, 0x33, 0xe9, 0x19, 0x3c, 0x95, 0xe1, 0x1f, 0x61, 0x71, 0xb8, - 0xf9, 0x4b, 0x06, 0x4c, 0xe8, 0x5f, 0x49, 0xae, 0xc3, 0xb0, 0x38, 0x82, 0x36, 0xf0, 0x08, 0x9a, - 0x7d, 0xcd, 0x30, 0x3f, 0x7c, 0xd6, 0x8e, 0x9c, 0x05, 0x16, 0x53, 0xfd, 0x82, 0x83, 0xd8, 0xa4, - 0xa3, 0xea, 0x17, 0x42, 0x6a, 0xc9, 0x32, 0xb6, 0x8d, 0xb3, 0x68, 0xd8, 0x6d, 0x46, 0xea, 0x36, - 0x2e, 0x40, 0x88, 0x25, 0x4a, 0xcc, 0x43, 0x03, 0xa0, 0x5e, 0xbf, 0x7b, 0x8f, 0x1e, 0xac, 0x39, - 0x5e, 0x80, 0x5b, 0x61, 0x9c, 0x8d, 0xf7, 0xc4, 0x68, 0x95, 0xc4, 0x56, 0x98, 0xcf, 0xdc, 0x7d, - 0x7a, 0xa0, 0x6d, 0x85, 0x25, 0x2a, 0x4e, 0xf9, 0xc0, 0x7b, 0xe0, 0x44, 0x94, 0x11, 0xe6, 0x90, - 0x90, 0x4f, 0x79, 0x0e, 0x4d, 0x51, 0x2a, 0xc8, 0xe4, 0x6b, 0x30, 0x91, 0xfc, 0xc2, 0x0d, 0x7d, - 0x1e, 0xf7, 0x89, 0x52, 0x22, 0xf4, 0xc2, 0xda, 0x73, 0x47, 0x87, 0x95, 0x8b, 0x0a, 0xd7, 0xf4, - 0x56, 0x3f, 0xc5, 0xcc, 0xfc, 0x1d, 0x03, 0x60, 0x7d, 0xb9, 0x2e, 0x3f, 0xf0, 0x0a, 0x14, 0x62, - 0x3f, 0x62, 0x89, 0xef, 0xb7, 0x53, 0x1b, 0x4a, 0x2c, 0x27, 0x2f, 0x40, 0x3e, 0xf9, 0x92, 0xa9, - 0xa3, 0xc3, 0xca, 0xb8, 0xfe, 0x05, 0xac, 0x94, 0xdc, 0x81, 0x91, 0x81, 0xda, 0x8c, 0xd2, 0x99, - 0xd1, 0x56, 0x49, 0x8d, 0xa3, 0xf0, 0xe1, 0xd6, 0xfa, 0x67, 0x77, 0x14, 0xbe, 0x95, 0x83, 0x49, - 0xd6, 0xaf, 0xd5, 0x6e, 0xb4, 0xe7, 0x07, 0x5e, 0x74, 0xf0, 0xd4, 0xee, 0x8a, 0xdf, 0xd1, 0x0c, - 0xa2, 0x8b, 0x52, 0x6d, 0xa9, 0xdf, 0x36, 0xd0, 0xe6, 0xf8, 0xcf, 0x47, 0x60, 0x3a, 0x83, 0x8a, - 0xbc, 0x26, 0x82, 0xbf, 0x12, 0x3f, 0x14, 0x06, 0x77, 0xfd, 0xe8, 0xb0, 0x52, 0x92, 0xe8, 0xeb, - 0x49, 0xb0, 0xd7, 0x1c, 0x8c, 0x89, 0xad, 0xcf, 0x4a, 0x62, 0x51, 0x63, 0xd4, 0x90, 0x74, 0x5a, - 0xa2, 0x6a, 0x52, 0x91, 0x48, 0x15, 0x4a, 0xf3, 0x7b, 0xb4, 0xb1, 0xef, 0xb5, 0x77, 0xef, 0xd1, - 0x03, 0x6e, 0x2f, 0x95, 0x6a, 0xcf, 0xb2, 0x9d, 0x56, 0x43, 0xc0, 0xd9, 0x90, 0xea, 0x9b, 0x38, - 0x8d, 0x84, 0xbc, 0x07, 0x63, 0x75, 0x6f, 0xb7, 0x2d, 0x39, 0x14, 0x90, 0xc3, 0xa5, 0xa3, 0xc3, - 0xca, 0xb9, 0x90, 0x83, 0x7b, 0x19, 0xa8, 0x04, 0xe4, 0x15, 0x18, 0xb2, 0xfc, 0x26, 0xe5, 0xcb, - 0xb0, 0x08, 0x27, 0x0a, 0x18, 0x40, 0x75, 0xae, 0x23, 0x06, 0xb9, 0x0b, 0x23, 0xec, 0x9f, 0xfb, - 0x4e, 0x67, 0x76, 0x18, 0xf5, 0x36, 0x89, 0x0d, 0x7c, 0x84, 0x76, 0xbc, 0xf6, 0xae, 0x6a, 0xe3, - 0x37, 0xa9, 0xdd, 0x72, 0x3a, 0xda, 0xba, 0xc8, 0x11, 0xc9, 0x26, 0x8c, 0x25, 0x8a, 0x20, 0x9c, - 0x1d, 0xd1, 0x8e, 0x22, 0x93, 0x92, 0xda, 0xf3, 0x82, 0xd9, 0xf9, 0xa8, 0x19, 0xa2, 0x6c, 0x77, - 0x18, 0xbe, 0xfe, 0x31, 0x0a, 0x23, 0x6d, 0x0f, 0x52, 0xec, 0xbf, 0x07, 0x31, 0x4e, 0xdc, 0x83, - 0xb8, 0x00, 0xa2, 0x93, 0xaa, 0xcd, 0x5d, 0x11, 0xfd, 0xf7, 0x4a, 0x7f, 0x01, 0xbb, 0x9e, 0x20, - 0xe3, 0x9c, 0xe4, 0xde, 0x2e, 0xd1, 0xff, 0x4e, 0x73, 0x57, 0xf3, 0x76, 0xc5, 0xa8, 0xac, 0x1b, - 0x12, 0x55, 0x23, 0x77, 0xe0, 0xb2, 0x1b, 0x92, 0x92, 0xa4, 0x1b, 0x3e, 0x7e, 0x18, 0xf5, 0xeb, - 0x06, 0x85, 0x11, 0x59, 0x01, 0xa8, 0x36, 0x22, 0xef, 0x01, 0x45, 0x91, 0x18, 0xd3, 0x3a, 0x62, - 0xbe, 0x7a, 0x8f, 0x1e, 0xd4, 0x69, 0x94, 0x9c, 0x0a, 0x38, 0x88, 0x9a, 0x12, 0x13, 0x4b, 0xe1, - 0x40, 0x3a, 0x70, 0xb6, 0xea, 0xba, 0x1e, 0x8f, 0x08, 0x5d, 0x0f, 0x98, 0xfc, 0xba, 0xc8, 0xba, - 0x94, 0xcd, 0xfa, 0x15, 0xc1, 0xfa, 0x79, 0x27, 0xa6, 0xb2, 0x23, 0x4e, 0x96, 0xae, 0x26, 0x9b, - 0xb1, 0xb9, 0x0a, 0x13, 0x7a, 0x97, 0xea, 0xb1, 0x90, 0x25, 0x28, 0x5a, 0xf5, 0xaa, 0x5d, 0xbf, - 0x5b, 0xbd, 0x59, 0x36, 0x48, 0x19, 0x4a, 0xe2, 0xd7, 0x9c, 0x3d, 0xf7, 0xc6, 0x9b, 0xe5, 0x9c, - 0x06, 0x79, 0xe3, 0xe6, 0x5c, 0x39, 0x6f, 0xfe, 0x81, 0x01, 0x45, 0xd9, 0x3e, 0xf2, 0x26, 0xe4, - 0xeb, 0xf5, 0xbb, 0xa9, 0x13, 0xf0, 0x64, 0xe9, 0xe5, 0x8b, 0x4c, 0x18, 0xee, 0xa9, 0x8b, 0x4c, - 0xbd, 0x7e, 0x97, 0xd1, 0xad, 0x2f, 0xd7, 0x85, 0xd1, 0x92, 0x21, 0xae, 0x53, 0x7d, 0x8e, 0x05, - 0xdf, 0x84, 0xfc, 0x87, 0x5b, 0xeb, 0x62, 0x37, 0x94, 0x31, 0xbe, 0x48, 0xf7, 0xf1, 0x43, 0x75, - 0xe9, 0x63, 0x04, 0xa6, 0x05, 0x63, 0xca, 0xd4, 0xe2, 0x46, 0x44, 0xcb, 0x8f, 0xa3, 0x04, 0x85, - 0x11, 0xc1, 0x20, 0x96, 0x28, 0x61, 0x36, 0xcf, 0xb2, 0xdf, 0x70, 0x9a, 0xc2, 0x1a, 0x41, 0x9b, - 0xa7, 0xc9, 0x00, 0x16, 0x87, 0x9b, 0x7f, 0x6c, 0x40, 0x79, 0x2d, 0xf0, 0x1f, 0x78, 0x4c, 0x03, - 0xaf, 0xfb, 0xfb, 0xb4, 0xbd, 0x79, 0x93, 0xbc, 0x2e, 0x95, 0x00, 0x37, 0xe1, 0xce, 0x33, 0x2a, - 0x54, 0x02, 0x3f, 0x3a, 0xac, 0x40, 0xfd, 0x20, 0x8c, 0x68, 0x8b, 0x95, 0x4b, 0x45, 0xa0, 0x04, - 0x5b, 0xe6, 0x06, 0x0f, 0xe0, 0x3a, 0x21, 0xd8, 0xb2, 0x02, 0x43, 0xd8, 0x1c, 0x25, 0x86, 0x66, - 0x28, 0x62, 0x00, 0x8b, 0xc3, 0x15, 0x85, 0xfd, 0xed, 0x5c, 0xcf, 0x37, 0xcc, 0x7d, 0xa6, 0x82, - 0xa0, 0xf4, 0x8f, 0x1b, 0x68, 0x11, 0xfb, 0x32, 0xcc, 0xa4, 0xbb, 0x04, 0xfd, 0x22, 0x55, 0x98, - 0xd4, 0xe1, 0xd2, 0x45, 0x72, 0x3e, 0xb3, 0xae, 0xcd, 0x39, 0x2b, 0x8d, 0x6f, 0xfe, 0xc0, 0x80, - 0x51, 0xfc, 0xd7, 0xea, 0x36, 0x29, 0xb3, 0x6c, 0xaa, 0x5b, 0x75, 0x71, 0x0c, 0xa9, 0x9e, 0xea, - 0x39, 0x0f, 0x43, 0x5b, 0x9c, 0x59, 0x6a, 0x7a, 0x24, 0x46, 0x16, 0xa4, 0xfc, 0xd0, 0x55, 0x1e, - 0x6a, 0xc5, 0xa4, 0xfc, 0x74, 0x36, 0x4c, 0x91, 0x0a, 0x64, 0x36, 0x7e, 0xec, 0x97, 0xdf, 0x94, - 0xae, 0x61, 0x1c, 0x3f, 0xa4, 0xf3, 0xb5, 0xa3, 0x13, 0x89, 0x46, 0x5e, 0x87, 0x61, 0x56, 0xb5, - 0x25, 0x0f, 0x46, 0x70, 0x57, 0x81, 0x6d, 0x0c, 0xb4, 0x33, 0x60, 0x8e, 0x64, 0xfe, 0xeb, 0x5c, - 0xba, 0x03, 0x85, 0x15, 0x70, 0xca, 0xb9, 0xf1, 0x36, 0x0c, 0x55, 0x9b, 0x4d, 0xff, 0xa1, 0xd0, - 0x12, 0xd2, 0x4d, 0x13, 0xf7, 0x1f, 0x5f, 0x61, 0x1d, 0x86, 0xa2, 0x05, 0x1f, 0x30, 0x00, 0x99, - 0x87, 0xd1, 0xea, 0x56, 0x7d, 0x69, 0x69, 0x61, 0x7d, 0x7d, 0x59, 0xc4, 0xb8, 0xbf, 0x24, 0xfb, - 0xc7, 0xf3, 0x5c, 0x3b, 0x8a, 0x9a, 0x7d, 0x42, 0x60, 0x13, 0x3a, 0xf2, 0x2e, 0xc0, 0x87, 0xbe, - 0xd7, 0xbe, 0x4f, 0xa3, 0x3d, 0xdf, 0x15, 0x1f, 0xcf, 0x4c, 0x8a, 0xb1, 0x8f, 0x7d, 0xaf, 0x6d, - 0xb7, 0x10, 0xcc, 0xda, 0x9e, 0x20, 0x59, 0xca, 0xff, 0xac, 0xa7, 0x6b, 0x7e, 0x84, 0x36, 0xcc, - 0x50, 0xd2, 0xd3, 0xdb, 0x7e, 0x94, 0x3e, 0xb7, 0x91, 0x68, 0xe6, 0x2f, 0xe7, 0x60, 0x82, 0xef, - 0x54, 0xb9, 0xc0, 0x3c, 0xb5, 0x93, 0xf1, 0x6d, 0x6d, 0x32, 0x5e, 0x90, 0x0b, 0x83, 0xf2, 0x69, - 0x03, 0x4d, 0xc5, 0x3d, 0x20, 0xbd, 0x34, 0xc4, 0x92, 0xfe, 0x94, 0x41, 0x66, 0xe1, 0xcd, 0x24, - 0x62, 0x20, 0x44, 0x22, 0x1b, 0x55, 0x61, 0x68, 0x69, 0x3c, 0xcc, 0x5f, 0xca, 0xc1, 0xb8, 0x62, - 0x4f, 0x3e, 0xb5, 0x1d, 0xff, 0x45, 0xad, 0xe3, 0xe5, 0x19, 0x84, 0xf2, 0x65, 0x03, 0xf5, 0x7b, - 0x17, 0xa6, 0x7a, 0x48, 0xd2, 0x66, 0xb9, 0x31, 0x88, 0x59, 0xfe, 0x5a, 0x6f, 0xf4, 0x01, 0x8f, - 0x87, 0x8f, 0xa3, 0x0f, 0xd4, 0x70, 0x87, 0x6f, 0xe5, 0x60, 0x46, 0xfc, 0xaa, 0x76, 0x5d, 0x2f, - 0x9a, 0xf7, 0xdb, 0x3b, 0xde, 0xee, 0x53, 0x3b, 0x16, 0x55, 0x6d, 0x2c, 0x2a, 0xfa, 0x58, 0x28, - 0x1f, 0xd8, 0x7f, 0x48, 0xcc, 0x7f, 0x57, 0x84, 0xd9, 0x7e, 0x04, 0x6c, 0xdb, 0xaf, 0xec, 0xaa, - 0x70, 0xdb, 0x9f, 0xda, 0xb1, 0xf2, 0xfd, 0x54, 0x12, 0xc2, 0x93, 0x1b, 0x20, 0x84, 0x67, 0x19, - 0xca, 0x58, 0x55, 0x9d, 0x86, 0xac, 0x13, 0xc2, 0x24, 0x18, 0xf7, 0xf2, 0xd1, 0x61, 0xe5, 0x92, - 0xc3, 0xca, 0xec, 0x50, 0x14, 0xda, 0xdd, 0xc0, 0x53, 0x78, 0xf4, 0x50, 0x92, 0xdf, 0x31, 0x60, - 0x02, 0x81, 0x8b, 0x0f, 0x68, 0x3b, 0x42, 0x66, 0x05, 0x71, 0x48, 0x13, 0xdf, 0x79, 0xaa, 0x47, - 0x81, 0xd7, 0xde, 0x45, 0x47, 0x52, 0x58, 0xdb, 0x66, 0xbd, 0xf0, 0xfd, 0xc3, 0xca, 0x3b, 0x9f, - 0xe4, 0x1e, 0x95, 0x60, 0x15, 0xb2, 0x8d, 0x3c, 0x6f, 0x28, 0xc5, 0x6a, 0x53, 0xcd, 0x4c, 0xb5, - 0x88, 0xfc, 0x38, 0x9c, 0xe7, 0x71, 0x18, 0xf3, 0x7e, 0x3b, 0xf2, 0xda, 0x5d, 0xbf, 0x1b, 0xd6, - 0x9c, 0xc6, 0x7e, 0xb7, 0x13, 0x0a, 0x67, 0x27, 0x7e, 0x79, 0x23, 0x2e, 0xb4, 0xb7, 0x79, 0xa9, - 0xc2, 0xb2, 0x1f, 0x03, 0x72, 0x17, 0xa6, 0x78, 0x51, 0xb5, 0x1b, 0xf9, 0xf5, 0x86, 0xd3, 0xf4, - 0xda, 0xbb, 0xe8, 0x03, 0x2d, 0xd6, 0x2e, 0xb2, 0xbd, 0xa5, 0xd3, 0x8d, 0x7c, 0x3b, 0xe4, 0x70, - 0x85, 0x5f, 0x2f, 0x11, 0x59, 0x82, 0x49, 0x8b, 0x3a, 0xee, 0x7d, 0xe7, 0xd1, 0xbc, 0xd3, 0x71, - 0x1a, 0x5e, 0x74, 0x80, 0x3b, 0xb3, 0x7c, 0xad, 0x72, 0x74, 0x58, 0x79, 0x26, 0xa0, 0x8e, 0x6b, - 0xb7, 0x9c, 0x47, 0x76, 0x43, 0x14, 0x2a, 0xcc, 0xd2, 0x74, 0x31, 0x2b, 0xaf, 0x1d, 0xb3, 0x1a, - 0x4d, 0xb3, 0xf2, 0xda, 0xfd, 0x59, 0x25, 0x74, 0x92, 0xd5, 0xba, 0x13, 0xec, 0xd2, 0x88, 0x3b, - 0x09, 0xe1, 0xb2, 0x71, 0xd5, 0x50, 0x58, 0x45, 0x58, 0x66, 0xa3, 0xc3, 0x30, 0xcd, 0x4a, 0xa1, - 0x63, 0x92, 0xb7, 0x15, 0x78, 0x11, 0x55, 0xbf, 0x70, 0x0c, 0x9b, 0x85, 0xfd, 0x8f, 0x6e, 0xd2, - 0x7e, 0x9f, 0xd8, 0x43, 0x99, 0x70, 0x53, 0x3e, 0xb2, 0xd4, 0xc3, 0x2d, 0xfb, 0x2b, 0x7b, 0x28, - 0x63, 0x6e, 0xea, 0x77, 0x8e, 0xe3, 0x77, 0x2a, 0xdc, 0xfa, 0x7c, 0x68, 0x0f, 0x25, 0x59, 0x61, - 0x9d, 0x16, 0xd1, 0x36, 0x93, 0x68, 0xe1, 0x24, 0x9d, 0xc0, 0xa6, 0xbd, 0x28, 0xf6, 0xd4, 0xe5, - 0x40, 0x16, 0xdb, 0x19, 0x2e, 0xd3, 0x34, 0xf1, 0x87, 0x85, 0xe2, 0x50, 0x79, 0xd8, 0x2a, 0x73, - 0x91, 0x8f, 0x98, 0xe0, 0xa0, 0x2e, 0x36, 0x7f, 0x23, 0x07, 0x17, 0xa4, 0x3a, 0xa6, 0xd1, 0x43, - 0x3f, 0xd8, 0xf7, 0xda, 0xbb, 0x4f, 0xb9, 0x56, 0xbd, 0xad, 0x69, 0xd5, 0x17, 0x53, 0x2b, 0x5c, - 0xea, 0x2b, 0x8f, 0x51, 0xad, 0x7f, 0x36, 0x04, 0xcf, 0x1e, 0x4b, 0x45, 0x3e, 0x62, 0xab, 0xa0, - 0x47, 0xdb, 0xd1, 0x92, 0xdb, 0xa4, 0x6c, 0x1b, 0xe6, 0x77, 0x23, 0xe1, 0xcc, 0x7e, 0xe1, 0xe8, - 0xb0, 0x32, 0xcd, 0xaf, 0x02, 0xd9, 0x9e, 0xdb, 0xa4, 0x76, 0xc4, 0x8b, 0xb5, 0x61, 0xea, 0xa5, - 0x66, 0x2c, 0xe3, 0x8b, 0x89, 0x4b, 0xed, 0x88, 0x06, 0x0f, 0x1c, 0x7e, 0x23, 0x42, 0xb0, 0xdc, - 0xa7, 0xb4, 0x63, 0x3b, 0xac, 0xd4, 0xf6, 0x44, 0xb1, 0xce, 0xb2, 0x87, 0x9a, 0xdc, 0x56, 0x58, - 0xce, 0xb3, 0xcd, 0xc1, 0x7d, 0xe7, 0x91, 0xb0, 0x78, 0xd1, 0xbf, 0xaa, 0xb0, 0xe4, 0x51, 0x90, - 0x2d, 0xe7, 0x91, 0xd5, 0x4b, 0x42, 0xbe, 0x06, 0x67, 0x85, 0xe2, 0x66, 0x4a, 0x2c, 0xf0, 0x9b, - 0xf2, 0x8b, 0x0b, 0xc8, 0xeb, 0xe5, 0xa3, 0xc3, 0xca, 0x79, 0xa1, 0xf6, 0xed, 0x06, 0xc7, 0xc8, - 0xfc, 0xea, 0x6c, 0x2e, 0x64, 0x9d, 0x2d, 0x64, 0xa9, 0xee, 0xb8, 0x4f, 0xc3, 0xd0, 0xd9, 0x95, - 0xd6, 0x31, 0x3f, 0x51, 0x52, 0x3a, 0xd3, 0x6e, 0xf1, 0x72, 0xab, 0x2f, 0x25, 0xb9, 0x0b, 0x13, - 0x5b, 0x74, 0x5b, 0x1d, 0x9f, 0xe1, 0x78, 0x8a, 0x97, 0x1f, 0xd2, 0xed, 0xfe, 0x83, 0x93, 0xa2, - 0x23, 0x1e, 0x4c, 0xad, 0x05, 0xfe, 0xa3, 0x03, 0xb6, 0xd5, 0xa3, 0x6d, 0x1a, 0x60, 0x70, 0xd7, - 0x08, 0xba, 0xab, 0x66, 0x13, 0xcb, 0x52, 0x2f, 0xaf, 0x3d, 0x7f, 0x74, 0x58, 0x79, 0xb6, 0xc3, - 0xc0, 0x76, 0x53, 0xc0, 0xed, 0xd4, 0xbd, 0xc0, 0x5e, 0xae, 0xe4, 0x27, 0x61, 0xd2, 0xf2, 0xbb, - 0x91, 0xd7, 0xde, 0xad, 0x47, 0x81, 0x13, 0xd1, 0x5d, 0xae, 0xc8, 0x93, 0x28, 0xb2, 0x54, 0x29, - 0x77, 0x4c, 0x07, 0x1c, 0x68, 0x87, 0x02, 0xaa, 0x69, 0x52, 0x9d, 0xc0, 0xfc, 0xb5, 0x1c, 0xcc, - 0x8a, 0x61, 0xb0, 0x68, 0xc3, 0x0f, 0xdc, 0xa7, 0x7f, 0xda, 0x2f, 0x6a, 0xd3, 0xfe, 0x85, 0x38, - 0x46, 0x29, 0xeb, 0x23, 0x8f, 0x99, 0xf5, 0xbf, 0x6f, 0xc0, 0xa5, 0xe3, 0x88, 0x58, 0xef, 0xc4, - 0x71, 0x7d, 0xa3, 0x3d, 0xf1, 0x7b, 0x1d, 0x98, 0xc6, 0xf1, 0x44, 0xc7, 0x71, 0x78, 0xd7, 0x0f, - 0x23, 0xf4, 0xde, 0xe5, 0xb4, 0x40, 0x82, 0x9a, 0xef, 0x37, 0x51, 0xcf, 0xd7, 0x5e, 0x63, 0xea, - 0xfc, 0xfb, 0x87, 0x15, 0x60, 0x20, 0x1e, 0x89, 0xc7, 0xd6, 0x7c, 0x2e, 0x31, 0xe8, 0x97, 0x0e, - 0x6d, 0x8c, 0xfe, 0xd8, 0xa7, 0x07, 0xa1, 0x95, 0xc5, 0x1a, 0x3d, 0x34, 0xd5, 0x6e, 0xb4, 0xb7, - 0x16, 0xd0, 0x1d, 0x1a, 0xd0, 0x76, 0x83, 0x7e, 0xc6, 0x3c, 0x34, 0xfa, 0xc7, 0x0d, 0xb4, 0x3d, - 0xf9, 0xcb, 0x11, 0x98, 0xc9, 0x22, 0x63, 0xfd, 0xa2, 0x58, 0xc4, 0xe9, 0x4b, 0xe4, 0x7f, 0xdb, - 0x80, 0x52, 0x9d, 0x36, 0xfc, 0xb6, 0x7b, 0xdb, 0x69, 0x44, 0xbe, 0x0c, 0xc9, 0xb0, 0xb9, 0x66, - 0x63, 0x70, 0x7b, 0x07, 0x0b, 0x34, 0xcf, 0xc0, 0x07, 0x83, 0x19, 0xa2, 0x0d, 0x1f, 0x03, 0x61, - 0x23, 0x26, 0x93, 0x49, 0x15, 0x78, 0xaa, 0xa1, 0x55, 0x4a, 0x6a, 0x30, 0x3e, 0xef, 0xb7, 0xdb, - 0x94, 0xfd, 0x50, 0xc2, 0x3a, 0x31, 0x10, 0xb3, 0x21, 0x0b, 0xd2, 0x1e, 0x02, 0x9d, 0x84, 0xdc, - 0x82, 0xfc, 0xc6, 0xdc, 0x6d, 0x31, 0x06, 0x32, 0x58, 0x6d, 0x63, 0xee, 0x36, 0xee, 0x75, 0x99, - 0xfd, 0x30, 0xde, 0x9d, 0xdb, 0x51, 0x7d, 0xa0, 0x1b, 0x73, 0xb7, 0xc9, 0x2a, 0x4c, 0x59, 0xf4, - 0xeb, 0x5d, 0x2f, 0xa0, 0x62, 0x02, 0xdc, 0xbf, 0x5d, 0xc5, 0xb1, 0x28, 0x72, 0x3d, 0x16, 0xf0, - 0x42, 0x69, 0xdb, 0xdb, 0xad, 0x1d, 0xf5, 0xe2, 0x64, 0x2f, 0x2d, 0xf9, 0x59, 0x38, 0xbb, 0xe0, - 0x85, 0xa2, 0xcd, 0xdc, 0xf9, 0xe8, 0xe2, 0x39, 0xe4, 0x70, 0x9f, 0xe9, 0xf0, 0x85, 0xcc, 0xe9, - 0xf0, 0xbc, 0x1b, 0x33, 0xb1, 0xb9, 0x67, 0xd3, 0x4d, 0xc7, 0xc3, 0x66, 0xd7, 0x43, 0x3e, 0x86, - 0x09, 0xf4, 0xf6, 0xa0, 0x3f, 0x16, 0xe3, 0xcd, 0x47, 0xfa, 0xd4, 0xfc, 0xb9, 0xcc, 0x9a, 0x2f, - 0xa2, 0xf3, 0xc8, 0x46, 0xaf, 0x2e, 0xc6, 0xa6, 0x6b, 0x7b, 0x04, 0x8d, 0x33, 0xf9, 0x10, 0x26, - 0xc5, 0xa2, 0xb3, 0xba, 0xb3, 0xbe, 0x47, 0x17, 0x9c, 0x03, 0x11, 0x84, 0x80, 0xf6, 0x9f, 0x58, - 0xa9, 0x6c, 0x7f, 0xc7, 0x8e, 0xf6, 0xa8, 0xed, 0x3a, 0x9a, 0x7a, 0x4e, 0x11, 0x92, 0x9f, 0x86, - 0xb1, 0x65, 0x1f, 0x0f, 0x9e, 0x50, 0xd5, 0x8c, 0x22, 0x9f, 0x2f, 0xe3, 0xc5, 0x69, 0x0e, 0x4e, - 0x2d, 0x22, 0x3f, 0x3a, 0xac, 0xbc, 0x7d, 0x5a, 0x29, 0x54, 0x2a, 0xb0, 0xd4, 0xda, 0xc8, 0x3c, - 0x14, 0xb7, 0xe8, 0x36, 0xfb, 0xda, 0xf4, 0xa5, 0x3f, 0x09, 0xe6, 0xfa, 0xe2, 0xa1, 0xf8, 0xa5, - 0x9e, 0xea, 0x48, 0x0c, 0x12, 0xc0, 0x14, 0xf6, 0xcf, 0x9a, 0x13, 0x86, 0x0f, 0xfd, 0xc0, 0x6d, - 0xd2, 0x50, 0x1e, 0x8f, 0xf4, 0x76, 0xfe, 0x5c, 0x66, 0xe7, 0x5f, 0xe2, 0x9d, 0xdf, 0x51, 0x38, - 0xa8, 0xe2, 0xd6, 0xc3, 0xde, 0xfc, 0x37, 0x06, 0x4a, 0x3d, 0xb9, 0x86, 0xc1, 0x67, 0x71, 0x54, - 0x3b, 0xee, 0x66, 0x9d, 0x4e, 0xea, 0x2e, 0x00, 0x47, 0x61, 0x5b, 0xdf, 0xdb, 0x4e, 0x83, 0x46, - 0xd2, 0x47, 0x8a, 0xc8, 0x3b, 0x08, 0x51, 0xb7, 0xbe, 0x1c, 0x87, 0x7c, 0x09, 0x66, 0x16, 0xe8, - 0x03, 0xaf, 0x41, 0xab, 0x51, 0x44, 0x43, 0xde, 0xc3, 0xf3, 0x55, 0x7e, 0x98, 0x38, 0x5a, 0x7b, - 0xf1, 0xe8, 0xb0, 0x72, 0xd9, 0xc5, 0x72, 0xdb, 0x49, 0x10, 0xec, 0x86, 0xa3, 0xf2, 0xca, 0xe4, - 0x60, 0xfe, 0x6f, 0x23, 0xe9, 0x75, 0xf2, 0x32, 0x14, 0xac, 0xb5, 0xb8, 0xfd, 0xfc, 0x9c, 0x30, - 0xd5, 0x7c, 0x44, 0x20, 0x5f, 0x81, 0xb3, 0x0a, 0x1f, 0xec, 0x11, 0xea, 0xb2, 0x06, 0xf1, 0x8f, - 0x79, 0x09, 0x0f, 0x86, 0x94, 0x96, 0x38, 0x1c, 0x23, 0xd5, 0xa2, 0x6c, 0x1e, 0xec, 0x63, 0x95, - 0x82, 0x05, 0xda, 0xf6, 0x38, 0x6f, 0xe5, 0x63, 0x55, 0xde, 0x2e, 0x22, 0xa4, 0x3f, 0x36, 0x8b, - 0xc3, 0x87, 0x85, 0x62, 0xa1, 0x3c, 0x64, 0xfe, 0x95, 0xa1, 0x64, 0xbd, 0x78, 0x4a, 0x57, 0xac, - 0x37, 0xb5, 0x15, 0x6b, 0x46, 0x90, 0xc6, 0x5f, 0xc5, 0xca, 0x32, 0xad, 0x8c, 0x49, 0x18, 0xd7, - 0x90, 0x30, 0xcc, 0x76, 0x23, 0xa4, 0x01, 0xf7, 0x49, 0x7e, 0xb6, 0xc2, 0x6c, 0xe3, 0xef, 0x1a, - 0x28, 0x7a, 0xf2, 0xcf, 0x0d, 0x98, 0x4c, 0x51, 0xb0, 0xde, 0x60, 0x20, 0xb5, 0x37, 0xba, 0x21, - 0x0d, 0x2c, 0x84, 0xf2, 0xa0, 0xbc, 0x65, 0x3d, 0x28, 0xaf, 0x69, 0x31, 0x18, 0xf9, 0x00, 0x86, - 0x36, 0x70, 0x07, 0xa1, 0xc7, 0x75, 0xc4, 0xfc, 0xb1, 0x90, 0xcf, 0xb0, 0x2e, 0xfb, 0x57, 0x55, - 0x10, 0x58, 0x46, 0xea, 0x30, 0x32, 0x1f, 0x50, 0xcc, 0x6f, 0x51, 0x18, 0xfc, 0x00, 0xae, 0xc1, - 0x49, 0xd2, 0x07, 0x70, 0x82, 0x93, 0xf9, 0xab, 0x39, 0x20, 0xc9, 0x37, 0xd2, 0x46, 0x40, 0xa3, - 0xf0, 0xa9, 0x1d, 0xf4, 0xf7, 0xb5, 0x41, 0x7f, 0xb6, 0x67, 0xd0, 0xf9, 0xe7, 0x0d, 0x34, 0xf6, - 0x7f, 0x6c, 0xc0, 0xb9, 0x6c, 0x42, 0xf2, 0x02, 0x0c, 0xaf, 0xae, 0xaf, 0xc9, 0xd0, 0x20, 0xf1, - 0x29, 0x7e, 0x07, 0x2d, 0x63, 0x4b, 0x14, 0x91, 0xd7, 0x61, 0xf8, 0x23, 0x6b, 0x9e, 0x2d, 0x99, - 0xca, 0xed, 0x99, 0xaf, 0x07, 0x76, 0x43, 0xdf, 0x72, 0x09, 0x24, 0x75, 0x6c, 0xf3, 0x4f, 0x6c, - 0x6c, 0xbf, 0x95, 0x83, 0xc9, 0x6a, 0xa3, 0x41, 0xc3, 0x90, 0x19, 0x44, 0x34, 0x8c, 0x9e, 0xda, - 0x81, 0xcd, 0x0e, 0xfa, 0xd1, 0xbe, 0x6d, 0xa0, 0x51, 0xfd, 0x13, 0x03, 0xce, 0x4a, 0xaa, 0x07, - 0x1e, 0x7d, 0xb8, 0xbe, 0x17, 0xd0, 0x70, 0xcf, 0x6f, 0xba, 0x83, 0xde, 0x03, 0xc3, 0x55, 0xda, - 0x6b, 0x46, 0x34, 0x50, 0x1d, 0xd4, 0x3b, 0x08, 0xd1, 0x56, 0x69, 0x84, 0x90, 0x1b, 0x30, 0x52, - 0xed, 0x74, 0x02, 0xff, 0x01, 0x9f, 0xf6, 0xe3, 0xe2, 0x3c, 0x92, 0x83, 0xb4, 0xf3, 0x4b, 0x0e, - 0x62, 0xcd, 0x58, 0xa0, 0x6d, 0x1e, 0xd1, 0x3c, 0xce, 0x9b, 0xe1, 0xd2, 0xb6, 0x6a, 0xa1, 0x61, - 0xb9, 0xf9, 0xcd, 0x02, 0x94, 0xd4, 0x0f, 0x21, 0x26, 0x0c, 0xf3, 0xf0, 0x14, 0x35, 0x4c, 0xc0, - 0x41, 0x88, 0x25, 0x4a, 0x92, 0xa8, 0x9f, 0xdc, 0x89, 0x51, 0x3f, 0x5b, 0x30, 0xbe, 0x16, 0xf8, - 0x1d, 0x3f, 0xa4, 0x2e, 0x4f, 0x51, 0xc4, 0xb5, 0xd6, 0x74, 0x1c, 0x0a, 0xcb, 0xfb, 0x9c, 0x15, - 0xf1, 0xed, 0x40, 0x47, 0x60, 0xdb, 0xe9, 0x04, 0x46, 0x3a, 0x1f, 0xee, 0xe0, 0x77, 0x42, 0x71, - 0x5d, 0x20, 0x76, 0xf0, 0x33, 0x88, 0xee, 0xe0, 0x67, 0x10, 0x75, 0x5a, 0x0c, 0x3d, 0xa9, 0x69, - 0x41, 0x7e, 0xd5, 0x80, 0xb1, 0x6a, 0xbb, 0x2d, 0xa2, 0x7e, 0xe4, 0x3d, 0xff, 0xb3, 0x89, 0x93, - 0x9f, 0x87, 0x85, 0x72, 0x1f, 0xff, 0x57, 0x85, 0x8f, 0xff, 0xed, 0x4f, 0xe4, 0xe3, 0x5f, 0x0f, - 0x1c, 0x2f, 0x0a, 0xf1, 0x30, 0x37, 0xa9, 0x50, 0x0d, 0xfd, 0x55, 0xda, 0x41, 0xde, 0x86, 0x72, - 0x2c, 0x8f, 0x4b, 0x6d, 0x97, 0x3e, 0xa2, 0x3c, 0x48, 0x6a, 0x9c, 0xdf, 0x30, 0xd4, 0x0e, 0x2f, - 0xd2, 0x88, 0xe6, 0xb7, 0x0c, 0x38, 0xa7, 0x0a, 0x44, 0xbd, 0xbb, 0xdd, 0xf2, 0x70, 0xfb, 0x43, - 0xae, 0xc3, 0xa8, 0x18, 0xaf, 0xd8, 0x90, 0xeb, 0xcd, 0x6b, 0x95, 0xa0, 0x90, 0x45, 0x36, 0x44, - 0x8c, 0x87, 0xf0, 0x15, 0x4c, 0xa7, 0xa6, 0x1b, 0x2b, 0xaa, 0xcd, 0x8a, 0xce, 0x2e, 0x07, 0xf8, - 0x5b, 0x1f, 0x3b, 0x06, 0x31, 0xdf, 0x83, 0x29, 0xbd, 0x95, 0x75, 0x8a, 0x57, 0xd0, 0xe4, 0xa7, - 0x19, 0xd9, 0x9f, 0x26, 0xcb, 0xcd, 0x2d, 0x20, 0x3d, 0xf4, 0x21, 0x1e, 0x54, 0xd1, 0x48, 0x1e, - 0xa4, 0x4a, 0x77, 0x57, 0x0f, 0x62, 0x9c, 0xe1, 0x6d, 0x4c, 0xed, 0x6e, 0x24, 0x35, 0xff, 0x02, - 0x60, 0x3a, 0x43, 0x75, 0x9c, 0xb0, 0xb4, 0x57, 0xf4, 0xc9, 0x33, 0x1a, 0x47, 0x04, 0xc8, 0x29, - 0xf3, 0x9e, 0xcc, 0xe6, 0x75, 0xcc, 0x54, 0x39, 0x2e, 0xc5, 0xd7, 0xa7, 0xb1, 0xbc, 0xab, 0x41, - 0x3b, 0x43, 0x4f, 0x2c, 0x68, 0xa7, 0x06, 0xe3, 0xe2, 0xab, 0xc4, 0x54, 0x1e, 0x4e, 0xdc, 0x02, - 0x01, 0x2f, 0xb0, 0x7b, 0xa6, 0xb4, 0x4e, 0xc2, 0x79, 0x84, 0x7e, 0xf3, 0x01, 0x15, 0x3c, 0x46, - 0x54, 0x1e, 0x58, 0x90, 0xc9, 0x43, 0x21, 0x21, 0xff, 0xdc, 0x00, 0x22, 0x20, 0xea, 0x7c, 0x2e, - 0x1e, 0x37, 0x9f, 0xdd, 0x27, 0x33, 0x9f, 0x9f, 0x95, 0x6d, 0xcc, 0x9e, 0xd7, 0x19, 0xcd, 0x22, - 0xff, 0xd4, 0x80, 0x29, 0x1e, 0x39, 0xa2, 0x36, 0x76, 0xf4, 0xb8, 0xc6, 0x36, 0x9e, 0x4c, 0x63, - 0x2f, 0x85, 0x58, 0x6d, 0x9f, 0xb6, 0xf6, 0x36, 0x8a, 0xfc, 0x38, 0x40, 0x3c, 0xa3, 0x64, 0x84, - 0xe2, 0xa5, 0x0c, 0x2d, 0x10, 0x23, 0x25, 0x97, 0x2c, 0xa3, 0x98, 0x4e, 0x8d, 0xe9, 0x49, 0xb8, - 0x91, 0x9f, 0x85, 0x19, 0x36, 0x5f, 0x62, 0x88, 0x88, 0x73, 0x9b, 0x1d, 0xc3, 0x5a, 0x3e, 0xdf, - 0x7f, 0x69, 0xbf, 0x9e, 0x45, 0xc6, 0xef, 0x89, 0x24, 0x89, 0x16, 0xa2, 0x96, 0xba, 0xe5, 0xcb, - 0xa2, 0xc0, 0x80, 0x56, 0x6c, 0x7d, 0x38, 0x5b, 0xc2, 0x3a, 0x33, 0xf5, 0xdb, 0x05, 0x39, 0x17, - 0xb8, 0x7e, 0x0b, 0xf5, 0x8b, 0x1e, 0x08, 0x22, 0x1f, 0x01, 0xa9, 0x77, 0x77, 0x77, 0x69, 0x18, - 0x51, 0x97, 0xc3, 0x68, 0x10, 0xce, 0x8e, 0xa3, 0x7e, 0x40, 0x37, 0x55, 0x28, 0x4b, 0xed, 0x40, - 0x16, 0xab, 0x42, 0xd2, 0x4b, 0x4c, 0x28, 0xcc, 0x88, 0x8f, 0x66, 0x50, 0x99, 0xa6, 0x20, 0x9c, - 0x9d, 0xd0, 0xa2, 0x08, 0x93, 0x92, 0xda, 0x73, 0x32, 0x8f, 0x9d, 0x92, 0xeb, 0x40, 0xdb, 0xf6, - 0x66, 0xb1, 0xbb, 0xb8, 0x0d, 0x17, 0xfa, 0xf6, 0x66, 0xc6, 0x5d, 0x91, 0x1b, 0xfa, 0x5d, 0x91, - 0x0b, 0xfd, 0xb4, 0x6e, 0xa8, 0xde, 0x17, 0xf9, 0x2d, 0x23, 0xa5, 0x66, 0x85, 0x4d, 0xc4, 0xf3, - 0x2b, 0xf6, 0x5b, 0x87, 0x72, 0x98, 0x94, 0x81, 0x2b, 0xe2, 0x5c, 0x62, 0x8b, 0x31, 0x45, 0xac, - 0x2a, 0x72, 0x54, 0xc9, 0x8f, 0xa9, 0x71, 0xcd, 0x7f, 0x69, 0x00, 0xe1, 0x2d, 0x9c, 0x77, 0x3a, - 0xce, 0xb6, 0xd7, 0xf4, 0x22, 0x8f, 0x86, 0xe4, 0x1e, 0x94, 0x05, 0x0b, 0x67, 0xbb, 0x49, 0xd5, - 0x30, 0x30, 0x71, 0x4e, 0x1c, 0x97, 0xd9, 0x69, 0xeb, 0xa9, 0x87, 0xb0, 0x8f, 0x8c, 0xe4, 0x1e, - 0x43, 0x46, 0xcc, 0x1f, 0x1a, 0x70, 0xa1, 0xb7, 0xd9, 0xa2, 0xe6, 0xb8, 0xf3, 0x8c, 0x13, 0x3a, - 0x2f, 0xeb, 0x2b, 0x73, 0xe8, 0x61, 0x7d, 0x62, 0x5f, 0x99, 0x4f, 0x1c, 0xb6, 0xa7, 0xff, 0xca, - 0x87, 0x6a, 0x52, 0x0f, 0xf2, 0x7a, 0x56, 0x40, 0x0f, 0xbf, 0x75, 0xc3, 0xc1, 0x7a, 0x2c, 0x8f, - 0xdc, 0xe5, 0xe4, 0x32, 0x77, 0x39, 0xf2, 0x02, 0x51, 0x3e, 0xeb, 0x02, 0x91, 0xf9, 0x8b, 0x39, - 0x28, 0xad, 0x35, 0xbb, 0xbb, 0x5e, 0x7b, 0xc1, 0x89, 0x9c, 0xa7, 0x76, 0xcb, 0xf4, 0x96, 0xb6, - 0x65, 0x8a, 0x23, 0xce, 0xe2, 0x0f, 0x1b, 0x2c, 0xc9, 0x9e, 0x01, 0x93, 0x09, 0x09, 0x57, 0x0f, - 0x77, 0xa1, 0xc0, 0x7e, 0x08, 0x0b, 0xec, 0x72, 0x0f, 0x63, 0xc4, 0xba, 0x1e, 0xff, 0x27, 0x36, - 0x31, 0x7a, 0x6a, 0x43, 0xe4, 0x70, 0xf1, 0x0b, 0x3c, 0x33, 0xd9, 0xe9, 0xb3, 0xa8, 0xfe, 0xa1, - 0x01, 0xe5, 0xf4, 0x97, 0x90, 0x7b, 0x30, 0xc2, 0x38, 0x79, 0x71, 0x96, 0xb3, 0x17, 0xfb, 0x7c, - 0xf3, 0x75, 0x81, 0xc6, 0x9b, 0x87, 0x9d, 0x4f, 0x39, 0xc4, 0x92, 0x1c, 0x2e, 0x5a, 0x50, 0x52, - 0xb1, 0x32, 0x5a, 0xf7, 0x9a, 0xae, 0x13, 0xcf, 0x65, 0xf7, 0x83, 0xda, 0xea, 0xdf, 0xd4, 0x5a, - 0x2d, 0xb4, 0xe1, 0xa0, 0xe9, 0x2a, 0xf1, 0xca, 0x1d, 0x9f, 0x0e, 0xaa, 0x9c, 0x49, 0xad, 0xaf, - 0x5f, 0xb9, 0xe3, 0x30, 0xb6, 0xd7, 0xe2, 0xf5, 0x09, 0x39, 0xc3, 0xbd, 0x56, 0x07, 0x21, 0xaa, - 0xbd, 0xce, 0x71, 0xcc, 0x7f, 0x90, 0x87, 0x73, 0x49, 0xf3, 0x78, 0xf2, 0xce, 0x35, 0x27, 0x70, - 0x5a, 0xe1, 0x09, 0x33, 0xe0, 0x6a, 0x4f, 0xd3, 0xf0, 0x4a, 0xb9, 0x6c, 0x9a, 0xd2, 0x20, 0x33, - 0xd5, 0x20, 0xdc, 0xa4, 0xf2, 0x06, 0xc9, 0x66, 0x90, 0x7b, 0x90, 0xaf, 0xd3, 0x48, 0x5c, 0x3c, - 0xbd, 0xd2, 0xd3, 0xab, 0x6a, 0xbb, 0xae, 0xd7, 0x69, 0xc4, 0x07, 0x91, 0xc7, 0xee, 0x53, 0x2d, - 0x96, 0x9e, 0x6d, 0x37, 0xb6, 0x60, 0x78, 0xf1, 0x51, 0x87, 0x36, 0x22, 0x71, 0xdf, 0xf4, 0x95, - 0xe3, 0xf9, 0x71, 0x5c, 0xe5, 0x56, 0x2b, 0x45, 0x80, 0xda, 0x59, 0x1c, 0xe5, 0xe2, 0x9b, 0x50, - 0x94, 0x95, 0x9f, 0xea, 0x76, 0xe6, 0x5b, 0x30, 0xa6, 0x54, 0x72, 0x2a, 0xa1, 0xff, 0x6b, 0x03, - 0x86, 0x99, 0xb6, 0xdd, 0x7c, 0xe3, 0x29, 0xd5, 0x48, 0xb7, 0x34, 0x8d, 0x34, 0xa5, 0x5c, 0x23, - 0xc2, 0x79, 0xf9, 0xc6, 0x09, 0xba, 0xe8, 0xd0, 0x00, 0x48, 0x90, 0xc9, 0x1d, 0x18, 0x11, 0xf9, - 0x62, 0x44, 0x66, 0x5c, 0xf5, 0x5e, 0x92, 0xcc, 0x1c, 0x16, 0x5b, 0x71, 0x7e, 0x27, 0x6d, 0xf6, - 0x4a, 0x6a, 0xb2, 0x90, 0xc4, 0x6e, 0xab, 0x17, 0x61, 0x19, 0x9b, 0x79, 0xbf, 0xcd, 0xef, 0xa9, - 0x28, 0x39, 0xc8, 0xfa, 0x04, 0x71, 0x57, 0x85, 0xe3, 0x26, 0x7f, 0x1c, 0x93, 0x73, 0x82, 0x49, - 0xb6, 0x4f, 0xe7, 0x2f, 0x4b, 0xfc, 0xe6, 0x87, 0x6c, 0xd8, 0xbb, 0x50, 0xba, 0xed, 0x07, 0x0f, - 0x9d, 0xc0, 0xad, 0xee, 0x52, 0x11, 0x75, 0x5f, 0xc4, 0xd0, 0xf9, 0xf1, 0x1d, 0x0e, 0xb7, 0x1d, - 0x56, 0xf0, 0xa3, 0xc3, 0x4a, 0xa1, 0xe6, 0xfb, 0x4d, 0x4b, 0x43, 0x27, 0xab, 0x30, 0x7e, 0xdf, - 0x79, 0x24, 0xce, 0x40, 0xd7, 0xd7, 0x97, 0x45, 0xec, 0xce, 0x2b, 0x47, 0x87, 0x95, 0x0b, 0x2d, - 0xe7, 0x51, 0x7c, 0x76, 0xda, 0x3f, 0xbc, 0x5c, 0xa7, 0x27, 0x1e, 0x4c, 0xac, 0xf9, 0x41, 0x24, - 0x2a, 0x61, 0x36, 0x7b, 0xbe, 0xcf, 0x29, 0xda, 0x8d, 0xcc, 0x53, 0xb4, 0x0b, 0x6c, 0xa3, 0x62, - 0xef, 0xc4, 0xe4, 0xda, 0x75, 0x45, 0x8d, 0x31, 0x79, 0x17, 0xa6, 0xe6, 0x69, 0x10, 0x79, 0x3b, - 0x5e, 0xc3, 0x89, 0xe8, 0x6d, 0x3f, 0x68, 0x39, 0x91, 0x70, 0x18, 0xa1, 0xc3, 0xa0, 0x41, 0x39, - 0xa7, 0x96, 0x13, 0x59, 0xbd, 0x98, 0xe4, 0x2b, 0x59, 0xd1, 0x50, 0x43, 0xf8, 0xf9, 0xaf, 0x33, - 0x6b, 0x24, 0x23, 0x1a, 0xaa, 0x4f, 0x17, 0x64, 0xc4, 0x45, 0xed, 0x1e, 0x77, 0x94, 0x5c, 0xac, - 0xdd, 0x14, 0xc7, 0xda, 0x27, 0x1f, 0x15, 0xc7, 0xe3, 0xd6, 0xe7, 0xc8, 0x78, 0x0e, 0xf2, 0xb5, - 0xb5, 0xdb, 0xe8, 0x02, 0x12, 0x47, 0xb7, 0xb4, 0xbd, 0xe7, 0xb4, 0x1b, 0x68, 0x44, 0x89, 0x78, - 0x10, 0x55, 0xe1, 0xd5, 0xd6, 0x6e, 0x13, 0x07, 0xa6, 0xd7, 0x68, 0xd0, 0xf2, 0xa2, 0x2f, 0xdd, - 0xbc, 0xa9, 0x0c, 0x54, 0x11, 0x9b, 0x76, 0x43, 0x34, 0xad, 0xd2, 0x41, 0x14, 0xfb, 0xd1, 0xcd, - 0x9b, 0x99, 0xc3, 0x11, 0x37, 0x2c, 0x8b, 0x17, 0x59, 0x84, 0x89, 0xfb, 0xce, 0x23, 0x71, 0xc8, - 0x1f, 0xef, 0x61, 0xf3, 0x78, 0xdb, 0x00, 0x05, 0xab, 0x91, 0x14, 0xa9, 0x43, 0xac, 0x13, 0x91, - 0x77, 0x60, 0x2c, 0x11, 0xaf, 0x10, 0x8f, 0x77, 0xf3, 0x3c, 0xcc, 0x54, 0x11, 0x4e, 0xcd, 0x57, - 0xa6, 0xa0, 0x93, 0x8d, 0xd8, 0x05, 0xc1, 0x2d, 0x61, 0x3c, 0xd0, 0x1d, 0xad, 0xdd, 0x50, 0x5d, - 0x10, 0x0e, 0x96, 0x68, 0x9f, 0x35, 0x19, 0xef, 0x0d, 0x78, 0xf4, 0x91, 0xa5, 0x73, 0x51, 0x3c, - 0x1b, 0x6b, 0x81, 0xdf, 0xea, 0x44, 0x18, 0x85, 0x99, 0xf2, 0x6c, 0x74, 0xb0, 0x24, 0xc3, 0xb3, - 0xc1, 0x49, 0xb2, 0x63, 0x17, 0xc6, 0x1f, 0x23, 0x76, 0x81, 0x42, 0x61, 0xd9, 0x6f, 0xec, 0x63, - 0xd8, 0xe5, 0x68, 0xed, 0x23, 0xa6, 0x3f, 0x9a, 0x7e, 0x63, 0xff, 0xc9, 0x9d, 0xb9, 0x23, 0x7b, - 0xb2, 0xc2, 0xbe, 0x9d, 0x89, 0x95, 0xa8, 0x7a, 0x76, 0x52, 0x3b, 0x49, 0xd4, 0xca, 0xb8, 0xa1, - 0xc2, 0xa5, 0x50, 0x7e, 0x88, 0xa5, 0x93, 0x13, 0x0a, 0xe5, 0x05, 0x1a, 0xee, 0x47, 0x7e, 0x67, - 0xbe, 0xe9, 0x75, 0xb6, 0x7d, 0x27, 0x70, 0x67, 0xcb, 0x7d, 0x14, 0xc6, 0xcb, 0x99, 0x0a, 0x63, - 0xca, 0xe5, 0xf4, 0x76, 0x43, 0x32, 0xb0, 0x7a, 0x58, 0x92, 0xaf, 0xc0, 0x04, 0x9b, 0x2d, 0x8b, - 0x8f, 0x22, 0xda, 0xe6, 0xa2, 0x34, 0x85, 0x4b, 0xfd, 0x8c, 0x72, 0x71, 0x33, 0x2e, 0xe4, 0x42, - 0x8a, 0xda, 0x83, 0xc6, 0x04, 0xaa, 0x90, 0xea, 0xac, 0x88, 0x0b, 0xb3, 0xf7, 0x9d, 0x47, 0x4a, - 0x9a, 0x23, 0x45, 0xea, 0x09, 0x4a, 0x2c, 0x26, 0xd4, 0x63, 0x12, 0xbb, 0x1f, 0x23, 0xf5, 0x99, - 0x00, 0x7d, 0x39, 0x91, 0x9f, 0x86, 0xf3, 0xe2, 0xb3, 0x16, 0x30, 0x57, 0x82, 0x1f, 0x1c, 0xd4, - 0xf7, 0x9c, 0x80, 0x4d, 0xdc, 0xe9, 0xd3, 0x69, 0x58, 0xd9, 0x61, 0xae, 0xe4, 0x63, 0x87, 0x9c, - 0x91, 0xd5, 0xaf, 0x06, 0xf3, 0xc7, 0x53, 0xc3, 0x4e, 0x96, 0x60, 0x44, 0xe0, 0x8a, 0x85, 0xb5, - 0xb7, 0xf6, 0x67, 0x33, 0x6b, 0x1f, 0x11, 0xb5, 0x5b, 0x92, 0xde, 0xfc, 0x23, 0x03, 0xc6, 0xb5, - 0x1e, 0x25, 0x6f, 0x2a, 0x51, 0x4f, 0x49, 0xb4, 0xa2, 0x86, 0x93, 0xf9, 0xa8, 0xc6, 0x9b, 0x22, - 0xd4, 0x2d, 0xd7, 0x9f, 0x2e, 0x33, 0x89, 0xdd, 0xb1, 0x5b, 0xbd, 0x24, 0x57, 0x44, 0xa1, 0x4f, - 0xae, 0x88, 0x5f, 0x19, 0x87, 0x09, 0x7d, 0x0d, 0x67, 0x46, 0xf5, 0xb2, 0xbf, 0xeb, 0xb5, 0xa5, - 0x4f, 0x80, 0x67, 0x3f, 0x41, 0x88, 0xf6, 0x42, 0x05, 0x42, 0xc8, 0x4b, 0x00, 0xf1, 0xe9, 0xba, - 0xdc, 0xf6, 0x8b, 0xf7, 0x34, 0x94, 0x02, 0xf2, 0x13, 0x00, 0x2b, 0xbe, 0x4b, 0xe3, 0x04, 0x3a, - 0xc7, 0xf8, 0x04, 0x5f, 0x16, 0x3e, 0x41, 0xf1, 0x06, 0xc6, 0xd1, 0x61, 0xe5, 0x6c, 0xdb, 0x77, - 0x69, 0x6f, 0xe6, 0x1c, 0x85, 0x23, 0xf9, 0x22, 0x0c, 0x59, 0xdd, 0x26, 0x95, 0xf9, 0x5c, 0xc6, - 0xe4, 0x9c, 0xee, 0x36, 0x95, 0xdc, 0xb8, 0x41, 0x37, 0x7d, 0x14, 0xc4, 0x00, 0xe4, 0x7d, 0x00, - 0x26, 0xb6, 0x98, 0x64, 0x52, 0x5e, 0x18, 0x47, 0x17, 0x81, 0x22, 0xf1, 0x98, 0x9a, 0x52, 0xab, - 0x3c, 0x21, 0x21, 0xab, 0x30, 0x22, 0x34, 0xa4, 0x38, 0x6a, 0x79, 0x2e, 0xcb, 0xc9, 0xa7, 0x98, - 0x49, 0x22, 0xc1, 0x0a, 0x82, 0x75, 0xbf, 0x1b, 0x77, 0x71, 0xbc, 0x03, 0xa3, 0x8c, 0xfd, 0x46, - 0x48, 0xc5, 0x35, 0xf2, 0x51, 0x1e, 0x76, 0xaa, 0x34, 0xa8, 0x1b, 0xea, 0x1e, 0x86, 0x84, 0x80, - 0x7c, 0x05, 0x53, 0x22, 0x89, 0xae, 0x3e, 0xd6, 0x57, 0x7c, 0xa5, 0xa7, 0xab, 0x67, 0x9c, 0x4e, - 0x27, 0x23, 0x87, 0x5c, 0xcc, 0x8f, 0xec, 0xc6, 0x57, 0xb3, 0xe2, 0x04, 0xe9, 0xc7, 0x54, 0x70, - 0xad, 0xa7, 0x82, 0x59, 0x79, 0xdb, 0xa8, 0x37, 0x11, 0x92, 0xc6, 0x97, 0x74, 0xa0, 0x9c, 0x28, - 0x13, 0x51, 0x17, 0x1c, 0x57, 0xd7, 0xeb, 0x3d, 0x75, 0xa9, 0x03, 0xd8, 0x53, 0x5d, 0x0f, 0x77, - 0xe2, 0x26, 0xc9, 0xac, 0x45, 0x7d, 0x63, 0xc7, 0xd5, 0xf7, 0x52, 0x4f, 0x7d, 0xd3, 0xee, 0x76, - 0x6f, 0x3d, 0x29, 0x9e, 0xe4, 0x1d, 0x18, 0x97, 0x10, 0x9c, 0x1f, 0xe8, 0xa3, 0x15, 0x5b, 0x18, - 0x77, 0x1b, 0x63, 0x0d, 0xf5, 0x2c, 0x40, 0x2a, 0xb2, 0x4a, 0xcd, 0xa5, 0x63, 0x5c, 0xa3, 0x4e, - 0x4b, 0x85, 0x8e, 0x4c, 0xbe, 0x0c, 0x63, 0x4b, 0x2d, 0xf6, 0x21, 0x7e, 0xdb, 0x89, 0x28, 0xae, - 0xb7, 0x89, 0xdf, 0x5b, 0x29, 0x51, 0x44, 0x95, 0xa7, 0x1b, 0x4d, 0x8a, 0x54, 0x7b, 0x45, 0xa1, - 0x60, 0x9d, 0xc7, 0x5d, 0x5b, 0x42, 0x86, 0x43, 0xb1, 0xba, 0x3e, 0x9b, 0xe1, 0x7b, 0x56, 0xd8, - 0xe3, 0x72, 0xc5, 0x3d, 0x66, 0xb6, 0x98, 0x10, 0x5a, 0xe7, 0xe9, 0x3c, 0xc9, 0xbb, 0x30, 0x26, - 0x2e, 0xc2, 0x56, 0xad, 0x95, 0x70, 0xb6, 0x8c, 0x1f, 0x8f, 0x29, 0xfc, 0xe4, 0x9d, 0x59, 0xdb, - 0x09, 0x52, 0x07, 0x90, 0x09, 0x3e, 0xf9, 0x12, 0xcc, 0x6c, 0x79, 0x6d, 0xd7, 0x7f, 0x18, 0x0a, - 0x05, 0x2e, 0x14, 0xdd, 0x54, 0x12, 0x66, 0xf5, 0x90, 0x97, 0xdb, 0x72, 0xa1, 0xe9, 0x51, 0x7c, - 0x99, 0x1c, 0xc8, 0xcf, 0xf4, 0x70, 0xe6, 0x12, 0x44, 0x8e, 0x93, 0xa0, 0xb9, 0x1e, 0x09, 0xea, - 0xad, 0x3e, 0x2d, 0x4e, 0x99, 0xd5, 0x10, 0x1f, 0x88, 0x6e, 0x56, 0x7d, 0xe8, 0x7b, 0xed, 0xd9, - 0x69, 0xed, 0xf9, 0xa1, 0x38, 0xd2, 0x1a, 0xf1, 0xd6, 0xfc, 0xa6, 0xd7, 0x38, 0x90, 0xa9, 0x7c, - 0x75, 0x83, 0xed, 0x63, 0x5f, 0xf3, 0x9f, 0x64, 0xb0, 0x26, 0x5f, 0x86, 0x12, 0xfb, 0x1b, 0x5b, - 0xb7, 0x33, 0xda, 0x69, 0xa5, 0x82, 0x29, 0xea, 0xc1, 0x31, 0xc2, 0x9b, 0xba, 0x19, 0x86, 0xaf, - 0xc6, 0xca, 0xfc, 0xa1, 0x01, 0x33, 0x59, 0x6d, 0x3d, 0x21, 0x2d, 0x92, 0x99, 0x8a, 0x5b, 0x40, - 0xd7, 0x0b, 0x8f, 0x5b, 0x88, 0xa3, 0x15, 0x2a, 0x30, 0x74, 0xcf, 0x6b, 0xbb, 0x32, 0xae, 0x0e, - 0x97, 0xc3, 0x7d, 0x06, 0xb0, 0x38, 0x9c, 0x21, 0xe0, 0x1d, 0x0c, 0x5c, 0x2f, 0x87, 0x38, 0x02, - 0x5e, 0xd4, 0xb0, 0x38, 0x9c, 0x21, 0xb0, 0x65, 0x57, 0x2e, 0x13, 0x88, 0xc0, 0x56, 0xe3, 0xd0, - 0xe2, 0x70, 0x72, 0x05, 0x46, 0x56, 0xdb, 0xcb, 0xd4, 0x79, 0x40, 0xc5, 0xa1, 0x21, 0xba, 0x8a, - 0xfc, 0xb6, 0xdd, 0x64, 0x30, 0x4b, 0x16, 0x9a, 0xdf, 0x31, 0x60, 0xaa, 0xa7, 0x9b, 0x4e, 0xce, - 0xfc, 0x74, 0xfc, 0x09, 0xed, 0x20, 0xdf, 0xc7, 0x9b, 0x5f, 0xc8, 0x6e, 0xbe, 0xf9, 0xfb, 0x05, - 0x38, 0xdf, 0x67, 0xd5, 0x4a, 0xa2, 0x2b, 0x8c, 0x13, 0xa3, 0x2b, 0xbe, 0xca, 0x56, 0x09, 0xc7, - 0x6b, 0x85, 0xeb, 0x7e, 0xd2, 0xe2, 0xe4, 0x20, 0x0a, 0xcb, 0x64, 0x6a, 0x15, 0x99, 0x06, 0xe4, - 0x42, 0x03, 0x29, 0xec, 0xc8, 0xef, 0xf1, 0xc7, 0xeb, 0xcc, 0x7a, 0xe2, 0x1b, 0xf2, 0x7f, 0x43, - 0xe2, 0x1b, 0xf4, 0x53, 0xc5, 0xc2, 0x13, 0x3d, 0x55, 0xcc, 0x3e, 0x80, 0x18, 0x7a, 0x9c, 0xa3, - 0xb8, 0x79, 0x18, 0xaf, 0x53, 0x27, 0x68, 0xec, 0x55, 0x43, 0x3e, 0x48, 0xc3, 0xc8, 0x0d, 0x55, - 0x72, 0x88, 0x05, 0xb6, 0x13, 0xf6, 0x8e, 0x85, 0x46, 0x63, 0xfe, 0xa7, 0x54, 0x58, 0xc6, 0xdf, - 0x44, 0x79, 0x79, 0x05, 0x86, 0xb6, 0xf6, 0x68, 0x20, 0x8d, 0x64, 0x6c, 0xc8, 0x43, 0x06, 0x50, - 0x1b, 0x82, 0x18, 0xe6, 0x4f, 0x43, 0x49, 0xad, 0x0c, 0x15, 0x02, 0xfb, 0x2d, 0x66, 0x24, 0x57, - 0x08, 0x0c, 0x60, 0x71, 0xf8, 0x89, 0xd9, 0xd8, 0x92, 0x5e, 0xc8, 0x9f, 0xd4, 0x0b, 0xac, 0x72, - 0x94, 0x37, 0xa5, 0x72, 0xfc, 0xad, 0x56, 0x1e, 0x31, 0x80, 0xc5, 0xe1, 0x4f, 0xb4, 0xf2, 0xff, - 0x60, 0x40, 0x01, 0x33, 0x61, 0xbc, 0x01, 0xa3, 0xd2, 0x9f, 0xad, 0x66, 0x87, 0x98, 0x96, 0xee, - 0xee, 0x50, 0x0f, 0xaa, 0x11, 0x40, 0x56, 0xd5, 0x26, 0x0d, 0xb6, 0xb5, 0xd8, 0xab, 0x07, 0x0c, - 0xa0, 0x56, 0x85, 0x18, 0xa7, 0x18, 0x0f, 0x8c, 0x2f, 0x13, 0xdb, 0x51, 0xae, 0xb2, 0x78, 0x7c, - 0x59, 0xcf, 0xde, 0x53, 0x62, 0x99, 0xbf, 0x6e, 0xc0, 0xd9, 0x4c, 0x4b, 0x86, 0xd5, 0xca, 0x4d, - 0x26, 0x45, 0x1c, 0xd3, 0xf6, 0x12, 0xc7, 0x38, 0x4d, 0x1c, 0xd9, 0x29, 0x64, 0xeb, 0x79, 0x18, - 0x8d, 0x77, 0x98, 0x64, 0x46, 0x0e, 0x1d, 0x3a, 0x3d, 0xe5, 0x76, 0xec, 0xaf, 0x0d, 0x18, 0x66, - 0x4d, 0x78, 0x6a, 0xaf, 0x15, 0x65, 0xbb, 0xc0, 0xd9, 0x27, 0x0d, 0x74, 0x99, 0xe8, 0x77, 0x86, - 0x01, 0x12, 0x64, 0xb2, 0x0d, 0x13, 0xab, 0x4b, 0x0b, 0xf3, 0x4b, 0x2e, 0x6d, 0x47, 0x78, 0x06, - 0x9c, 0x4a, 0x2f, 0xc1, 0xb6, 0xc6, 0x41, 0xdb, 0x69, 0x0a, 0x84, 0x83, 0x44, 0x37, 0xf8, 0x9e, - 0xdb, 0xb0, 0xbd, 0x98, 0x4e, 0x35, 0x29, 0x75, 0x8e, 0xac, 0x8e, 0x7a, 0xf5, 0xfe, 0xb2, 0x52, - 0x47, 0x6e, 0xc0, 0x3a, 0x42, 0xa7, 0xd5, 0xec, 0x53, 0x87, 0xce, 0x91, 0xec, 0x41, 0xf9, 0x0e, - 0xae, 0x3e, 0x4a, 0x2d, 0xf9, 0xe3, 0x6b, 0x79, 0x41, 0xd4, 0xf2, 0x0c, 0x5f, 0xb6, 0xb2, 0xeb, - 0xe9, 0xe1, 0x9a, 0x48, 0x6e, 0xe1, 0x44, 0xc9, 0xfd, 0x3b, 0x06, 0x0c, 0xf3, 0xe5, 0x2d, 0x7e, - 0x43, 0x28, 0x73, 0x01, 0xdd, 0x7a, 0x32, 0x0b, 0x68, 0x19, 0x35, 0x97, 0xe6, 0x42, 0xe0, 0x65, - 0x64, 0x21, 0xf5, 0x20, 0x91, 0x3c, 0xe7, 0x40, 0xd3, 0x9a, 0x97, 0x24, 0xd1, 0x78, 0xfc, 0x2d, - 0x22, 0x95, 0x0b, 0xc7, 0x50, 0x9f, 0x47, 0x1d, 0x79, 0xcc, 0xe7, 0x51, 0x97, 0x61, 0x54, 0x84, - 0x97, 0xd5, 0x0e, 0xc4, 0x06, 0x5a, 0xba, 0x88, 0x62, 0xb8, 0x92, 0x75, 0x9d, 0x83, 0xec, 0x6d, - 0x2d, 0x67, 0x62, 0x8c, 0x48, 0x56, 0x61, 0x34, 0xb9, 0x13, 0x35, 0xaa, 0x1d, 0x56, 0xc7, 0x70, - 0x11, 0x7f, 0xcd, 0xaf, 0xdd, 0x66, 0x5e, 0x81, 0x4a, 0x78, 0x98, 0xdf, 0x34, 0xa0, 0x9c, 0x96, - 0x17, 0xf2, 0x0e, 0x8c, 0xc5, 0xd7, 0xd2, 0xe2, 0xe8, 0x13, 0xf4, 0x36, 0x27, 0xf7, 0xd8, 0xb4, - 0x38, 0x14, 0x15, 0x9d, 0xcc, 0x41, 0x91, 0x4d, 0x3b, 0x25, 0x69, 0x36, 0xea, 0x93, 0xae, 0x80, - 0xa9, 0x87, 0xaf, 0x12, 0x4f, 0x99, 0xb5, 0xff, 0x25, 0x0f, 0x63, 0xca, 0x60, 0x91, 0x57, 0xa0, - 0xb8, 0x14, 0x2e, 0xfb, 0x8d, 0x7d, 0xea, 0x8a, 0x33, 0x1d, 0x7c, 0xfd, 0xd6, 0x0b, 0xed, 0x26, - 0x02, 0xad, 0xb8, 0x98, 0xd4, 0x60, 0x9c, 0xff, 0x27, 0xaf, 0x1f, 0xe7, 0x12, 0x7f, 0x34, 0x47, - 0x96, 0x17, 0x8f, 0xd5, 0xe5, 0x5d, 0x23, 0x21, 0x5f, 0x03, 0xe0, 0x00, 0x36, 0xbe, 0x03, 0x44, - 0x97, 0xcb, 0x09, 0x7c, 0x56, 0x54, 0x10, 0x79, 0xea, 0x17, 0xa2, 0x28, 0x28, 0x0c, 0xf1, 0xe5, - 0x4d, 0xbf, 0xb1, 0x3f, 0xf8, 0xdb, 0xbb, 0xc9, 0xcb, 0x9b, 0x7e, 0x63, 0xdf, 0xce, 0x0e, 0x35, - 0x54, 0x59, 0x92, 0x6f, 0x19, 0x70, 0xd1, 0xa2, 0x0d, 0xff, 0x01, 0x0d, 0x0e, 0xaa, 0x11, 0x62, - 0xa9, 0x35, 0x9e, 0x1c, 0xd7, 0x78, 0x4b, 0xd4, 0xf8, 0x72, 0x20, 0xb8, 0xe0, 0x9d, 0xa8, 0x56, - 0x27, 0xb2, 0x8f, 0x69, 0xc2, 0x31, 0x55, 0x9a, 0x7f, 0x66, 0x28, 0x53, 0x80, 0xac, 0xc0, 0x68, - 0x2c, 0x2c, 0xc2, 0x65, 0x1a, 0x5b, 0x66, 0x12, 0x6e, 0xd1, 0x9d, 0xda, 0x33, 0xe2, 0xf8, 0x65, - 0x3a, 0x16, 0x39, 0x6d, 0x46, 0x48, 0x20, 0xf9, 0x00, 0x0a, 0x38, 0x54, 0x27, 0x67, 0x59, 0x93, - 0x4b, 0x4d, 0x81, 0x8d, 0x11, 0xb6, 0x1a, 0x29, 0xc9, 0xe7, 0x44, 0x0c, 0x50, 0x5e, 0xcb, 0x5f, - 0xcc, 0x40, 0xac, 0x1d, 0xf1, 0x1a, 0x93, 0x44, 0xb7, 0x2a, 0xd2, 0xfa, 0xf7, 0x72, 0x50, 0x4e, - 0x4f, 0x3c, 0xf2, 0x3e, 0x94, 0xe4, 0xfd, 0xb6, 0xbb, 0x4e, 0xb8, 0x27, 0x72, 0xa2, 0xe2, 0xae, - 0x55, 0x5e, 0x8a, 0xb3, 0xf7, 0x1c, 0x2d, 0x77, 0x9e, 0x46, 0xc0, 0x16, 0xe4, 0x75, 0x71, 0x69, - 0x42, 0x99, 0x40, 0x91, 0x1f, 0x75, 0x52, 0x39, 0x51, 0x25, 0x1a, 0x79, 0x03, 0xf2, 0xfc, 0xd2, - 0xa7, 0x9a, 0x50, 0xeb, 0xfe, 0xed, 0x2a, 0xbf, 0xb3, 0xc6, 0x4f, 0xfc, 0xf5, 0xa3, 0x13, 0x86, - 0x4f, 0x96, 0x95, 0x2b, 0x83, 0xc3, 0x5a, 0x62, 0x21, 0x09, 0x8e, 0x3f, 0xee, 0xe4, 0xbb, 0x83, - 0x1f, 0x16, 0x8a, 0xf9, 0x72, 0x41, 0x5c, 0x12, 0xfb, 0xbd, 0x3c, 0x8c, 0xc6, 0xf5, 0x13, 0x02, - 0x68, 0x6f, 0x88, 0xa3, 0x7b, 0xfc, 0x9f, 0x5c, 0x80, 0xa2, 0x34, 0x31, 0xc4, 0xf1, 0xfd, 0x48, - 0x28, 0xcc, 0x8b, 0x59, 0x90, 0xb6, 0x04, 0x37, 0x2f, 0x2c, 0xf9, 0x93, 0xdc, 0x84, 0xd8, 0x50, - 0xe8, 0x67, 0x51, 0x14, 0xd8, 0x80, 0x59, 0x31, 0x1a, 0x99, 0x80, 0x9c, 0xc7, 0x03, 0xe2, 0x47, - 0xad, 0x9c, 0xe7, 0x92, 0xf7, 0xa1, 0xe8, 0xb8, 0x2e, 0x75, 0x6d, 0x27, 0x1a, 0xe0, 0x1d, 0xe4, - 0x22, 0xe3, 0xc6, 0x35, 0x3a, 0x52, 0x55, 0x23, 0x52, 0x85, 0x51, 0x7c, 0x06, 0xb7, 0x1b, 0x0e, - 0xf4, 0x76, 0x6e, 0xc2, 0xa1, 0xc8, 0xc8, 0x36, 0x42, 0xea, 0x92, 0x97, 0xa1, 0xc0, 0x46, 0x53, - 0xac, 0x07, 0x71, 0x9a, 0xc4, 0xd5, 0xf5, 0x35, 0xde, 0x61, 0x77, 0xcf, 0x58, 0x88, 0x40, 0x5e, - 0x84, 0x7c, 0x77, 0x6e, 0x47, 0x68, 0xfa, 0x72, 0x72, 0x1f, 0x38, 0x46, 0x63, 0xc5, 0xe4, 0x16, - 0x14, 0x1f, 0xea, 0x37, 0x3f, 0xcf, 0xa6, 0x86, 0x31, 0xc6, 0x8f, 0x11, 0x6b, 0x45, 0x18, 0xe6, - 0x77, 0x1e, 0xcd, 0xe7, 0x00, 0x92, 0xaa, 0x7b, 0xa3, 0x2c, 0xcc, 0xaf, 0xc1, 0x68, 0x5c, 0x25, - 0x79, 0x16, 0x60, 0x9f, 0x1e, 0xd8, 0x7b, 0x4e, 0xdb, 0x15, 0x6f, 0xfa, 0x94, 0xac, 0xd1, 0x7d, - 0x7a, 0x70, 0x17, 0x01, 0xe4, 0x3c, 0x8c, 0x74, 0xd8, 0xa8, 0xca, 0x8c, 0xbe, 0xd6, 0x70, 0xa7, - 0xbb, 0xcd, 0x24, 0x74, 0x16, 0x46, 0xd0, 0xf9, 0x21, 0x26, 0xda, 0xb8, 0x25, 0x7f, 0x9a, 0xbf, - 0x95, 0xc3, 0x5c, 0x0f, 0x4a, 0x3b, 0xc9, 0x0b, 0x30, 0xde, 0x08, 0x28, 0x2e, 0x47, 0x0e, 0x33, - 0x8b, 0x44, 0x3d, 0xa5, 0x04, 0xb8, 0xe4, 0x92, 0x2b, 0x30, 0x99, 0xa4, 0x18, 0xb6, 0x1b, 0xdb, - 0xe2, 0xde, 0x77, 0xc9, 0x1a, 0xef, 0xc8, 0x1c, 0xc3, 0xf3, 0xdb, 0x78, 0x91, 0xa3, 0xac, 0xde, - 0x77, 0x8c, 0x64, 0xba, 0xe0, 0x51, 0x6b, 0x52, 0x81, 0xe3, 0xc1, 0xc9, 0x39, 0x18, 0x76, 0x9c, - 0xdd, 0xae, 0xc7, 0x83, 0xca, 0x4b, 0x96, 0xf8, 0x45, 0x5e, 0x85, 0xa9, 0xd0, 0xdb, 0x6d, 0x3b, - 0x51, 0x37, 0x10, 0xc9, 0x36, 0x68, 0x80, 0x22, 0x35, 0x6e, 0x95, 0xe3, 0x82, 0x79, 0x0e, 0x27, - 0xaf, 0x03, 0x51, 0xeb, 0xf3, 0xb7, 0x3f, 0xa6, 0x0d, 0x2e, 0x6a, 0x25, 0x6b, 0x4a, 0x29, 0x59, - 0xc5, 0x02, 0xf2, 0x3c, 0x94, 0x02, 0x1a, 0xa2, 0x49, 0x86, 0xdd, 0x86, 0x29, 0x84, 0xac, 0x31, - 0x09, 0xbb, 0x47, 0x0f, 0xcc, 0x1a, 0x4c, 0xf5, 0xcc, 0x47, 0xf2, 0x3a, 0xb7, 0xee, 0xc5, 0xfa, - 0x5c, 0xe2, 0x9b, 0x19, 0x7c, 0xfa, 0x4a, 0x7f, 0x39, 0x9d, 0x23, 0x99, 0x6d, 0x28, 0xa9, 0xfa, - 0xf5, 0x84, 0x1b, 0xf5, 0xe7, 0x30, 0xec, 0x94, 0x2b, 0x9f, 0xe1, 0xa3, 0xc3, 0x4a, 0xce, 0x73, - 0x31, 0xd8, 0xf4, 0x2a, 0x14, 0xa5, 0x95, 0xa0, 0xbe, 0x3f, 0x23, 0x0c, 0xca, 0x03, 0x2b, 0x2e, - 0x35, 0x5f, 0x86, 0x11, 0xa1, 0x42, 0x8f, 0x77, 0x44, 0x99, 0xdf, 0xc8, 0xc1, 0xa4, 0x45, 0xd9, - 0x04, 0x17, 0x2f, 0xbb, 0x7c, 0xc6, 0x92, 0x2d, 0x6b, 0xdf, 0x76, 0x4c, 0x02, 0x8b, 0xef, 0x1a, - 0x30, 0x9d, 0x81, 0xfb, 0x89, 0xb2, 0xb3, 0xbd, 0x09, 0xa3, 0x0b, 0x9e, 0xd3, 0xac, 0xba, 0x6e, - 0x1c, 0x3e, 0x8b, 0xd6, 0xa0, 0xcb, 0xa6, 0x93, 0xc3, 0xa0, 0xea, 0x62, 0x1a, 0xa3, 0x92, 0x6b, - 0x42, 0x28, 0x92, 0xfc, 0x91, 0x32, 0x9d, 0x33, 0xf0, 0x36, 0x25, 0xc9, 0x9c, 0xf1, 0x2e, 0x24, - 0x07, 0x26, 0xa7, 0xb3, 0x4f, 0xed, 0xd0, 0x65, 0xdf, 0x85, 0x4c, 0x7f, 0xde, 0x40, 0xdb, 0xce, - 0x6f, 0xe6, 0xe0, 0x5c, 0x36, 0xe1, 0x27, 0x4d, 0xb4, 0x87, 0xd9, 0x43, 0x94, 0x8c, 0xd9, 0x98, - 0x68, 0x8f, 0xa7, 0x1a, 0x41, 0xfc, 0x04, 0x81, 0xec, 0xc0, 0xf8, 0xb2, 0x13, 0x46, 0x77, 0xa9, - 0x13, 0x44, 0xdb, 0xd4, 0x89, 0x06, 0xb0, 0x60, 0xe3, 0xf7, 0xc9, 0x71, 0x51, 0xdb, 0x93, 0x94, - 0xe9, 0xf7, 0xc9, 0x35, 0xb6, 0xb1, 0xa0, 0x14, 0x06, 0x10, 0x94, 0xaf, 0xc3, 0x64, 0x9d, 0xb6, - 0x9c, 0xce, 0x9e, 0x1f, 0x50, 0xe1, 0x3b, 0xbf, 0x0e, 0xe3, 0x31, 0x28, 0x53, 0x5a, 0xf4, 0x62, - 0x0d, 0x5f, 0xe9, 0x88, 0x44, 0x95, 0xe8, 0xc5, 0xe6, 0x6f, 0xe4, 0xe0, 0x7c, 0xb5, 0x21, 0x0e, - 0x1a, 0x44, 0x81, 0x3c, 0x0f, 0xfd, 0x94, 0xeb, 0x26, 0x37, 0x60, 0xf4, 0xbe, 0xf3, 0x68, 0x99, - 0xe2, 0x0b, 0xc2, 0x3c, 0x5d, 0x13, 0x37, 0xbf, 0x9c, 0x47, 0x76, 0xec, 0xf6, 0xb2, 0x12, 0x1c, - 0x75, 0xb3, 0x59, 0x78, 0xcc, 0xcd, 0xa6, 0x09, 0xc3, 0x77, 0xfd, 0xa6, 0x2b, 0x16, 0x27, 0x71, - 0x6e, 0xb1, 0x87, 0x10, 0x4b, 0x94, 0x98, 0x3f, 0x34, 0x60, 0x22, 0x6e, 0x31, 0x36, 0xe1, 0x53, - 0xef, 0x92, 0x2b, 0x30, 0x82, 0x15, 0xc5, 0xaf, 0x23, 0xe1, 0xa2, 0xd1, 0x64, 0x20, 0xdb, 0x73, - 0x2d, 0x59, 0xa8, 0xf6, 0xc4, 0xd0, 0xe3, 0xf5, 0x84, 0xf9, 0xcf, 0xf0, 0x48, 0x44, 0xfd, 0x4a, - 0xb6, 0x12, 0x29, 0x0d, 0x31, 0x06, 0x6c, 0x48, 0xee, 0x89, 0x0d, 0x49, 0xbe, 0xef, 0x90, 0xfc, - 0x42, 0x0e, 0xc6, 0xe2, 0xc6, 0x7e, 0xc6, 0x92, 0x08, 0xc4, 0xdf, 0x35, 0x50, 0x08, 0x7d, 0x5d, - 0xd1, 0x15, 0x22, 0x52, 0xfd, 0x03, 0x18, 0x16, 0x93, 0xc9, 0x48, 0x9d, 0x0b, 0xa6, 0x46, 0x37, - 0x79, 0x63, 0x1a, 0x07, 0x34, 0xb4, 0x04, 0x1d, 0xde, 0x51, 0xd8, 0xa2, 0xdb, 0xe2, 0x84, 0xec, - 0xa9, 0x5d, 0xa3, 0xb2, 0xef, 0x28, 0x24, 0x1f, 0x36, 0xd0, 0xea, 0xf4, 0x8f, 0x0a, 0x50, 0x4e, - 0x93, 0x9c, 0x9c, 0xa6, 0x61, 0xad, 0xbb, 0x2d, 0x1e, 0xe8, 0xc0, 0x34, 0x0d, 0x9d, 0xee, 0xb6, - 0xc5, 0x60, 0xe4, 0x0a, 0x14, 0xd6, 0x02, 0xef, 0x01, 0x7e, 0xb5, 0x78, 0x9f, 0xa4, 0x13, 0x78, - 0x0f, 0xd4, 0x60, 0x5d, 0x56, 0x8e, 0x1b, 0xda, 0xe5, 0xba, 0xf2, 0xbc, 0x3f, 0xdf, 0xd0, 0x36, - 0xc3, 0x74, 0x3e, 0x20, 0x89, 0xc6, 0x96, 0xca, 0x1a, 0x75, 0x02, 0x91, 0x52, 0x40, 0xa8, 0x33, - 0x5c, 0x2a, 0xb7, 0x11, 0xcc, 0x93, 0xfd, 0x5a, 0x2a, 0x12, 0x69, 0x02, 0x51, 0x7e, 0xca, 0x09, - 0x7c, 0xf2, 0x1e, 0x4f, 0xbe, 0xab, 0x35, 0xa3, 0xb2, 0xb6, 0xd5, 0xd9, 0x9c, 0xc1, 0xf7, 0x49, - 0xfa, 0x08, 0xd7, 0x60, 0x14, 0x5d, 0x5e, 0xe8, 0xc8, 0x28, 0x9e, 0xc8, 0x4c, 0x06, 0x46, 0x03, - 0xc6, 0x13, 0xd8, 0xb1, 0x3b, 0x23, 0x61, 0x42, 0xde, 0x83, 0x31, 0x35, 0x9a, 0x97, 0xc7, 0x9c, - 0x5e, 0xe2, 0xf7, 0xc7, 0xfa, 0xe4, 0xcd, 0x53, 0x09, 0xcc, 0xcf, 0xa9, 0x52, 0x22, 0x16, 0xed, - 0x63, 0xa5, 0xc4, 0xfc, 0x35, 0x34, 0xe3, 0x5b, 0x7e, 0x44, 0x85, 0xf5, 0xf2, 0xd4, 0xea, 0xb1, - 0xc4, 0x85, 0x3c, 0xa4, 0xc5, 0xb4, 0x68, 0x5f, 0x77, 0x8a, 0x87, 0xed, 0x7f, 0xd7, 0x80, 0xb3, - 0x99, 0xb4, 0xe4, 0x3a, 0x40, 0x62, 0x23, 0x8a, 0x5e, 0xe2, 0x59, 0x94, 0x63, 0xa8, 0xa5, 0x60, - 0x90, 0xaf, 0xa6, 0xad, 0xbb, 0x93, 0x17, 0x27, 0xf9, 0xd6, 0xc8, 0x84, 0x6e, 0xdd, 0x65, 0xd8, - 0x74, 0xe6, 0x77, 0xf3, 0x30, 0xd5, 0xf3, 0x46, 0xe5, 0x09, 0x51, 0x04, 0xfb, 0xa9, 0x17, 0xd0, - 0xf8, 0x71, 0xc7, 0xb5, 0x7e, 0x2f, 0x64, 0x66, 0xbc, 0x87, 0x86, 0x6e, 0x31, 0x91, 0xc0, 0xfb, - 0x84, 0x67, 0xd1, 0xc2, 0xec, 0xb7, 0xf3, 0x5e, 0xed, 0x5b, 0xdb, 0x13, 0x78, 0x43, 0xef, 0x6f, - 0xf0, 0x13, 0x63, 0xbf, 0x96, 0x83, 0xe9, 0x9e, 0x6f, 0x7e, 0x6a, 0x67, 0xdd, 0x07, 0xda, 0xea, - 0xf6, 0x5c, 0xbf, 0x31, 0x1d, 0xc8, 0x8a, 0xf8, 0x5f, 0x06, 0x9c, 0xef, 0x43, 0x49, 0x0e, 0xd2, - 0x42, 0xc4, 0xad, 0x8a, 0x9b, 0xc7, 0x57, 0xf8, 0x44, 0x44, 0xe9, 0x53, 0x93, 0x84, 0x6f, 0xe4, - 0x00, 0xb6, 0xe8, 0xf6, 0xd3, 0x9d, 0x83, 0xea, 0x0b, 0x9a, 0x00, 0x28, 0x0e, 0xcc, 0xc1, 0x53, - 0x50, 0xad, 0xa2, 0x23, 0x71, 0xf0, 0x04, 0x54, 0xf1, 0x7b, 0x2a, 0xb9, 0xec, 0xf7, 0x54, 0xcc, - 0x6d, 0x98, 0xb9, 0x43, 0xa3, 0x64, 0x25, 0x94, 0x7b, 0xc8, 0xe3, 0xd9, 0xbe, 0x06, 0xa3, 0x02, - 0x5f, 0xcf, 0x8d, 0x2f, 0x43, 0xe2, 0x3c, 0xd7, 0x4a, 0x10, 0x4c, 0x0a, 0xe7, 0x17, 0x68, 0x93, - 0x46, 0xf4, 0xd3, 0xad, 0xa6, 0x0e, 0x84, 0x7f, 0x0a, 0x7f, 0x66, 0x63, 0xa0, 0x1a, 0x4e, 0xec, - 0x9f, 0x4d, 0x38, 0x1b, 0xb7, 0xfd, 0x49, 0xf2, 0xbd, 0xc1, 0x6c, 0x09, 0x71, 0x21, 0x32, 0xe1, - 0x78, 0x8c, 0x13, 0xf1, 0x11, 0x5c, 0x94, 0x04, 0x5b, 0x5e, 0x7c, 0x12, 0x33, 0x10, 0x2d, 0x79, - 0x07, 0xc6, 0x14, 0x1a, 0x71, 0xad, 0x1b, 0x4f, 0x3b, 0x1f, 0x7a, 0xd1, 0x9e, 0x1d, 0x72, 0xb8, - 0x7a, 0xda, 0xa9, 0xa0, 0x9b, 0x5f, 0x81, 0x67, 0xe2, 0xb8, 0x95, 0x8c, 0xaa, 0x53, 0xcc, 0x8d, - 0xd3, 0x31, 0x5f, 0x49, 0x3e, 0x6b, 0xa9, 0x1d, 0x47, 0xc0, 0x4b, 0xde, 0x44, 0xfd, 0x2c, 0xf1, - 0x31, 0x97, 0x94, 0xdc, 0x7c, 0x62, 0x2d, 0x4a, 0x00, 0xe6, 0xdb, 0x4a, 0x63, 0x33, 0x18, 0x6a, - 0xc4, 0x46, 0x9a, 0xf8, 0x1b, 0x39, 0x98, 0x5c, 0x5d, 0x5a, 0x98, 0x8f, 0xdd, 0xc8, 0x9f, 0xb1, - 0x04, 0x59, 0xda, 0xb7, 0xf5, 0xd7, 0x37, 0xe6, 0x06, 0x4c, 0xa7, 0xba, 0x01, 0x5f, 0x11, 0x7a, - 0x8f, 0xc7, 0x97, 0xc4, 0x60, 0xb9, 0xb2, 0x9c, 0xcb, 0x62, 0xbf, 0x79, 0xcb, 0x4a, 0x61, 0x9b, - 0xdf, 0x1d, 0x4e, 0xf1, 0x15, 0x2a, 0xec, 0x35, 0x18, 0x5d, 0x0a, 0xc3, 0x2e, 0x0d, 0x36, 0xac, - 0x65, 0xd5, 0x46, 0xf4, 0x10, 0x68, 0x77, 0x83, 0xa6, 0x95, 0x20, 0x90, 0x57, 0xa0, 0x28, 0x2e, - 0xe1, 0x49, 0x9d, 0x80, 0xc7, 0xe5, 0xf1, 0x1d, 0x3e, 0x2b, 0x2e, 0x26, 0x6f, 0x40, 0x89, 0xff, - 0xcf, 0xa5, 0x4d, 0x74, 0x38, 0xfa, 0xaa, 0x04, 0x3a, 0x97, 0x4e, 0x4b, 0x43, 0x63, 0x3b, 0x33, - 0xf9, 0x4c, 0x29, 0x6b, 0x51, 0x21, 0xd9, 0x99, 0xc9, 0x17, 0x4d, 0xb1, 0x4d, 0x2a, 0x12, 0xb9, - 0x06, 0xf9, 0xea, 0xbc, 0xa5, 0xa6, 0x03, 0x77, 0x1a, 0x01, 0x4f, 0xa7, 0xaf, 0xbd, 0x04, 0x56, - 0x9d, 0xb7, 0xc8, 0x1c, 0x14, 0xf1, 0xa5, 0x17, 0x97, 0x06, 0x22, 0xea, 0x15, 0xa5, 0xa6, 0x23, - 0x60, 0xea, 0xc9, 0xa3, 0xc4, 0x23, 0x37, 0x60, 0x64, 0xc1, 0x0b, 0x3b, 0x4d, 0xe7, 0x40, 0x64, - 0xc6, 0xc1, 0xc3, 0x10, 0x97, 0x83, 0x54, 0x39, 0x13, 0x58, 0xe4, 0x15, 0x18, 0xaa, 0x37, 0xfc, - 0x0e, 0xdb, 0x6d, 0xc5, 0xa1, 0x2d, 0x21, 0x03, 0x68, 0x79, 0x2f, 0x18, 0x00, 0xef, 0x85, 0xf3, - 0xeb, 0x6d, 0xa3, 0xca, 0xbd, 0xf0, 0xf4, 0xb5, 0x36, 0x81, 0xd3, 0x1b, 0x7c, 0x08, 0x4f, 0x32, - 0xf8, 0x70, 0x1b, 0xce, 0xdf, 0x41, 0x53, 0xbf, 0x4e, 0x03, 0x4c, 0x46, 0xca, 0x5f, 0x8d, 0xda, - 0xb0, 0x96, 0xc4, 0x95, 0x3e, 0xbc, 0x60, 0xc5, 0x77, 0x03, 0x76, 0xc8, 0x71, 0xe4, 0x83, 0x53, - 0xa9, 0xa7, 0x32, 0xfa, 0x31, 0x22, 0x5f, 0x82, 0x99, 0xac, 0x22, 0x71, 0xb9, 0x0f, 0xe3, 0xda, - 0xb3, 0x2b, 0x50, 0x03, 0xcb, 0xb3, 0x38, 0x90, 0x65, 0x28, 0x73, 0x78, 0xd5, 0x6d, 0x79, 0xed, - 0xc5, 0x96, 0xe3, 0x35, 0xf1, 0xaa, 0x9f, 0xb8, 0xaf, 0x29, 0xb8, 0x3a, 0xac, 0xd0, 0xa6, 0xac, - 0x54, 0x8b, 0x4e, 0x4a, 0x51, 0xa2, 0x3a, 0xaa, 0x57, 0xef, 0x2f, 0x27, 0x73, 0xea, 0xb3, 0x75, - 0x6e, 0xa4, 0x7d, 0xdb, 0x31, 0xe7, 0x46, 0x1b, 0x30, 0x9d, 0xea, 0x06, 0xa9, 0x8e, 0x34, 0x70, - 0x5a, 0x1d, 0xa5, 0x68, 0xac, 0x14, 0xb6, 0xf9, 0x5f, 0x87, 0x53, 0x7c, 0x85, 0xaf, 0xc8, 0x84, - 0x61, 0xae, 0x6d, 0xd4, 0xd4, 0x79, 0x5c, 0x17, 0x59, 0xa2, 0x84, 0x5c, 0x80, 0x7c, 0xbd, 0xbe, - 0xaa, 0x26, 0xf6, 0x0c, 0x43, 0xdf, 0x62, 0x30, 0x36, 0x42, 0xe8, 0x06, 0x52, 0xae, 0x98, 0x35, - 0x68, 0x10, 0x89, 0x77, 0x6c, 0x5f, 0x4a, 0xe6, 0x71, 0x21, 0xe9, 0x6f, 0x31, 0x8f, 0x93, 0xd9, - 0x3b, 0x0f, 0xb3, 0xd5, 0x30, 0xa4, 0x41, 0xc4, 0x5f, 0x23, 0x08, 0xbb, 0x2d, 0x1a, 0x08, 0x59, - 0x13, 0x3a, 0x86, 0xbf, 0x82, 0xdf, 0x08, 0xad, 0xbe, 0x88, 0xe4, 0x2a, 0x14, 0xab, 0x5d, 0xd7, - 0xa3, 0xed, 0x86, 0x16, 0x5d, 0xef, 0x08, 0x98, 0x15, 0x97, 0x92, 0x8f, 0xe0, 0xac, 0x20, 0x92, - 0x0a, 0x47, 0xf4, 0x00, 0xd7, 0x35, 0x7c, 0x07, 0x2b, 0xe6, 0x82, 0x54, 0x53, 0xb6, 0xe8, 0x92, - 0x6c, 0x4a, 0x52, 0x85, 0xf2, 0x22, 0x9e, 0x93, 0xca, 0xd7, 0xac, 0xfd, 0x40, 0x64, 0x9d, 0x46, - 0xcd, 0xc5, 0xcf, 0x50, 0x6d, 0x37, 0x2e, 0xb4, 0x7a, 0xd0, 0xc9, 0x3d, 0x98, 0x4e, 0xc3, 0x98, - 0x3e, 0x1e, 0x4d, 0x5e, 0x9b, 0xeb, 0xe1, 0x82, 0x8a, 0x39, 0x8b, 0x8a, 0x6c, 0xc3, 0x54, 0x35, - 0x8a, 0x02, 0x6f, 0xbb, 0x1b, 0xd1, 0x94, 0xea, 0x92, 0x8e, 0xc6, 0xb8, 0x5c, 0xaa, 0xaf, 0x67, - 0x84, 0x30, 0x4e, 0x3b, 0x31, 0x65, 0xac, 0xc2, 0xac, 0x5e, 0x76, 0xc4, 0x8d, 0x1f, 0xac, 0x14, - 0x8f, 0x3a, 0x8a, 0x2b, 0x51, 0xd2, 0xa1, 0x5b, 0x0d, 0x0f, 0x5a, 0x2d, 0x1a, 0x05, 0x78, 0x72, - 0x8f, 0x8f, 0x3e, 0x9a, 0x22, 0x06, 0xe8, 0xa2, 0xf2, 0x4e, 0x2b, 0x3e, 0xec, 0xa9, 0x85, 0x47, - 0x6a, 0x3c, 0xb5, 0xe5, 0xa3, 0x34, 0xe0, 0xf2, 0xd1, 0x84, 0xa9, 0xc5, 0x76, 0x23, 0x38, 0xc0, - 0xbb, 0x99, 0xb2, 0x71, 0xe3, 0x27, 0x34, 0x4e, 0xbe, 0xe8, 0x72, 0xc9, 0x91, 0x12, 0x96, 0xd5, - 0xbc, 0x5e, 0xc6, 0xe6, 0xdf, 0x82, 0x72, 0xba, 0x2f, 0x1f, 0xf3, 0x95, 0xee, 0xd3, 0x84, 0x66, - 0xb3, 0x91, 0x4e, 0x7f, 0x0b, 0xb9, 0xa1, 0x3d, 0xc5, 0x6c, 0x24, 0xa9, 0x03, 0x94, 0x47, 0x93, - 0xb5, 0x07, 0x98, 0xe5, 0x34, 0xce, 0x65, 0x4d, 0x63, 0xf3, 0x17, 0x73, 0x30, 0xc5, 0xa3, 0x49, - 0x9f, 0x7e, 0x5b, 0xf1, 0x3d, 0x4d, 0x39, 0x4b, 0x5f, 0x60, 0xea, 0xeb, 0x8e, 0xb1, 0x16, 0xbf, - 0x06, 0x67, 0x7b, 0xba, 0x02, 0x15, 0xf4, 0x82, 0x8c, 0xe3, 0xed, 0x51, 0xd1, 0xb3, 0xd9, 0x95, - 0x6c, 0xde, 0xb2, 0x7a, 0x28, 0xcc, 0x7f, 0x92, 0xeb, 0xe1, 0x2f, 0xec, 0x46, 0xd5, 0x12, 0x34, - 0x4e, 0x67, 0x09, 0xe6, 0x3e, 0x91, 0x25, 0x98, 0x1f, 0xc4, 0x12, 0xfc, 0x08, 0xc6, 0xd7, 0xa9, - 0xc3, 0x2c, 0x1a, 0x71, 0x5d, 0xae, 0xa0, 0x3d, 0x93, 0xcc, 0xca, 0xa4, 0x7e, 0x89, 0xaf, 0xda, - 0x46, 0x8c, 0x80, 0xa9, 0x16, 0x7e, 0x7f, 0xce, 0xd2, 0x39, 0xa8, 0x8b, 0xc6, 0x50, 0xff, 0x45, - 0xc3, 0x0c, 0x60, 0xac, 0x5e, 0x5f, 0xdd, 0x72, 0x02, 0xa6, 0x2d, 0x42, 0x66, 0x32, 0xca, 0x30, - 0x51, 0x23, 0x51, 0xbc, 0xbd, 0xf1, 0xa1, 0x12, 0x8b, 0x29, 0x16, 0x49, 0x2c, 0x42, 0x2a, 0x78, - 0x44, 0x9c, 0x80, 0x69, 0x11, 0x71, 0x02, 0x66, 0xfe, 0xe3, 0x02, 0x94, 0x79, 0xe8, 0x23, 0xdb, - 0xf7, 0x8a, 0xfc, 0x40, 0x3d, 0xef, 0x44, 0x18, 0xa7, 0x7f, 0x27, 0xe2, 0x13, 0xc4, 0xd8, 0x2a, - 0x77, 0xb1, 0xf3, 0x03, 0xdc, 0xc5, 0x7e, 0x4b, 0xbb, 0xc8, 0x5c, 0x48, 0x1e, 0x22, 0xdd, 0xef, - 0x6e, 0xd3, 0xe3, 0xaf, 0x30, 0xbf, 0xa9, 0xde, 0x38, 0x1e, 0x4a, 0xa2, 0x4f, 0x90, 0xf2, 0x98, - 0xbb, 0xc6, 0xb1, 0x16, 0x1b, 0x3e, 0x4d, 0xbc, 0xf9, 0xc8, 0xff, 0xd7, 0x78, 0xf3, 0x45, 0x00, - 0x25, 0x69, 0x4c, 0x31, 0x79, 0x8f, 0xf4, 0xe4, 0x84, 0x31, 0x0a, 0xa1, 0xf9, 0x9b, 0x13, 0x30, - 0x55, 0xaf, 0xaf, 0x2e, 0x78, 0xce, 0x6e, 0xdb, 0x0f, 0x23, 0xaf, 0xb1, 0xd4, 0xde, 0xf1, 0xd9, - 0x14, 0x5e, 0xa7, 0x61, 0x74, 0xbb, 0xe9, 0x3f, 0x54, 0x63, 0x9f, 0x23, 0x1a, 0x46, 0xf6, 0x4e, - 0xd3, 0x7f, 0x68, 0xc5, 0xc5, 0x6c, 0x89, 0x58, 0x0c, 0x82, 0xf8, 0xe9, 0x13, 0x5c, 0x22, 0x28, - 0x03, 0x58, 0x1c, 0xce, 0x66, 0x49, 0xbd, 0xcb, 0xb3, 0x7f, 0xf0, 0x4c, 0x73, 0x38, 0x4b, 0x42, - 0x0e, 0xb2, 0x64, 0x19, 0xa1, 0xbd, 0x02, 0x2b, 0xb4, 0xe6, 0x79, 0x2d, 0x6a, 0x3d, 0x29, 0x16, - 0x6f, 0xe8, 0x21, 0x14, 0x47, 0xd7, 0xee, 0x20, 0x5c, 0x35, 0xde, 0x7b, 0xe6, 0xc0, 0x01, 0x9c, - 0x65, 0xc6, 0xe5, 0xa9, 0x6d, 0x8e, 0x6b, 0x42, 0x27, 0x98, 0x78, 0x5f, 0x22, 0xc3, 0xf0, 0x50, - 0x9f, 0x47, 0xc8, 0xac, 0x81, 0xfc, 0xa2, 0x01, 0xcf, 0x66, 0x96, 0xc4, 0xb3, 0x7b, 0x4c, 0xbb, - 0x39, 0xa0, 0x28, 0x0d, 0xcc, 0x98, 0xf2, 0x6a, 0xbf, 0xaa, 0xed, 0x0c, 0x55, 0x70, 0x7c, 0x4d, - 0xe4, 0xdf, 0x1a, 0x70, 0x5e, 0xc3, 0xc0, 0x54, 0x8b, 0x2d, 0xda, 0x8e, 0xe4, 0xc3, 0xe1, 0x7d, - 0xe4, 0xfa, 0xe3, 0x27, 0x23, 0xd7, 0x2f, 0xe8, 0xdf, 0xc2, 0xd3, 0x51, 0x63, 0xf5, 0xea, 0x4e, - 0xb1, 0x4f, 0x0b, 0xc9, 0x4f, 0xc2, 0x14, 0x16, 0x49, 0xfb, 0x87, 0xc9, 0x2c, 0x9a, 0x4d, 0xa5, - 0xda, 0xdc, 0xf7, 0x0f, 0x2b, 0xe3, 0x5a, 0x01, 0x5e, 0x2b, 0xc4, 0xda, 0x62, 0x73, 0xc9, 0x6b, - 0xef, 0xf8, 0x5a, 0x6a, 0xd5, 0x34, 0x33, 0xf2, 0xef, 0x0d, 0x98, 0x65, 0x50, 0xde, 0xe0, 0xdb, - 0x81, 0xdf, 0x8a, 0xcb, 0x43, 0x71, 0xe3, 0xbc, 0x4f, 0x07, 0x35, 0x9f, 0x4c, 0x07, 0xbd, 0x84, - 0x4d, 0xe6, 0xb3, 0xdf, 0xde, 0x09, 0xfc, 0x56, 0xd2, 0x7c, 0x2d, 0x5b, 0x49, 0xbf, 0x46, 0x92, - 0x9f, 0x37, 0xe0, 0x82, 0xb6, 0x8d, 0x52, 0x2f, 0xe5, 0xcd, 0x4e, 0x6a, 0xce, 0x01, 0xb5, 0xa8, - 0x76, 0x5d, 0x48, 0xfa, 0x15, 0x6c, 0x41, 0xb2, 0x2e, 0x60, 0x5b, 0xec, 0x16, 0xc7, 0x52, 0x9a, - 0xd0, 0xbf, 0x16, 0xe2, 0xc1, 0x14, 0x3a, 0x96, 0x34, 0xbf, 0xc4, 0x4c, 0x7f, 0xbf, 0xc4, 0x15, - 0x51, 0xf5, 0x73, 0x78, 0xf1, 0xa9, 0xbf, 0x73, 0xa2, 0x97, 0x2b, 0xf9, 0x19, 0xb8, 0xd0, 0x03, - 0x8c, 0xe7, 0xd5, 0xd9, 0xbe, 0xf3, 0xea, 0xd5, 0xa3, 0xc3, 0xca, 0xcb, 0x59, 0xb5, 0x65, 0xcd, - 0xa9, 0xfe, 0x35, 0x90, 0x7b, 0x00, 0x49, 0xe1, 0xec, 0x39, 0x14, 0xc5, 0x57, 0x85, 0x24, 0x28, - 0x25, 0x4c, 0x3f, 0x2b, 0xb5, 0xa9, 0xcb, 0x58, 0x82, 0x44, 0x56, 0xa1, 0xa4, 0x5c, 0xef, 0x3a, - 0x98, 0x3d, 0xcf, 0xd9, 0x7d, 0xff, 0xb0, 0xa2, 0xc1, 0xd9, 0xee, 0x4f, 0xbd, 0x21, 0xa6, 0xfa, - 0x9b, 0x34, 0x44, 0xf2, 0x87, 0x06, 0xcc, 0x30, 0x40, 0x22, 0x28, 0xa2, 0xa1, 0xb3, 0xc7, 0x49, - 0xf2, 0xde, 0x93, 0x91, 0xe4, 0xe7, 0xb1, 0x8d, 0xaa, 0x24, 0xf7, 0x7c, 0x7c, 0x66, 0xe3, 0x50, - 0x82, 0x35, 0xbf, 0xa4, 0x26, 0xc1, 0x17, 0x06, 0x90, 0x60, 0xde, 0xd5, 0x27, 0x4b, 0x70, 0xdf, - 0x5a, 0xcc, 0x6f, 0xe6, 0x60, 0x4c, 0x31, 0x0d, 0xc9, 0xe7, 0xa1, 0xb4, 0x1a, 0xec, 0x3a, 0x6d, - 0xef, 0xa7, 0x1c, 0xe5, 0xe8, 0x1c, 0x4d, 0x4f, 0x5f, 0x81, 0x5b, 0x1a, 0x16, 0x86, 0x3c, 0x53, - 0xa7, 0xa5, 0x6e, 0x5a, 0x98, 0x69, 0x69, 0x21, 0xf4, 0x94, 0xe6, 0xd1, 0xfb, 0x19, 0xe6, 0xd1, - 0xa9, 0xf2, 0xbc, 0xbc, 0xd3, 0x6b, 0x24, 0x0d, 0x9e, 0x96, 0xc5, 0xfc, 0xe5, 0x1c, 0x94, 0xd7, - 0x83, 0x6e, 0x18, 0x51, 0x57, 0x1e, 0xfc, 0x7e, 0xb6, 0x1e, 0x8f, 0xd3, 0x3f, 0xee, 0x98, 0xd0, - 0xa6, 0xc2, 0xaf, 0xff, 0x76, 0x05, 0x9f, 0xf6, 0x4f, 0x77, 0x87, 0x7c, 0xda, 0x5f, 0x87, 0xa7, - 0x6f, 0x7d, 0xa6, 0xa9, 0xac, 0x34, 0xbe, 0xf9, 0xa7, 0xb9, 0x34, 0x6f, 0xe1, 0x09, 0x7b, 0x09, - 0x46, 0xf8, 0x5b, 0xbe, 0xf2, 0x62, 0x9a, 0x48, 0x8e, 0x8a, 0x20, 0x4b, 0x96, 0x9d, 0xe6, 0xfe, - 0x6f, 0x7c, 0x2e, 0x96, 0xcf, 0x3e, 0x17, 0x23, 0x6f, 0x42, 0x09, 0x63, 0x7d, 0xab, 0xae, 0x1b, - 0x30, 0x4b, 0xae, 0x90, 0xe4, 0x41, 0x7d, 0x48, 0xb7, 0x6d, 0x1e, 0x13, 0xec, 0xb8, 0x6e, 0x60, - 0x69, 0x78, 0x64, 0x1e, 0x66, 0xb4, 0xd0, 0x72, 0x49, 0x3f, 0x94, 0xec, 0xf4, 0x23, 0x2c, 0xe0, - 0xc4, 0x99, 0xc8, 0x98, 0x0a, 0xdc, 0x6f, 0x32, 0x53, 0x0c, 0x0d, 0x74, 0x3d, 0x87, 0xa4, 0x9c, - 0xf4, 0x32, 0xde, 0x84, 0x60, 0xfe, 0x94, 0x96, 0xd3, 0xd1, 0x52, 0x12, 0x71, 0x44, 0xf3, 0x2f, - 0x0d, 0x36, 0xd7, 0x1a, 0xfb, 0x9f, 0xb1, 0x9b, 0xc9, 0xec, 0x93, 0x8e, 0x71, 0xd4, 0xfe, 0x67, - 0x83, 0xdf, 0x2d, 0x14, 0xe2, 0xf3, 0x16, 0x0c, 0xf3, 0x97, 0x83, 0xc5, 0x2d, 0x38, 0x95, 0x0b, - 0x2f, 0x48, 0x62, 0x8b, 0xf8, 0xfb, 0xc3, 0x96, 0x20, 0x50, 0xf7, 0xae, 0xb9, 0x81, 0xf6, 0xae, - 0xca, 0xc3, 0x06, 0x83, 0x3d, 0x98, 0x63, 0x9c, 0xfc, 0xb0, 0x81, 0xf9, 0x7f, 0x73, 0xfc, 0x7b, - 0x44, 0xa3, 0x06, 0x4d, 0xa5, 0x7d, 0x05, 0x0a, 0x4c, 0x0e, 0xd4, 0x7c, 0xe5, 0x4c, 0x56, 0xb4, - 0x37, 0xd2, 0xfc, 0x26, 0xfa, 0xb4, 0x50, 0xd7, 0xaa, 0x97, 0xe1, 0x51, 0x1d, 0xab, 0xf3, 0x06, - 0x31, 0xf0, 0x39, 0x1a, 0xdf, 0xa5, 0xea, 0x74, 0x68, 0xeb, 0x2f, 0x07, 0x61, 0x39, 0xdb, 0x98, - 0xc6, 0x77, 0xd2, 0xd4, 0xc3, 0xa8, 0xd6, 0x8e, 0x63, 0xf3, 0xbb, 0x50, 0xaa, 0xb6, 0x4d, 0xae, - 0xaf, 0x2d, 0xc2, 0x84, 0x9e, 0x29, 0x47, 0x38, 0x8c, 0x31, 0xbb, 0x45, 0x2a, 0xcb, 0x8e, 0xea, - 0x9a, 0xd4, 0x89, 0xd8, 0xc6, 0x5f, 0x4b, 0x87, 0xa2, 0xbe, 0xe2, 0xc0, 0xd3, 0x2f, 0xda, 0xbd, - 0x79, 0xbc, 0x74, 0x12, 0x25, 0xd8, 0xe1, 0x73, 0x50, 0x16, 0x33, 0x33, 0xce, 0x4b, 0x80, 0x6e, - 0xb9, 0xa5, 0x05, 0x4b, 0x9d, 0x4d, 0x0d, 0xcf, 0x0d, 0x2c, 0x84, 0x9a, 0xdf, 0x31, 0xe0, 0x82, - 0x78, 0x11, 0xd9, 0xa2, 0x61, 0x14, 0x78, 0x3c, 0xcd, 0x01, 0xca, 0xe3, 0xe7, 0xc9, 0x3b, 0x32, - 0xb3, 0xab, 0xae, 0x20, 0xd3, 0x75, 0xd4, 0xc6, 0x85, 0x50, 0x0e, 0x61, 0x6e, 0x57, 0x99, 0xd1, - 0xf5, 0x2d, 0x91, 0xd1, 0x35, 0x77, 0x3c, 0x71, 0x3c, 0x2f, 0x5c, 0xda, 0x96, 0x99, 0x5c, 0xbf, - 0x9d, 0x83, 0xb3, 0x19, 0xcd, 0xda, 0xfc, 0xfc, 0x53, 0xaa, 0x1c, 0x6a, 0x9a, 0x72, 0x90, 0x29, - 0xbf, 0xfb, 0x76, 0x7c, 0xa6, 0xae, 0xf8, 0x4d, 0x03, 0xce, 0xeb, 0xd2, 0x23, 0xce, 0x11, 0x36, - 0x6f, 0x91, 0xb7, 0x61, 0xf8, 0x2e, 0x75, 0x5c, 0x2a, 0xaf, 0xcf, 0x9e, 0x4d, 0xbd, 0x73, 0xc0, - 0x0b, 0x39, 0xdb, 0x3f, 0xe5, 0x53, 0xf9, 0x8c, 0x25, 0x48, 0xc8, 0x82, 0x68, 0x1c, 0x0f, 0x2d, - 0x32, 0x65, 0x94, 0x4d, 0x56, 0x55, 0xc7, 0x38, 0x35, 0x7f, 0xd7, 0x80, 0x67, 0x8e, 0xa1, 0x61, - 0x03, 0xc7, 0x86, 0x5e, 0x1d, 0x38, 0x5c, 0x58, 0x10, 0x4a, 0xde, 0x83, 0xc9, 0x75, 0x61, 0xb2, - 0xca, 0xe1, 0x50, 0x9e, 0x87, 0x92, 0xd6, 0xac, 0x2d, 0xc7, 0x25, 0x8d, 0x4c, 0xae, 0x42, 0xf1, - 0xae, 0x1f, 0x46, 0xed, 0x24, 0x55, 0x21, 0x9e, 0xdc, 0xec, 0x09, 0x98, 0x15, 0x97, 0x32, 0xb3, - 0x40, 0x6f, 0xa6, 0x08, 0x65, 0x7d, 0x01, 0x86, 0x19, 0x4e, 0xec, 0x19, 0x45, 0x39, 0xc0, 0xf7, - 0x7a, 0x3d, 0xd7, 0x12, 0x45, 0xb1, 0x4f, 0x3e, 0x97, 0x19, 0x71, 0xf2, 0x4d, 0x03, 0xca, 0x3a, - 0xef, 0xc7, 0x1d, 0x9a, 0x77, 0xb5, 0xa1, 0x79, 0x26, 0x7b, 0x68, 0xfa, 0x8f, 0x49, 0x4f, 0xd6, - 0xb0, 0x81, 0xc6, 0xc2, 0x84, 0xe1, 0x05, 0xbf, 0xe5, 0x78, 0x6d, 0x35, 0xd3, 0x95, 0x8b, 0x10, - 0x4b, 0x94, 0x28, 0xbd, 0x95, 0xef, 0xdb, 0x5b, 0xe6, 0xaf, 0x14, 0xe0, 0x82, 0x45, 0x77, 0x3d, - 0x66, 0x20, 0x6d, 0x84, 0x5e, 0x7b, 0x57, 0x8b, 0x07, 0x32, 0x53, 0x1d, 0x2e, 0x6e, 0x41, 0x30, - 0x48, 0xdc, 0xdf, 0xaf, 0x40, 0x91, 0x69, 0x69, 0xa5, 0xcf, 0xd1, 0xdb, 0x85, 0xf9, 0x1a, 0xf9, - 0xb8, 0xca, 0x62, 0x72, 0x4d, 0xac, 0x21, 0xca, 0x3d, 0x35, 0xb6, 0x86, 0xfc, 0xe8, 0xb0, 0x02, - 0xfc, 0x7d, 0x16, 0x56, 0x2a, 0xd6, 0x91, 0xd8, 0xa8, 0x2a, 0xf4, 0x31, 0xaa, 0xee, 0xc3, 0x4c, - 0xd5, 0xe5, 0xfa, 0xc9, 0x69, 0xae, 0x05, 0x5e, 0xbb, 0xe1, 0x75, 0x9c, 0xa6, 0x34, 0xca, 0xd1, - 0xe7, 0xe9, 0xc4, 0xe5, 0x76, 0x27, 0x46, 0xb0, 0x32, 0xc9, 0xd8, 0x67, 0x2c, 0xac, 0xd4, 0x79, - 0x3a, 0x3e, 0xee, 0xc8, 0xc4, 0xcf, 0x70, 0xdb, 0x21, 0xcf, 0xc7, 0x67, 0xc5, 0xc5, 0x68, 0xce, - 0xe1, 0x55, 0xd5, 0xf5, 0xe5, 0xfa, 0x3d, 0x71, 0xf5, 0x53, 0x86, 0xd1, 0xf3, 0x9b, 0xad, 0x51, - 0x33, 0xc4, 0xb3, 0x17, 0x0d, 0x2f, 0xa1, 0xab, 0xd7, 0xef, 0x32, 0xba, 0x62, 0x0f, 0x5d, 0x18, - 0xee, 0xa9, 0x74, 0x1c, 0x8f, 0xdc, 0x00, 0xe0, 0x81, 0xc8, 0x28, 0x10, 0xa3, 0x89, 0xf1, 0x17, - 0x20, 0x94, 0x1b, 0x7f, 0x0a, 0x0a, 0x79, 0x07, 0xa6, 0x17, 0xe7, 0xe7, 0xe4, 0x06, 0x76, 0xc1, - 0x6f, 0x74, 0x5b, 0xb4, 0x1d, 0xe1, 0x1d, 0xe3, 0x12, 0x1f, 0x43, 0xda, 0x98, 0x63, 0x52, 0x90, - 0x85, 0x26, 0x6e, 0x76, 0xf2, 0xbc, 0x00, 0xf3, 0xbe, 0x4b, 0xc3, 0xcd, 0x9b, 0x9f, 0xb1, 0x9b, - 0x9d, 0xca, 0xb7, 0xe1, 0x6c, 0xbb, 0x99, 0x39, 0x33, 0xff, 0x3e, 0xde, 0xec, 0xec, 0xc1, 0x25, - 0x3f, 0x06, 0x43, 0xf8, 0x53, 0xac, 0xb8, 0xd3, 0x19, 0x6c, 0x93, 0xd5, 0xb6, 0xc1, 0x33, 0xab, - 0x21, 0x01, 0x59, 0x4a, 0x5e, 0xbf, 0x3a, 0xc5, 0xfd, 0x24, 0x91, 0x5c, 0x44, 0x7f, 0xf6, 0xd0, - 0x85, 0x92, 0x5a, 0x21, 0x93, 0x91, 0xbb, 0x4e, 0xb8, 0x47, 0xdd, 0x79, 0xf9, 0x58, 0x7a, 0x89, - 0xcb, 0xc8, 0x1e, 0x42, 0xf1, 0x49, 0x46, 0x4b, 0x41, 0x61, 0xda, 0x61, 0x29, 0xdc, 0x08, 0x45, - 0x53, 0xc4, 0x2e, 0xc8, 0xc3, 0xdd, 0xab, 0x6b, 0x89, 0x22, 0xd4, 0x96, 0xd2, 0xdd, 0x1d, 0x38, - 0x8d, 0x7d, 0x1a, 0x6c, 0xde, 0xfc, 0x34, 0xb4, 0xa5, 0x5e, 0xc7, 0x31, 0x63, 0xf2, 0x0d, 0x88, - 0x13, 0x03, 0x6a, 0xc8, 0xcc, 0x46, 0x4c, 0xa2, 0x2a, 0x8d, 0xc4, 0x46, 0x4c, 0xa2, 0x2a, 0x55, - 0x1b, 0x31, 0x46, 0x8d, 0x9f, 0xa6, 0xc8, 0x9d, 0xf0, 0x34, 0x45, 0x9f, 0x67, 0x78, 0xe4, 0x85, - 0x9c, 0xcf, 0xd0, 0xc3, 0x67, 0x5f, 0x84, 0x52, 0x35, 0x8a, 0x9c, 0xc6, 0x1e, 0x75, 0xf1, 0x09, - 0x14, 0x25, 0x98, 0xcb, 0x11, 0x70, 0xd5, 0x55, 0xa6, 0xe2, 0x2a, 0x0f, 0x1f, 0x8e, 0x0c, 0xf0, - 0xf0, 0xe1, 0x0d, 0x18, 0x59, 0x6a, 0x3f, 0xf0, 0x58, 0x9f, 0x14, 0x93, 0xc4, 0x5e, 0x1e, 0x07, - 0xe9, 0xaf, 0xe5, 0x21, 0x88, 0xbc, 0xa5, 0x58, 0x10, 0xa3, 0x89, 0x29, 0xcf, 0xb7, 0x59, 0xb6, - 0x34, 0x24, 0xd4, 0x53, 0x34, 0x89, 0x4e, 0xde, 0x84, 0x11, 0xb9, 0x7b, 0x86, 0xc4, 0x7c, 0x17, - 0x94, 0x0e, 0x2f, 0xd1, 0x72, 0x89, 0x89, 0xdd, 0xf3, 0x3b, 0xfa, 0xdd, 0xdf, 0x31, 0x25, 0xa7, - 0x8e, 0x72, 0xf7, 0x57, 0xcb, 0xa9, 0xa3, 0xdc, 0x02, 0x8e, 0x37, 0x43, 0xa5, 0x13, 0x37, 0x43, - 0x9b, 0x50, 0x5a, 0x73, 0x82, 0xc8, 0x63, 0xcb, 0x51, 0x3b, 0xe2, 0x49, 0x5d, 0x93, 0xbd, 0xba, - 0x52, 0x94, 0xbc, 0x86, 0xd5, 0x51, 0xf0, 0xf5, 0xa4, 0x24, 0x09, 0x9c, 0xac, 0x64, 0xdc, 0x0e, - 0x11, 0x59, 0xd6, 0xf1, 0x04, 0x48, 0x4d, 0xe9, 0xcd, 0x4b, 0x55, 0x57, 0x71, 0xef, 0xc5, 0x92, - 0x5b, 0x7c, 0x0c, 0x70, 0xcf, 0x38, 0x89, 0x6c, 0x30, 0x33, 0x1c, 0xda, 0x15, 0xa9, 0x8d, 0x63, - 0x8c, 0x48, 0xbe, 0x0a, 0x25, 0xf6, 0x3f, 0x66, 0xb8, 0xf4, 0x28, 0x4f, 0xda, 0x9a, 0xdc, 0x16, - 0xd0, 0x27, 0x34, 0x4f, 0x83, 0x59, 0xa7, 0x11, 0x9f, 0xc0, 0xc8, 0x38, 0xed, 0x78, 0xd1, 0xb8, - 0x91, 0xf7, 0xa1, 0xa4, 0x66, 0xc8, 0x9d, 0x9d, 0x4a, 0xe2, 0x7b, 0x5c, 0x01, 0x4f, 0x8f, 0x92, - 0x46, 0xc0, 0xd6, 0xaf, 0x6a, 0xa7, 0x83, 0xb4, 0x44, 0x91, 0xf6, 0x4e, 0x27, 0x4d, 0x26, 0xd1, - 0xc8, 0x07, 0x50, 0xaa, 0x76, 0x3a, 0x89, 0xc6, 0x99, 0x56, 0xb6, 0x84, 0x9d, 0x8e, 0x9d, 0xa9, - 0x75, 0x34, 0x0a, 0x26, 0x58, 0xc2, 0xe0, 0xc3, 0x7a, 0x67, 0x12, 0xc1, 0x92, 0x79, 0x5f, 0xd3, - 0x82, 0xa5, 0xa0, 0x9b, 0x3f, 0x34, 0xe0, 0x7c, 0x9f, 0x6e, 0x1b, 0xf8, 0x95, 0xd7, 0x1b, 0xc9, - 0x1a, 0xac, 0xb8, 0x23, 0xc4, 0x1a, 0xac, 0x7e, 0xb4, 0x5c, 0x8d, 0xb3, 0x33, 0xcc, 0xe6, 0x3f, - 0xb5, 0x0c, 0xb3, 0xe6, 0xa1, 0x01, 0x63, 0x8a, 0x30, 0x3f, 0xc1, 0x57, 0xd5, 0xae, 0x88, 0x54, - 0xeb, 0xf9, 0x04, 0xaf, 0x95, 0x72, 0x3d, 0x60, 0x6a, 0xf5, 0xaf, 0x01, 0x2c, 0x3b, 0x61, 0x54, - 0x6d, 0x44, 0xde, 0x03, 0x3a, 0x80, 0xe6, 0x4e, 0xf2, 0x4a, 0x39, 0xf8, 0x38, 0x03, 0x23, 0xeb, - 0xc9, 0x2b, 0x15, 0x33, 0x34, 0x57, 0x60, 0xb8, 0xee, 0x07, 0x51, 0xed, 0x80, 0x2f, 0xc7, 0x0b, - 0x34, 0x6c, 0xa8, 0x4e, 0x49, 0x0f, 0xdd, 0x13, 0x0d, 0x4b, 0x14, 0x31, 0x9b, 0xf8, 0xb6, 0x47, - 0x9b, 0xae, 0x7a, 0x5a, 0xbc, 0xc3, 0x00, 0x16, 0x87, 0x5f, 0x7b, 0x1f, 0x26, 0xa5, 0x60, 0xaf, - 0x2f, 0xd7, 0xf1, 0x0b, 0x26, 0x61, 0x6c, 0x73, 0xd1, 0x5a, 0xba, 0xfd, 0x65, 0xfb, 0xf6, 0xc6, - 0xf2, 0x72, 0xf9, 0x0c, 0x19, 0x87, 0x51, 0x01, 0x98, 0xaf, 0x96, 0x0d, 0x52, 0x82, 0xe2, 0xd2, - 0x4a, 0x7d, 0x71, 0x7e, 0xc3, 0x5a, 0x2c, 0xe7, 0xae, 0xbd, 0x04, 0x13, 0x49, 0xb8, 0x10, 0xa6, - 0x14, 0x19, 0x81, 0xbc, 0x55, 0xdd, 0x2a, 0x9f, 0x21, 0x00, 0xc3, 0x6b, 0xf7, 0xe6, 0xeb, 0x37, - 0x6f, 0x96, 0x8d, 0x6b, 0x9f, 0x83, 0x29, 0x74, 0x54, 0x2e, 0xb3, 0x7d, 0x43, 0x9b, 0x06, 0x58, - 0x53, 0x09, 0x8a, 0x75, 0xda, 0x71, 0x02, 0x27, 0xa2, 0xbc, 0x9a, 0xfb, 0xdd, 0x66, 0xe4, 0x75, - 0x9a, 0xf4, 0x51, 0xd9, 0xb8, 0xf6, 0x16, 0x4c, 0x5a, 0x7e, 0x37, 0xf2, 0xda, 0xbb, 0xf2, 0x59, - 0x0a, 0x72, 0x16, 0xa6, 0x36, 0x56, 0xaa, 0xf7, 0x6b, 0x4b, 0x77, 0x36, 0x56, 0x37, 0xea, 0xf6, - 0xfd, 0xea, 0xfa, 0xfc, 0xdd, 0xf2, 0x19, 0xd6, 0xe0, 0xfb, 0xab, 0xf5, 0x75, 0xdb, 0x5a, 0x9c, - 0x5f, 0x5c, 0x59, 0x2f, 0x1b, 0xd7, 0x7e, 0xc9, 0x80, 0x09, 0xfd, 0x05, 0x71, 0x72, 0x19, 0x2e, - 0x6d, 0xd4, 0x17, 0x2d, 0x7b, 0x7d, 0xf5, 0xde, 0xe2, 0x8a, 0xbd, 0x51, 0xaf, 0xde, 0x59, 0xb4, - 0x37, 0x56, 0xea, 0x6b, 0x8b, 0xf3, 0x4b, 0xb7, 0x97, 0x16, 0x17, 0xca, 0x67, 0x48, 0x05, 0x9e, - 0x51, 0x30, 0xac, 0xc5, 0xf9, 0xd5, 0xcd, 0x45, 0xcb, 0x5e, 0xab, 0xd6, 0xeb, 0x5b, 0xab, 0xd6, - 0x42, 0xd9, 0x20, 0x17, 0xe1, 0x5c, 0x06, 0xc2, 0xfd, 0xdb, 0xd5, 0x72, 0xae, 0xa7, 0x6c, 0x65, - 0x71, 0xab, 0xba, 0x6c, 0xd7, 0x56, 0xd7, 0xcb, 0xf9, 0x6b, 0xef, 0x33, 0xc3, 0x2b, 0x79, 0x7b, - 0x8f, 0x14, 0xa1, 0xb0, 0xb2, 0xba, 0xb2, 0x58, 0x3e, 0x43, 0xc6, 0x60, 0x64, 0x6d, 0x71, 0x65, - 0x61, 0x69, 0xe5, 0x0e, 0xef, 0xd6, 0xea, 0xda, 0x9a, 0xb5, 0xba, 0xb9, 0xb8, 0x50, 0xce, 0xb1, - 0xbe, 0x5b, 0x58, 0x5c, 0x61, 0x2d, 0xcb, 0x5f, 0x33, 0xf9, 0xb3, 0x2f, 0x5a, 0x4a, 0x7f, 0xd6, - 0x5b, 0x8b, 0x5f, 0x5a, 0x5f, 0x5c, 0xa9, 0x2f, 0xad, 0xae, 0x94, 0xcf, 0x5c, 0xbb, 0x94, 0xc2, - 0x91, 0x23, 0x51, 0xaf, 0xdf, 0x2d, 0x9f, 0xb9, 0xf6, 0x55, 0x28, 0xa9, 0x76, 0x07, 0x39, 0x0f, - 0xd3, 0xea, 0xef, 0x35, 0xda, 0x76, 0xbd, 0xf6, 0x6e, 0xf9, 0x4c, 0xba, 0xc0, 0xea, 0xb6, 0xdb, - 0xac, 0x00, 0x3f, 0x5e, 0x2d, 0x58, 0xa7, 0x41, 0xcb, 0x6b, 0x33, 0x93, 0xa2, 0x9c, 0xab, 0x95, - 0xbf, 0xf7, 0xe7, 0xcf, 0x9d, 0xf9, 0xde, 0x0f, 0x9e, 0x33, 0xfe, 0xf4, 0x07, 0xcf, 0x19, 0xff, - 0xf3, 0x07, 0xcf, 0x19, 0xdb, 0xc3, 0x28, 0xe8, 0xb7, 0xfe, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, - 0x8c, 0x17, 0x2b, 0x03, 0x96, 0xc5, 0x00, 0x00, + // 12841 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0xbd, 0x7f, 0x6c, 0x1c, 0x49, + 0x76, 0x18, 0xac, 0x9e, 0x19, 0x92, 0xc3, 0xc7, 0x21, 0x39, 0x2c, 0x52, 0x12, 0xa5, 0xd5, 0xee, + 0x68, 0x7b, 0x77, 0xb5, 0x5a, 0xed, 0xae, 0x74, 0xa2, 0x6e, 0x75, 0xde, 0xdb, 0x9f, 0x33, 0x24, + 0x25, 0x71, 0x45, 0x91, 0xdc, 0x1e, 0xfe, 0xb8, 0xf3, 0xdd, 0xb9, 0xdd, 0x9c, 0x2e, 0x92, 0xbd, + 0x9c, 0x99, 0x9e, 0xeb, 0xee, 0x91, 0x44, 0xfb, 0x33, 0x6c, 0xe3, 0xc3, 0x7d, 0x07, 0xc3, 0xf0, + 0x9d, 0xef, 0xc3, 0xf9, 0xb3, 0xfd, 0xc1, 0x81, 0x1d, 0x23, 0x4e, 0xe2, 0x04, 0x67, 0x04, 0x76, + 0x80, 0x24, 0x48, 0xe0, 0xc0, 0x40, 0x62, 0x1c, 0x82, 0x04, 0xf1, 0x7f, 0x81, 0x2f, 0x01, 0x13, + 0xdf, 0xf9, 0x2f, 0x02, 0x01, 0x02, 0xf8, 0x2f, 0x5f, 0x62, 0x20, 0xa8, 0x57, 0x55, 0xdd, 0x55, + 0x3d, 0x3d, 0xe4, 0x70, 0x57, 0x8b, 0x58, 0xfb, 0x17, 0x39, 0xaf, 0xde, 0x7b, 0x55, 0x5d, 0xf5, + 0xea, 0xd5, 0xab, 0x57, 0xaf, 0x5e, 0xc1, 0x58, 0x74, 0xd0, 0xa1, 0xe1, 0xf5, 0x4e, 0xe0, 0x47, + 0x3e, 0x19, 0xc2, 0x1f, 0x17, 0x67, 0x76, 0xfd, 0x5d, 0x1f, 0x21, 0x37, 0xd8, 0x7f, 0xbc, 0xf0, + 0x62, 0x65, 0xd7, 0xf7, 0x77, 0x9b, 0xf4, 0x06, 0xfe, 0xda, 0xee, 0xee, 0xdc, 0x88, 0xbc, 0x16, + 0x0d, 0x23, 0xa7, 0xd5, 0x11, 0x08, 0xf3, 0xbb, 0x5e, 0xb4, 0xd7, 0xdd, 0xbe, 0xde, 0xf0, 0x5b, + 0x37, 0x76, 0x03, 0xe7, 0xa1, 0x17, 0x39, 0x91, 0xe7, 0xb7, 0x9d, 0xe6, 0x8d, 0x88, 0x36, 0x69, + 0xc7, 0x0f, 0xa2, 0x1b, 0x4e, 0xc7, 0xbb, 0x81, 0x75, 0xdc, 0x78, 0x14, 0x38, 0x9d, 0x0e, 0x0d, + 0x92, 0x7f, 0x38, 0x13, 0xf3, 0xef, 0xe6, 0x61, 0xf4, 0x3e, 0xa5, 0x9d, 0x6a, 0xd3, 0x7b, 0x48, + 0xc9, 0x0b, 0x50, 0x58, 0x71, 0x5a, 0x74, 0xd6, 0xb8, 0x6c, 0x5c, 0x1d, 0xad, 0x4d, 0x1e, 0x1d, + 0x56, 0xc6, 0x42, 0x1a, 0x3c, 0xa4, 0x81, 0xdd, 0x76, 0x5a, 0xd4, 0xc2, 0x42, 0xf2, 0x2a, 0x8c, + 0xb2, 0xbf, 0x61, 0xc7, 0x69, 0xd0, 0xd9, 0x1c, 0x62, 0x8e, 0x1f, 0x1d, 0x56, 0x46, 0xdb, 0x12, + 0x68, 0x25, 0xe5, 0xe4, 0x0a, 0x8c, 0x2c, 0x53, 0x27, 0xa4, 0x4b, 0x0b, 0xb3, 0xf9, 0xcb, 0xc6, + 0xd5, 0x7c, 0xad, 0x74, 0x74, 0x58, 0x29, 0x36, 0x19, 0xc8, 0xf6, 0x5c, 0x4b, 0x16, 0x92, 0x25, + 0x18, 0x59, 0x7c, 0xdc, 0xf1, 0x02, 0x1a, 0xce, 0x16, 0x2e, 0x1b, 0x57, 0xc7, 0xe6, 0x2e, 0x5e, + 0xe7, 0xdf, 0x7f, 0x5d, 0x7e, 0xff, 0xf5, 0x75, 0xf9, 0xfd, 0xb5, 0xe9, 0xef, 0x1f, 0x56, 0xce, + 0x1c, 0x1d, 0x56, 0x46, 0x28, 0x27, 0xf9, 0xd5, 0xff, 0x5a, 0x31, 0x2c, 0x49, 0x4f, 0xde, 0x86, + 0xc2, 0xfa, 0x41, 0x87, 0xce, 0x8e, 0x5e, 0x36, 0xae, 0x4e, 0xcc, 0x3d, 0x77, 0x9d, 0xf7, 0x78, + 0xfc, 0x91, 0xc9, 0x7f, 0x0c, 0xab, 0x56, 0x3c, 0x3a, 0xac, 0x14, 0x18, 0x8a, 0x85, 0x54, 0xe4, + 0x75, 0x18, 0xbe, 0xe7, 0x87, 0xd1, 0xd2, 0xc2, 0x2c, 0xe0, 0xa7, 0x9d, 0x3d, 0x3a, 0xac, 0x4c, + 0xed, 0xf9, 0x61, 0x64, 0x7b, 0xee, 0x6b, 0x7e, 0xcb, 0x8b, 0x68, 0xab, 0x13, 0x1d, 0x58, 0x02, + 0xc9, 0xdc, 0x86, 0x71, 0x8d, 0x1f, 0x19, 0x83, 0x91, 0x8d, 0x95, 0xfb, 0x2b, 0xab, 0x5b, 0x2b, + 0xe5, 0x33, 0xa4, 0x08, 0x85, 0x95, 0xd5, 0x85, 0xc5, 0xb2, 0x41, 0x46, 0x20, 0x5f, 0x5d, 0x5b, + 0x2b, 0xe7, 0x48, 0x09, 0x8a, 0x0b, 0xd5, 0xf5, 0x6a, 0xad, 0x5a, 0x5f, 0x2c, 0xe7, 0xc9, 0x34, + 0x4c, 0x6e, 0x2d, 0xad, 0x2c, 0xac, 0x6e, 0xd5, 0xed, 0x85, 0xc5, 0xfa, 0xfd, 0xf5, 0xd5, 0xb5, + 0x72, 0x81, 0x4c, 0x00, 0xdc, 0xdf, 0xa8, 0x2d, 0x5a, 0x2b, 0x8b, 0xeb, 0x8b, 0xf5, 0xf2, 0x90, + 0xf9, 0xcd, 0x3c, 0x14, 0x1f, 0xd0, 0xc8, 0x71, 0x9d, 0xc8, 0x21, 0x97, 0xb4, 0x21, 0xc2, 0xd6, + 0x2b, 0x63, 0xf3, 0x42, 0xef, 0xd8, 0x0c, 0x1d, 0x1d, 0x56, 0x8c, 0xd7, 0xd5, 0x31, 0x79, 0x0b, + 0xc6, 0x16, 0x68, 0xd8, 0x08, 0xbc, 0x0e, 0x93, 0x17, 0x1c, 0x97, 0xd1, 0xda, 0x85, 0xa3, 0xc3, + 0xca, 0x59, 0x37, 0x01, 0x2b, 0xdf, 0xaa, 0x62, 0x93, 0x25, 0x18, 0x5e, 0x76, 0xb6, 0x69, 0x33, + 0x9c, 0x1d, 0xba, 0x9c, 0xbf, 0x3a, 0x36, 0xf7, 0x8c, 0xe8, 0x5f, 0xd9, 0xc0, 0xeb, 0xbc, 0x74, + 0xb1, 0x1d, 0x05, 0x07, 0xb5, 0x99, 0xa3, 0xc3, 0x4a, 0xb9, 0x89, 0x00, 0xb5, 0xef, 0x38, 0x0a, + 0xa9, 0x27, 0x63, 0x3e, 0x7c, 0xe2, 0x98, 0x3f, 0xfb, 0xfd, 0xc3, 0x8a, 0xc1, 0xc6, 0x42, 0x8c, + 0x79, 0xc2, 0x4f, 0x1f, 0xfd, 0xcb, 0x90, 0x5b, 0x5a, 0x98, 0x1d, 0x41, 0x59, 0x2b, 0x1f, 0x1d, + 0x56, 0x4a, 0xda, 0xb0, 0xe5, 0x96, 0x16, 0x2e, 0xbe, 0x09, 0x63, 0x4a, 0x1b, 0x49, 0x19, 0xf2, + 0xfb, 0xf4, 0x80, 0xf7, 0xa7, 0xc5, 0xfe, 0x25, 0x33, 0x30, 0xf4, 0xd0, 0x69, 0x76, 0x45, 0x07, + 0x5a, 0xfc, 0xc7, 0x17, 0x73, 0x3f, 0x61, 0x98, 0xff, 0x6f, 0x01, 0x8a, 0x96, 0xcf, 0xe7, 0x19, + 0x79, 0x05, 0x86, 0xea, 0x91, 0x13, 0xc9, 0xa1, 0x98, 0x3e, 0x3a, 0xac, 0x4c, 0x86, 0x0c, 0xa0, + 0xd4, 0xc7, 0x31, 0x18, 0xea, 0xda, 0x9e, 0x13, 0xca, 0x21, 0x41, 0xd4, 0x0e, 0x03, 0xa8, 0xa8, + 0x88, 0x41, 0xae, 0x40, 0xe1, 0x81, 0xef, 0x52, 0x31, 0x2a, 0xe4, 0xe8, 0xb0, 0x32, 0xd1, 0xf2, + 0x5d, 0x15, 0x11, 0xcb, 0xc9, 0x6b, 0x30, 0x3a, 0xdf, 0x0d, 0x02, 0xda, 0x66, 0xa2, 0x5a, 0x40, + 0xe4, 0x89, 0xa3, 0xc3, 0x0a, 0x34, 0x38, 0x90, 0x4d, 0xae, 0x04, 0x81, 0x75, 0x75, 0x3d, 0x72, + 0x82, 0x88, 0xba, 0xb3, 0x43, 0x03, 0x75, 0x35, 0x9b, 0x5e, 0x53, 0x21, 0x27, 0x49, 0x77, 0xb5, + 0xe0, 0x44, 0xee, 0xc1, 0xd8, 0xdd, 0xc0, 0x69, 0xd0, 0x35, 0x1a, 0x78, 0xbe, 0x8b, 0x63, 0x98, + 0xaf, 0x5d, 0x39, 0x3a, 0xac, 0x9c, 0xdb, 0x65, 0x60, 0xbb, 0x83, 0xf0, 0x84, 0xfa, 0xc7, 0x87, + 0x95, 0xe2, 0x42, 0x37, 0xc0, 0xde, 0xb3, 0x54, 0x52, 0xf2, 0xd3, 0x6c, 0x48, 0xc2, 0x08, 0xbb, + 0x96, 0xba, 0x38, 0x7a, 0xc7, 0x37, 0xd1, 0x14, 0x4d, 0x3c, 0xd7, 0x74, 0xc2, 0xc8, 0x0e, 0x38, + 0x5d, 0xaa, 0x9d, 0x2a, 0x4b, 0xb2, 0x0a, 0xc5, 0x7a, 0x63, 0x8f, 0xba, 0xdd, 0x26, 0x9d, 0x2d, + 0x22, 0xfb, 0xf3, 0x42, 0x70, 0xe5, 0x78, 0xca, 0xe2, 0xda, 0x45, 0xc1, 0x9b, 0x84, 0x02, 0xa2, + 0xf4, 0x7d, 0xcc, 0xe4, 0x8b, 0xc5, 0xdf, 0xf8, 0x9d, 0xca, 0x99, 0x5f, 0xf8, 0x2f, 0x97, 0xcf, + 0x98, 0xff, 0x2c, 0x07, 0xe5, 0x34, 0x13, 0xb2, 0x03, 0xe3, 0x1b, 0x1d, 0xd7, 0x89, 0xe8, 0x7c, + 0xd3, 0xa3, 0xed, 0x28, 0x44, 0x21, 0x39, 0xfe, 0x9b, 0x5e, 0x14, 0xf5, 0xce, 0x76, 0x91, 0xd0, + 0x6e, 0x70, 0xca, 0xd4, 0x57, 0xe9, 0x6c, 0x93, 0x7a, 0xea, 0xa8, 0xa7, 0x43, 0x94, 0xb0, 0xd3, + 0xd5, 0xc3, 0x35, 0x7c, 0x9f, 0x7a, 0x04, 0x5b, 0x21, 0x40, 0x6d, 0x77, 0xfb, 0x00, 0x25, 0x73, + 0x70, 0x01, 0x62, 0x24, 0x19, 0x02, 0xc4, 0xc0, 0xe6, 0x5f, 0x1a, 0x30, 0x61, 0xd1, 0xd0, 0xef, + 0x06, 0x0d, 0x7a, 0x8f, 0x3a, 0x2e, 0x0d, 0x98, 0xf8, 0xdf, 0xf7, 0xda, 0xae, 0x98, 0x53, 0x28, + 0xfe, 0xfb, 0x5e, 0x5b, 0x9d, 0xc2, 0x58, 0x4e, 0x3e, 0x07, 0x23, 0xf5, 0xee, 0x36, 0xa2, 0xf2, + 0x39, 0x75, 0x0e, 0x47, 0xac, 0xbb, 0x6d, 0xa7, 0xd0, 0x25, 0x1a, 0xb9, 0x01, 0x23, 0x9b, 0x34, + 0x08, 0x13, 0x8d, 0x87, 0x9a, 0xfd, 0x21, 0x07, 0xa9, 0x04, 0x02, 0x8b, 0xdc, 0x4d, 0xb4, 0xae, + 0x58, 0x93, 0x26, 0x53, 0xba, 0x2e, 0x11, 0x95, 0x96, 0x80, 0xa8, 0xa2, 0x22, 0xb1, 0xcc, 0xef, + 0xe4, 0xa0, 0xbc, 0xe0, 0x44, 0xce, 0xb6, 0x13, 0x8a, 0xfe, 0xdc, 0xbc, 0xc5, 0xf4, 0xb8, 0xf2, + 0xa1, 0xa8, 0xc7, 0x59, 0xcb, 0x3f, 0xf6, 0xe7, 0xbd, 0x94, 0xfe, 0xbc, 0x31, 0xb6, 0x40, 0x8a, + 0xcf, 0x4b, 0x3e, 0xea, 0x9d, 0x93, 0x3f, 0xaa, 0x2c, 0x3e, 0xaa, 0x28, 0x3f, 0x2a, 0xf9, 0x14, + 0xf2, 0x0e, 0x14, 0xea, 0x1d, 0xda, 0x10, 0x4a, 0x44, 0xea, 0x7e, 0xfd, 0xe3, 0x18, 0xc2, 0xe6, + 0xad, 0x5a, 0x49, 0xb0, 0x29, 0x84, 0x1d, 0xda, 0xb0, 0x90, 0x4c, 0x99, 0x34, 0xdf, 0x1d, 0x86, + 0x99, 0x2c, 0x32, 0xf2, 0x8e, 0xbe, 0x38, 0xf1, 0xee, 0x79, 0xa6, 0xef, 0xe2, 0x34, 0x6b, 0xe8, + 0xcb, 0xd3, 0x35, 0x28, 0xae, 0x31, 0x81, 0x6c, 0xf8, 0x4d, 0xd1, 0x73, 0x4c, 0x2b, 0x16, 0x3b, + 0x12, 0x66, 0x58, 0x71, 0x39, 0x79, 0x06, 0xf2, 0x1b, 0xd6, 0x92, 0xe8, 0xae, 0xd1, 0xa3, 0xc3, + 0x4a, 0xbe, 0x1b, 0x78, 0xb3, 0x86, 0xc5, 0xa0, 0xe4, 0x06, 0x0c, 0xcf, 0x57, 0xe7, 0x69, 0x10, + 0x61, 0x37, 0x95, 0x6a, 0xe7, 0x99, 0xb4, 0x34, 0x1c, 0xbb, 0x41, 0x83, 0x48, 0xab, 0x5e, 0xa0, + 0x91, 0x57, 0x21, 0x5f, 0xdd, 0xaa, 0x8b, 0x9e, 0x01, 0xd1, 0x33, 0xd5, 0xad, 0x7a, 0x6d, 0x5c, + 0x74, 0x44, 0xde, 0x79, 0x14, 0x32, 0xee, 0xd5, 0xad, 0xba, 0x3a, 0x5a, 0xc3, 0xc7, 0x8c, 0xd6, + 0x55, 0x28, 0x32, 0x3b, 0x83, 0x2d, 0xf0, 0xa8, 0x14, 0x47, 0xb9, 0xf9, 0xb4, 0x27, 0x60, 0x56, + 0x5c, 0x4a, 0x5e, 0x88, 0xcd, 0x96, 0x62, 0xc2, 0x4f, 0x98, 0x2d, 0xd2, 0x58, 0x21, 0x8f, 0x61, + 0x7c, 0xe1, 0xa0, 0xed, 0xb4, 0xbc, 0x86, 0x58, 0xc2, 0x47, 0x71, 0x09, 0xbf, 0x7e, 0xcc, 0x30, + 0x5e, 0xd7, 0x08, 0xf8, 0xaa, 0x2e, 0x95, 0xef, 0xac, 0xcb, 0xcb, 0xec, 0xf4, 0x0a, 0x3f, 0x6b, + 0x58, 0x7a, 0x45, 0x6c, 0x2e, 0x49, 0x15, 0x89, 0x76, 0x55, 0x22, 0x76, 0x12, 0x9c, 0xcc, 0xa5, + 0x40, 0x40, 0xd4, 0xb9, 0x14, 0x2f, 0xba, 0xef, 0x40, 0xfe, 0xee, 0xfc, 0xda, 0xec, 0x18, 0xf2, + 0x20, 0x82, 0xc7, 0xdd, 0xf9, 0xb5, 0xf9, 0xa6, 0xdf, 0x75, 0xeb, 0x1f, 0x2e, 0xd7, 0xce, 0x0b, + 0x36, 0xe3, 0xbb, 0x8d, 0x8e, 0xd6, 0x22, 0x46, 0x47, 0x16, 0xa1, 0x28, 0xbf, 0x72, 0xb6, 0x84, + 0x3c, 0xa6, 0x52, 0x1f, 0xbf, 0x79, 0x8b, 0xcf, 0x35, 0x57, 0xfc, 0x56, 0x5b, 0x21, 0x71, 0x2e, + 0x6e, 0x01, 0xe9, 0xed, 0x97, 0x0c, 0x4b, 0xe2, 0x55, 0xd5, 0x92, 0x18, 0x9b, 0x3b, 0x2b, 0xea, + 0x9a, 0xf7, 0x5b, 0x2d, 0xa7, 0xed, 0x22, 0xed, 0xe6, 0x9c, 0x6a, 0x60, 0x54, 0x61, 0x22, 0x69, + 0xc8, 0xb2, 0x17, 0x46, 0xe4, 0x06, 0x8c, 0x4a, 0x08, 0x5b, 0x44, 0xf2, 0x99, 0x4d, 0xb6, 0x12, + 0x1c, 0xf3, 0x4f, 0x73, 0x00, 0x49, 0xc9, 0x53, 0xaa, 0x67, 0xbe, 0xa0, 0xe9, 0x99, 0xb3, 0x69, + 0x01, 0xed, 0xab, 0x61, 0xc8, 0x7b, 0x30, 0xcc, 0x4c, 0xae, 0xae, 0x34, 0x29, 0xcf, 0xa7, 0x49, + 0xb1, 0x70, 0xf3, 0x56, 0x6d, 0x42, 0x10, 0x0f, 0x87, 0x08, 0xb1, 0x04, 0x99, 0xa2, 0xa2, 0xfe, + 0x70, 0x28, 0x19, 0x0c, 0xa1, 0x9c, 0xae, 0x2a, 0xda, 0xc5, 0x48, 0xe6, 0xa3, 0xd4, 0x2e, 0x8a, + 0x6e, 0xb9, 0xc0, 0x75, 0x0b, 0xef, 0xd4, 0x11, 0xa1, 0x5b, 0xd2, 0x9a, 0x85, 0x77, 0xe0, 0x89, + 0x9a, 0xa5, 0x93, 0x9e, 0xb6, 0x05, 0x14, 0x83, 0xab, 0x99, 0xbd, 0x92, 0x35, 0x61, 0x2f, 0x9f, + 0x34, 0x61, 0xd3, 0xd3, 0xf5, 0x56, 0x3f, 0x5d, 0x76, 0x56, 0xce, 0x2e, 0xe7, 0x91, 0x4a, 0x8e, + 0x3a, 0xed, 0x2d, 0x3e, 0x35, 0x87, 0xfb, 0x4e, 0xcd, 0xb3, 0x99, 0x53, 0x93, 0x4f, 0xcc, 0xb7, + 0x60, 0xa8, 0xfa, 0x33, 0xdd, 0x80, 0x0a, 0xdb, 0xaf, 0x24, 0xeb, 0x64, 0xb0, 0x78, 0x4e, 0x4f, + 0x3a, 0xec, 0xa7, 0x6a, 0x33, 0x63, 0x39, 0xab, 0x79, 0x7d, 0xb9, 0x2e, 0xec, 0x3a, 0x92, 0xea, + 0x96, 0xf5, 0x65, 0xa5, 0xd9, 0x91, 0xf6, 0xd5, 0x8c, 0x8a, 0xdc, 0x80, 0x5c, 0x75, 0x01, 0x37, + 0x8b, 0x63, 0x73, 0xa3, 0xb2, 0xda, 0x85, 0xda, 0x8c, 0x20, 0x29, 0x39, 0xda, 0xfe, 0xa1, 0xba, + 0x40, 0x6a, 0x30, 0xf4, 0xe0, 0xa0, 0xfe, 0xe1, 0xb2, 0x50, 0x64, 0xd3, 0x52, 0xae, 0x19, 0x6c, + 0x15, 0x57, 0xa1, 0x30, 0x69, 0x71, 0xeb, 0x20, 0xfc, 0x7a, 0x53, 0x6d, 0x31, 0xa2, 0x7d, 0x7a, + 0x0a, 0xe4, 0x9f, 0x18, 0x8a, 0xad, 0x21, 0x64, 0x9d, 0xed, 0x69, 0x85, 0xc4, 0x19, 0x89, 0xe5, + 0xd3, 0x23, 0x71, 0xb1, 0xbc, 0xbd, 0xc2, 0x47, 0x3f, 0xd7, 0x33, 0xfa, 0x63, 0xca, 0x4a, 0xc6, + 0xc7, 0x3c, 0xee, 0x8b, 0xfc, 0xc7, 0xee, 0x0b, 0xf3, 0x8f, 0x73, 0x58, 0x1f, 0x79, 0x0d, 0x86, + 0x2d, 0xba, 0x9b, 0x2c, 0xfa, 0xb8, 0x79, 0x0c, 0x10, 0xa2, 0x36, 0x92, 0xe3, 0xe0, 0x8a, 0x42, + 0xdd, 0x70, 0xcf, 0xdb, 0x89, 0x44, 0x4b, 0xe3, 0x15, 0x45, 0x80, 0x95, 0x15, 0x45, 0x40, 0xb4, + 0x15, 0x45, 0xc0, 0x98, 0xac, 0x5b, 0x0b, 0x75, 0xf1, 0x01, 0xf2, 0x6b, 0xad, 0x05, 0x45, 0x68, + 0x02, 0x57, 0x13, 0x1a, 0x6b, 0xa1, 0x4e, 0x6e, 0xc3, 0x68, 0xb5, 0xd1, 0xf0, 0xbb, 0xca, 0xee, + 0x6b, 0xf6, 0xe8, 0xb0, 0x32, 0xe3, 0x70, 0xa0, 0xee, 0x2b, 0x48, 0x50, 0x49, 0x1d, 0xc6, 0x16, + 0xd9, 0x96, 0xc5, 0x9b, 0x77, 0x1a, 0x7b, 0x54, 0x4c, 0x30, 0x29, 0xb1, 0x4a, 0x49, 0x6c, 0x42, + 0x9f, 0xa5, 0x08, 0x6c, 0x30, 0xa0, 0xba, 0x25, 0x57, 0x70, 0xcd, 0x5a, 0xd2, 0x15, 0xac, 0x61, + 0xf3, 0xcd, 0x6e, 0x18, 0xd1, 0x60, 0x69, 0x41, 0xf4, 0x23, 0x36, 0xac, 0xc1, 0x81, 0xa9, 0x86, + 0xc5, 0xa8, 0xe6, 0x7f, 0x36, 0xb0, 0x1b, 0xc8, 0x9b, 0x00, 0x4b, 0x6d, 0x66, 0xb6, 0x37, 0x68, + 0xcc, 0x00, 0x5d, 0x03, 0x9e, 0x80, 0xea, 0x1c, 0x14, 0x64, 0xbd, 0xea, 0xdc, 0xc0, 0x55, 0xb3, + 0x2a, 0xe5, 0x26, 0x40, 0x78, 0x89, 0x44, 0x95, 0x81, 0x80, 0xa6, 0xaa, 0x4c, 0x90, 0xc9, 0x15, + 0x18, 0x59, 0xaa, 0x3e, 0xa8, 0x76, 0xa3, 0x3d, 0x1c, 0x84, 0x22, 0x57, 0xc7, 0x9e, 0xd3, 0xb2, + 0x9d, 0x6e, 0xb4, 0x67, 0xc9, 0x42, 0xf3, 0xdf, 0xe5, 0xb4, 0x7e, 0x27, 0x16, 0x10, 0x8b, 0x76, + 0x9a, 0x5e, 0x03, 0x8d, 0x8a, 0xbb, 0x81, 0xdf, 0xed, 0xc4, 0x5f, 0x6b, 0x1e, 0x1d, 0x56, 0x9e, + 0x0b, 0x92, 0x52, 0x7b, 0x97, 0x15, 0xeb, 0x6d, 0xc8, 0xa0, 0x26, 0xef, 0x43, 0x69, 0x23, 0xa4, + 0x81, 0xf8, 0xc9, 0x36, 0x62, 0xf9, 0xab, 0xa3, 0xb5, 0x4b, 0xb8, 0xd1, 0x0a, 0x69, 0x10, 0xb3, + 0x51, 0x65, 0x49, 0xa3, 0x20, 0x2e, 0xcc, 0xae, 0x07, 0x4e, 0x3b, 0xf4, 0xa2, 0xc5, 0x76, 0x23, + 0x38, 0xc0, 0xe9, 0xb3, 0xd8, 0x76, 0xb6, 0x9b, 0xd4, 0xc5, 0x6e, 0x29, 0xd6, 0xae, 0x1e, 0x1d, + 0x56, 0x5e, 0x8c, 0x38, 0x8e, 0x4d, 0x63, 0x24, 0x9b, 0x72, 0x2c, 0x85, 0x73, 0x5f, 0x4e, 0xe4, + 0x3d, 0x28, 0x2d, 0xb6, 0xdd, 0x8e, 0xef, 0xb5, 0x23, 0x74, 0x93, 0x15, 0x62, 0x0b, 0xfb, 0x3c, + 0x15, 0x70, 0x9b, 0xc9, 0xa3, 0xda, 0x4c, 0x95, 0xc0, 0xfc, 0x05, 0x03, 0xc6, 0x14, 0xb5, 0xce, + 0xc6, 0x7d, 0x2d, 0xf0, 0x3f, 0xa2, 0x8d, 0x48, 0x17, 0xb9, 0x0e, 0x07, 0xa6, 0xc6, 0x3d, 0x46, + 0x4d, 0x89, 0x5a, 0xee, 0x14, 0xa2, 0x66, 0xde, 0x10, 0xab, 0x05, 0xdb, 0x2e, 0x2a, 0xde, 0x30, + 0xdc, 0x2e, 0x32, 0x73, 0x58, 0xdd, 0x2e, 0xb2, 0x72, 0xf3, 0xf7, 0x0d, 0xa6, 0xe5, 0xc9, 0x0d, + 0x80, 0xfb, 0xf4, 0x20, 0x72, 0xb6, 0xef, 0x78, 0x4d, 0xcd, 0xcb, 0xb9, 0x8f, 0x50, 0x7b, 0xc7, + 0x6b, 0x52, 0x4b, 0x41, 0x21, 0xb7, 0xa0, 0x78, 0x3f, 0xd8, 0x7e, 0x03, 0xd1, 0x73, 0xf1, 0x6a, + 0x3d, 0xbd, 0x1f, 0x6c, 0xbf, 0x81, 0xc8, 0xaa, 0x46, 0x91, 0x88, 0xc4, 0x84, 0xe1, 0x05, 0xbf, + 0xe5, 0x78, 0xd2, 0x42, 0x02, 0x66, 0x66, 0xb8, 0x08, 0xb1, 0x44, 0x09, 0xb3, 0x0f, 0xea, 0x6b, + 0x2b, 0xa2, 0xf3, 0xd1, 0x3e, 0x08, 0x3b, 0x6d, 0x8b, 0xc1, 0xcc, 0xef, 0x19, 0x30, 0xa6, 0x2c, + 0x5e, 0xe4, 0xf3, 0xc2, 0x23, 0x64, 0xa0, 0x3f, 0xf3, 0x5c, 0xef, 0xf2, 0xc6, 0x4a, 0xb9, 0x65, + 0xd7, 0xf2, 0x5d, 0x2a, 0xfc, 0x43, 0x89, 0xce, 0xcf, 0x0d, 0xa2, 0xf3, 0xdf, 0x04, 0xe0, 0x66, + 0x3f, 0x76, 0xa7, 0x32, 0x09, 0x15, 0xff, 0xaf, 0x3a, 0x18, 0x09, 0xb2, 0x69, 0x41, 0x49, 0xd5, + 0xf7, 0xa4, 0x06, 0xe3, 0x62, 0x97, 0x2b, 0xec, 0x44, 0xde, 0xcf, 0x38, 0x13, 0x04, 0xb7, 0xde, + 0x5d, 0xb7, 0x4e, 0x62, 0xfe, 0x62, 0x0e, 0x8a, 0x02, 0x32, 0xf7, 0x94, 0x9a, 0xb0, 0x6f, 0x68, + 0x26, 0xac, 0x5c, 0x19, 0x95, 0xbd, 0xd5, 0xdc, 0x09, 0x5b, 0xe4, 0x37, 0xa1, 0x24, 0xbb, 0x00, + 0x77, 0x02, 0xaf, 0xc0, 0x88, 0x74, 0xf2, 0xf0, 0x7d, 0xc0, 0xa4, 0xc6, 0x73, 0x73, 0xce, 0x92, + 0xe5, 0xe6, 0x77, 0x86, 0x24, 0x2d, 0xaf, 0x89, 0x75, 0x61, 0xd5, 0x75, 0x03, 0xb5, 0x0b, 0x1d, + 0xd7, 0x0d, 0x2c, 0x84, 0xb2, 0xc1, 0x5f, 0xeb, 0x6e, 0x37, 0xbd, 0x06, 0xe2, 0x28, 0x33, 0xb1, + 0x83, 0x50, 0x9b, 0xa1, 0xaa, 0x83, 0x9f, 0x20, 0x6b, 0x3b, 0xd4, 0xfc, 0xb1, 0x3b, 0xd4, 0x9f, + 0x82, 0xd1, 0xf9, 0x96, 0xab, 0x59, 0xb0, 0x66, 0x46, 0xa7, 0x5c, 0x8f, 0x91, 0xb8, 0xed, 0x7a, + 0x49, 0xf4, 0xd1, 0x4c, 0xa3, 0xe5, 0xf6, 0xda, 0xad, 0x09, 0x4b, 0x6d, 0x8b, 0x39, 0xf4, 0x49, + 0xb6, 0x98, 0xb7, 0x61, 0x74, 0x23, 0xa4, 0xeb, 0xdd, 0x76, 0x9b, 0x36, 0xd1, 0x9a, 0x2d, 0x72, + 0x7d, 0xd6, 0x0d, 0xa9, 0x1d, 0x21, 0x54, 0x6d, 0x40, 0x8c, 0xaa, 0x8a, 0xd5, 0xc8, 0x31, 0x62, + 0xf5, 0x79, 0x28, 0x54, 0x3b, 0x1d, 0xb9, 0xf7, 0x8e, 0xcd, 0xab, 0x4e, 0x07, 0x0d, 0x9e, 0x09, + 0xa7, 0xd3, 0xd1, 0x77, 0xd2, 0x88, 0x4d, 0x28, 0x90, 0xfb, 0xdd, 0x6d, 0x1a, 0xb4, 0x69, 0x44, + 0x43, 0xb1, 0x76, 0x86, 0xb3, 0x80, 0x3c, 0x66, 0xe5, 0x11, 0x47, 0x1a, 0x81, 0x6b, 0xf5, 0xfd, + 0xee, 0x36, 0xb5, 0xc5, 0x22, 0xac, 0xf6, 0x5d, 0x06, 0xc3, 0x8b, 0x75, 0x98, 0xd0, 0xfb, 0xff, + 0x09, 0xd8, 0xa4, 0x1f, 0x14, 0x8a, 0xc5, 0xf2, 0xa8, 0xf9, 0xcd, 0x1c, 0x8c, 0x55, 0x3b, 0x9d, + 0xa7, 0xdc, 0x01, 0xf6, 0x13, 0xda, 0xac, 0x3e, 0x97, 0x8c, 0xde, 0x29, 0x7c, 0x5f, 0x7f, 0x6d, + 0xc0, 0x64, 0x8a, 0x42, 0x6d, 0xbd, 0x31, 0xa0, 0x43, 0x28, 0x37, 0xa0, 0x43, 0x28, 0xdf, 0xdf, + 0x21, 0xa4, 0xce, 0x99, 0xc2, 0x27, 0x99, 0x33, 0x2f, 0x43, 0xbe, 0xda, 0xe9, 0x88, 0x5e, 0x29, + 0x25, 0xbd, 0xb2, 0x79, 0x8b, 0x2f, 0x6e, 0x4e, 0xa7, 0x63, 0x31, 0x0c, 0xf3, 0x75, 0x18, 0x45, + 0x30, 0x6a, 0xb4, 0xcb, 0x62, 0x2a, 0x70, 0x75, 0xa6, 0x91, 0x71, 0xb1, 0x37, 0xff, 0xa7, 0x01, + 0x43, 0xf8, 0xfb, 0x29, 0x15, 0x97, 0x39, 0x4d, 0x5c, 0xca, 0x8a, 0xb8, 0x0c, 0x22, 0x28, 0x7f, + 0x98, 0xc7, 0xde, 0x12, 0x22, 0x22, 0x5c, 0x0a, 0x46, 0x86, 0x4b, 0xe1, 0x13, 0x28, 0xf0, 0xfd, + 0xb4, 0x73, 0x21, 0x8f, 0x83, 0xf1, 0x42, 0xba, 0xa9, 0x4f, 0xc4, 0xaf, 0x70, 0x0f, 0xc8, 0x52, + 0x3b, 0xa4, 0x8d, 0x6e, 0x40, 0xeb, 0xfb, 0x5e, 0x67, 0x93, 0x06, 0xde, 0xce, 0x81, 0x30, 0xdd, + 0x51, 0xc7, 0x7a, 0xa2, 0xd4, 0x0e, 0xf7, 0xbd, 0x0e, 0x33, 0x13, 0xbc, 0x9d, 0x03, 0x2b, 0x83, + 0x86, 0xbc, 0x07, 0x23, 0x16, 0x7d, 0x14, 0x78, 0x91, 0xdc, 0x44, 0x4d, 0xc4, 0xbb, 0x3f, 0x84, + 0x72, 0x7b, 0x27, 0xe0, 0x3f, 0xd4, 0xf1, 0x17, 0xe5, 0x9f, 0xde, 0x0e, 0xfc, 0xbb, 0x43, 0x38, + 0x17, 0x4e, 0x38, 0xa8, 0x3d, 0xc6, 0x3f, 0xa4, 0x0f, 0x66, 0xfe, 0x34, 0x83, 0xb9, 0x09, 0x25, + 0xb6, 0xe9, 0x4f, 0x39, 0x8a, 0x2e, 0x25, 0x63, 0x79, 0x5d, 0x2d, 0x3e, 0xee, 0x8c, 0x56, 0xe3, + 0x43, 0xec, 0xb4, 0x90, 0xf0, 0xb3, 0xdf, 0x67, 0x15, 0xc6, 0x19, 0xe2, 0x11, 0xab, 0x8e, 0x06, + 0xef, 0xac, 0x53, 0x0b, 0xc6, 0xf0, 0x27, 0x13, 0x8c, 0x91, 0x8f, 0x23, 0x18, 0xe9, 0xd3, 0xf1, + 0xe2, 0x69, 0x4e, 0xc7, 0x2f, 0xbe, 0x07, 0x53, 0x3d, 0x3d, 0x7c, 0x9a, 0x13, 0xe6, 0x4f, 0x4f, + 0x2c, 0x7f, 0x2e, 0xee, 0x17, 0x32, 0x87, 0xfe, 0x02, 0x2f, 0xa0, 0x8d, 0x08, 0x55, 0xaf, 0xd0, + 0x96, 0x81, 0x80, 0xa5, 0xbc, 0x24, 0x08, 0x23, 0xef, 0xc2, 0x08, 0x3f, 0xa1, 0xe3, 0x1b, 0xdb, + 0xb1, 0xb9, 0x71, 0x51, 0x23, 0x87, 0x8a, 0x30, 0x09, 0x8e, 0xa1, 0xf6, 0xaa, 0x20, 0x32, 0xef, + 0xc2, 0xb0, 0x38, 0xe1, 0x3b, 0x7e, 0x5e, 0x54, 0x60, 0x68, 0x33, 0xe9, 0x19, 0x3c, 0x95, 0xe1, + 0x1f, 0x61, 0x71, 0xb8, 0xf9, 0xcb, 0x06, 0x4c, 0xe8, 0x5f, 0x49, 0xae, 0xc3, 0xb0, 0x38, 0x82, + 0x36, 0xf0, 0x08, 0x9a, 0x7d, 0xcd, 0x30, 0x3f, 0x7c, 0xd6, 0x8e, 0x9c, 0x05, 0x16, 0x53, 0xfd, + 0x82, 0x83, 0xd8, 0xa4, 0xa3, 0xea, 0x17, 0x42, 0x6a, 0xc9, 0x32, 0xb6, 0x8d, 0xb3, 0x68, 0xd8, + 0x6d, 0x46, 0xea, 0x36, 0x2e, 0x40, 0x88, 0x25, 0x4a, 0xcc, 0x43, 0x03, 0xa0, 0x5e, 0xbf, 0x77, + 0x9f, 0x1e, 0xac, 0x39, 0x5e, 0x80, 0x5b, 0x61, 0x9c, 0x8d, 0xf7, 0xc5, 0x68, 0x95, 0xc4, 0x56, + 0x98, 0xcf, 0xdc, 0x7d, 0x7a, 0xa0, 0x6d, 0x85, 0x25, 0x2a, 0x4e, 0xf9, 0xc0, 0x7b, 0xe8, 0x44, + 0x94, 0x11, 0xe6, 0x90, 0x90, 0x4f, 0x79, 0x0e, 0x4d, 0x51, 0x2a, 0xc8, 0xe4, 0x6b, 0x30, 0x91, + 0xfc, 0xc2, 0x0d, 0x7d, 0x1e, 0xf7, 0x89, 0x52, 0x22, 0xf4, 0xc2, 0xda, 0x73, 0x47, 0x87, 0x95, + 0x8b, 0x0a, 0xd7, 0xf4, 0x56, 0x3f, 0xc5, 0xcc, 0xfc, 0x5d, 0x03, 0x60, 0x7d, 0xb9, 0x2e, 0x3f, + 0xf0, 0x0a, 0x14, 0x62, 0x3f, 0x62, 0x89, 0xef, 0xb7, 0x53, 0x1b, 0x4a, 0x2c, 0x27, 0x2f, 0x40, + 0x3e, 0xf9, 0x92, 0xa9, 0xa3, 0xc3, 0xca, 0xb8, 0xfe, 0x05, 0xac, 0x94, 0xdc, 0x85, 0x91, 0x81, + 0xda, 0x8c, 0xd2, 0x99, 0xd1, 0x56, 0x49, 0x8d, 0xa3, 0xf0, 0xc1, 0xd6, 0xfa, 0x67, 0x77, 0x14, + 0xbe, 0x9d, 0x83, 0x49, 0xd6, 0xaf, 0xd5, 0x6e, 0xb4, 0xe7, 0x07, 0x5e, 0x74, 0xf0, 0xd4, 0xee, + 0x8a, 0xdf, 0xd6, 0x0c, 0xa2, 0x8b, 0x52, 0x6d, 0xa9, 0xdf, 0x36, 0xd0, 0xe6, 0xf8, 0x2f, 0x46, + 0x60, 0x3a, 0x83, 0x8a, 0xbc, 0x26, 0x82, 0xbf, 0x12, 0x3f, 0x14, 0x06, 0x77, 0xfd, 0xf8, 0xb0, + 0x52, 0x92, 0xe8, 0xeb, 0x49, 0xb0, 0xd7, 0x1c, 0x8c, 0x89, 0xad, 0xcf, 0x4a, 0x62, 0x51, 0x63, + 0xd4, 0x90, 0x74, 0x5a, 0xa2, 0x6a, 0x52, 0x91, 0x48, 0x15, 0x4a, 0xf3, 0x7b, 0xb4, 0xb1, 0xef, + 0xb5, 0x77, 0xef, 0xd3, 0x03, 0x6e, 0x2f, 0x95, 0x6a, 0xcf, 0xb2, 0x9d, 0x56, 0x43, 0xc0, 0xd9, + 0x90, 0xea, 0x9b, 0x38, 0x8d, 0x84, 0xbc, 0x0b, 0x63, 0x75, 0x6f, 0xb7, 0x2d, 0x39, 0x14, 0x90, + 0xc3, 0xa5, 0xa3, 0xc3, 0xca, 0xb9, 0x90, 0x83, 0x7b, 0x19, 0xa8, 0x04, 0xe4, 0x15, 0x18, 0xb2, + 0xfc, 0x26, 0xe5, 0xcb, 0xb0, 0x08, 0x27, 0x0a, 0x18, 0x40, 0x75, 0xae, 0x23, 0x06, 0xb9, 0x07, + 0x23, 0xec, 0x9f, 0x07, 0x4e, 0x67, 0x76, 0x18, 0xf5, 0x36, 0x89, 0x0d, 0x7c, 0x84, 0x76, 0xbc, + 0xf6, 0xae, 0x6a, 0xe3, 0x37, 0xa9, 0xdd, 0x72, 0x3a, 0xda, 0xba, 0xc8, 0x11, 0xc9, 0x26, 0x8c, + 0x25, 0x8a, 0x20, 0x9c, 0x1d, 0xd1, 0x8e, 0x22, 0x93, 0x92, 0xda, 0xf3, 0x82, 0xd9, 0xf9, 0xa8, + 0x19, 0xa2, 0x6c, 0x77, 0x18, 0xbe, 0xfe, 0x31, 0x0a, 0x23, 0x6d, 0x0f, 0x52, 0xec, 0xbf, 0x07, + 0x31, 0x4e, 0xdc, 0x83, 0xb8, 0x00, 0xa2, 0x93, 0xaa, 0xcd, 0x5d, 0x11, 0xfd, 0xf7, 0x4a, 0x7f, + 0x01, 0xbb, 0x9e, 0x20, 0xe3, 0x9c, 0xe4, 0xde, 0x2e, 0xd1, 0xff, 0x4e, 0x73, 0x57, 0xf3, 0x76, + 0xc5, 0xa8, 0xac, 0x1b, 0x12, 0x55, 0x23, 0x77, 0xe0, 0xb2, 0x1b, 0x92, 0x92, 0xa4, 0x1b, 0x3e, + 0x7a, 0x14, 0xf5, 0xeb, 0x06, 0x85, 0x11, 0x59, 0x01, 0xa8, 0x36, 0x22, 0xef, 0x21, 0x45, 0x91, + 0x18, 0xd3, 0x3a, 0x62, 0xbe, 0x7a, 0x9f, 0x1e, 0xd4, 0x69, 0x94, 0x9c, 0x0a, 0x38, 0x88, 0x9a, + 0x12, 0x13, 0x4b, 0xe1, 0x40, 0x3a, 0x70, 0xb6, 0xea, 0xba, 0x1e, 0x8f, 0x08, 0x5d, 0x0f, 0x98, + 0xfc, 0xba, 0xc8, 0xba, 0x94, 0xcd, 0xfa, 0x15, 0xc1, 0xfa, 0x79, 0x27, 0xa6, 0xb2, 0x23, 0x4e, + 0x96, 0xae, 0x26, 0x9b, 0xb1, 0xb9, 0x0a, 0x13, 0x7a, 0x97, 0xea, 0xb1, 0x90, 0x25, 0x28, 0x5a, + 0xf5, 0xaa, 0x5d, 0xbf, 0x57, 0xbd, 0x59, 0x36, 0x48, 0x19, 0x4a, 0xe2, 0xd7, 0x9c, 0x3d, 0xf7, + 0xc6, 0xed, 0x72, 0x4e, 0x83, 0xbc, 0x71, 0x73, 0xae, 0x9c, 0x37, 0xff, 0xd0, 0x80, 0xa2, 0x6c, + 0x1f, 0xb9, 0x0d, 0xf9, 0x7a, 0xfd, 0x5e, 0xea, 0x04, 0x3c, 0x59, 0x7a, 0xf9, 0x22, 0x13, 0x86, + 0x7b, 0xea, 0x22, 0x53, 0xaf, 0xdf, 0x63, 0x74, 0xeb, 0xcb, 0x75, 0x61, 0xb4, 0x64, 0x88, 0xeb, + 0x54, 0x9f, 0x63, 0xc1, 0xdb, 0x90, 0xff, 0x60, 0x6b, 0x5d, 0xec, 0x86, 0x32, 0xc6, 0x17, 0xe9, + 0x3e, 0x7a, 0xa4, 0x2e, 0x7d, 0x8c, 0xc0, 0xb4, 0x60, 0x4c, 0x99, 0x5a, 0xdc, 0x88, 0x68, 0xf9, + 0x71, 0x94, 0xa0, 0x30, 0x22, 0x18, 0xc4, 0x12, 0x25, 0xcc, 0xe6, 0x59, 0xf6, 0x1b, 0x4e, 0x53, + 0x58, 0x23, 0x68, 0xf3, 0x34, 0x19, 0xc0, 0xe2, 0x70, 0xf3, 0x4f, 0x0c, 0x28, 0xaf, 0x05, 0xfe, + 0x43, 0x8f, 0x69, 0xe0, 0x75, 0x7f, 0x9f, 0xb6, 0x37, 0x6f, 0x92, 0xd7, 0xa5, 0x12, 0xe0, 0x26, + 0xdc, 0x79, 0x46, 0x85, 0x4a, 0xe0, 0xc7, 0x87, 0x15, 0xa8, 0x1f, 0x84, 0x11, 0x6d, 0xb1, 0x72, + 0xa9, 0x08, 0x94, 0x60, 0xcb, 0xdc, 0xe0, 0x01, 0x5c, 0x27, 0x04, 0x5b, 0x56, 0x60, 0x08, 0x9b, + 0xa3, 0xc4, 0xd0, 0x0c, 0x45, 0x0c, 0x60, 0x71, 0xb8, 0xa2, 0xb0, 0xbf, 0x93, 0xeb, 0xf9, 0x86, + 0xb9, 0xcf, 0x54, 0x10, 0x94, 0xfe, 0x71, 0x03, 0x2d, 0x62, 0x5f, 0x86, 0x99, 0x74, 0x97, 0xa0, + 0x5f, 0xa4, 0x0a, 0x93, 0x3a, 0x5c, 0xba, 0x48, 0xce, 0x67, 0xd6, 0xb5, 0x39, 0x67, 0xa5, 0xf1, + 0xcd, 0x1f, 0x1a, 0x30, 0x8a, 0xff, 0x5a, 0xdd, 0x26, 0x65, 0x96, 0x4d, 0x75, 0xab, 0x2e, 0x8e, + 0x21, 0xd5, 0x53, 0x3d, 0xe7, 0x51, 0x68, 0x8b, 0x33, 0x4b, 0x4d, 0x8f, 0xc4, 0xc8, 0x82, 0x94, + 0x1f, 0xba, 0xca, 0x43, 0xad, 0x98, 0x94, 0x9f, 0xce, 0x86, 0x29, 0x52, 0x81, 0xcc, 0xc6, 0x8f, + 0xfd, 0xf2, 0x9b, 0xd2, 0x35, 0x8c, 0xe3, 0x87, 0x74, 0xbe, 0x76, 0x74, 0x22, 0xd1, 0xc8, 0xeb, + 0x30, 0xcc, 0xaa, 0xb6, 0xe4, 0xc1, 0x08, 0xee, 0x2a, 0xb0, 0x8d, 0x81, 0x76, 0x06, 0xcc, 0x91, + 0xcc, 0x7f, 0x9e, 0x4b, 0x77, 0xa0, 0xb0, 0x02, 0x4e, 0x39, 0x37, 0xde, 0x82, 0xa1, 0x6a, 0xb3, + 0xe9, 0x3f, 0x12, 0x5a, 0x42, 0xba, 0x69, 0xe2, 0xfe, 0xe3, 0x2b, 0xac, 0xc3, 0x50, 0xb4, 0xe0, + 0x03, 0x06, 0x20, 0xf3, 0x30, 0x5a, 0xdd, 0xaa, 0x2f, 0x2d, 0x2d, 0xac, 0xaf, 0x2f, 0x8b, 0x18, + 0xf7, 0x97, 0x64, 0xff, 0x78, 0x9e, 0x6b, 0x47, 0x51, 0xb3, 0x4f, 0x08, 0x6c, 0x42, 0x47, 0xde, + 0x01, 0xf8, 0xc0, 0xf7, 0xda, 0x0f, 0x68, 0xb4, 0xe7, 0xbb, 0xe2, 0xe3, 0x99, 0x49, 0x31, 0xf6, + 0x91, 0xef, 0xb5, 0xed, 0x16, 0x82, 0x59, 0xdb, 0x13, 0x24, 0x4b, 0xf9, 0x9f, 0xf5, 0x74, 0xcd, + 0x8f, 0xd0, 0x86, 0x19, 0x4a, 0x7a, 0x7a, 0xdb, 0x8f, 0xd2, 0xe7, 0x36, 0x12, 0xcd, 0xfc, 0x95, + 0x1c, 0x4c, 0xf0, 0x9d, 0x2a, 0x17, 0x98, 0xa7, 0x76, 0x32, 0xbe, 0xa5, 0x4d, 0xc6, 0x0b, 0x72, + 0x61, 0x50, 0x3e, 0x6d, 0xa0, 0xa9, 0xb8, 0x07, 0xa4, 0x97, 0x86, 0x58, 0xd2, 0x9f, 0x32, 0xc8, + 0x2c, 0xbc, 0x99, 0x44, 0x0c, 0x84, 0x48, 0x64, 0xa3, 0x2a, 0x0c, 0x2d, 0x8d, 0x87, 0xf9, 0xcb, + 0x39, 0x18, 0x57, 0xec, 0xc9, 0xa7, 0xb6, 0xe3, 0xbf, 0xa8, 0x75, 0xbc, 0x3c, 0x83, 0x50, 0xbe, + 0x6c, 0xa0, 0x7e, 0xef, 0xc2, 0x54, 0x0f, 0x49, 0xda, 0x2c, 0x37, 0x06, 0x31, 0xcb, 0x5f, 0xeb, + 0x8d, 0x3e, 0xe0, 0xf1, 0xf0, 0x71, 0xf4, 0x81, 0x1a, 0xee, 0xf0, 0xed, 0x1c, 0xcc, 0x88, 0x5f, + 0xd5, 0xae, 0xeb, 0x45, 0xf3, 0x7e, 0x7b, 0xc7, 0xdb, 0x7d, 0x6a, 0xc7, 0xa2, 0xaa, 0x8d, 0x45, + 0x45, 0x1f, 0x0b, 0xe5, 0x03, 0xfb, 0x0f, 0x89, 0xf9, 0xaf, 0x8a, 0x30, 0xdb, 0x8f, 0x80, 0x6d, + 0xfb, 0x95, 0x5d, 0x15, 0x6e, 0xfb, 0x53, 0x3b, 0x56, 0xbe, 0x9f, 0x4a, 0x42, 0x78, 0x72, 0x03, + 0x84, 0xf0, 0x2c, 0x43, 0x19, 0xab, 0xaa, 0xd3, 0x90, 0x75, 0x42, 0x98, 0x04, 0xe3, 0x5e, 0x3e, + 0x3a, 0xac, 0x5c, 0x72, 0x58, 0x99, 0x1d, 0x8a, 0x42, 0xbb, 0x1b, 0x78, 0x0a, 0x8f, 0x1e, 0x4a, + 0xf2, 0xbb, 0x06, 0x4c, 0x20, 0x70, 0xf1, 0x21, 0x6d, 0x47, 0xc8, 0xac, 0x20, 0x0e, 0x69, 0xe2, + 0x3b, 0x4f, 0xf5, 0x28, 0xf0, 0xda, 0xbb, 0xe8, 0x48, 0x0a, 0x6b, 0xdb, 0xac, 0x17, 0x7e, 0x70, + 0x58, 0x79, 0xfb, 0xe3, 0xdc, 0xa3, 0x12, 0xac, 0x42, 0xb6, 0x91, 0xe7, 0x0d, 0xa5, 0x58, 0x6d, + 0xaa, 0x99, 0xa9, 0x16, 0x91, 0x9f, 0x84, 0xf3, 0x3c, 0x0e, 0x63, 0xde, 0x6f, 0x47, 0x5e, 0xbb, + 0xeb, 0x77, 0xc3, 0x9a, 0xd3, 0xd8, 0xef, 0x76, 0x42, 0xe1, 0xec, 0xc4, 0x2f, 0x6f, 0xc4, 0x85, + 0xf6, 0x36, 0x2f, 0x55, 0x58, 0xf6, 0x63, 0x40, 0xee, 0xc1, 0x14, 0x2f, 0xaa, 0x76, 0x23, 0xbf, + 0xde, 0x70, 0x9a, 0x5e, 0x7b, 0x17, 0x7d, 0xa0, 0xc5, 0xda, 0x45, 0xb6, 0xb7, 0x74, 0xba, 0x91, + 0x6f, 0x87, 0x1c, 0xae, 0xf0, 0xeb, 0x25, 0x22, 0x4b, 0x30, 0x69, 0x51, 0xc7, 0x7d, 0xe0, 0x3c, + 0x9e, 0x77, 0x3a, 0x4e, 0xc3, 0x8b, 0x0e, 0x70, 0x67, 0x96, 0xaf, 0x55, 0x8e, 0x0e, 0x2b, 0xcf, + 0x04, 0xd4, 0x71, 0xed, 0x96, 0xf3, 0xd8, 0x6e, 0x88, 0x42, 0x85, 0x59, 0x9a, 0x2e, 0x66, 0xe5, + 0xb5, 0x63, 0x56, 0xa3, 0x69, 0x56, 0x5e, 0xbb, 0x3f, 0xab, 0x84, 0x4e, 0xb2, 0x5a, 0x77, 0x82, + 0x5d, 0x1a, 0x71, 0x27, 0x21, 0x5c, 0x36, 0xae, 0x1a, 0x0a, 0xab, 0x08, 0xcb, 0x6c, 0x74, 0x18, + 0xa6, 0x59, 0x29, 0x74, 0x4c, 0xf2, 0xb6, 0x02, 0x2f, 0xa2, 0xea, 0x17, 0x8e, 0x61, 0xb3, 0xb0, + 0xff, 0xd1, 0x4d, 0xda, 0xef, 0x13, 0x7b, 0x28, 0x13, 0x6e, 0xca, 0x47, 0x96, 0x7a, 0xb8, 0x65, + 0x7f, 0x65, 0x0f, 0x65, 0xcc, 0x4d, 0xfd, 0xce, 0x71, 0xfc, 0x4e, 0x85, 0x5b, 0x9f, 0x0f, 0xed, + 0xa1, 0x24, 0x2b, 0xac, 0xd3, 0x22, 0xda, 0x66, 0x12, 0x2d, 0x9c, 0xa4, 0x13, 0xd8, 0xb4, 0x17, + 0xc5, 0x9e, 0xba, 0x1c, 0xc8, 0x62, 0x3b, 0xc3, 0x65, 0x9a, 0x26, 0xfe, 0xa0, 0x50, 0x1c, 0x2a, + 0x0f, 0x5b, 0x65, 0x2e, 0xf2, 0x11, 0x13, 0x1c, 0xd4, 0xc5, 0xe6, 0x6f, 0xe6, 0xe0, 0x82, 0x54, + 0xc7, 0x34, 0x7a, 0xe4, 0x07, 0xfb, 0x5e, 0x7b, 0xf7, 0x29, 0xd7, 0xaa, 0x77, 0x34, 0xad, 0xfa, + 0x62, 0x6a, 0x85, 0x4b, 0x7d, 0xe5, 0x31, 0xaa, 0xf5, 0xcf, 0x87, 0xe0, 0xd9, 0x63, 0xa9, 0xc8, + 0x87, 0x6c, 0x15, 0xf4, 0x68, 0x3b, 0x5a, 0x72, 0x9b, 0x94, 0x6d, 0xc3, 0xfc, 0x6e, 0x24, 0x9c, + 0xd9, 0x2f, 0x1c, 0x1d, 0x56, 0xa6, 0xf9, 0x55, 0x20, 0xdb, 0x73, 0x9b, 0xd4, 0x8e, 0x78, 0xb1, + 0x36, 0x4c, 0xbd, 0xd4, 0x8c, 0x65, 0x7c, 0x31, 0x71, 0xa9, 0x1d, 0xd1, 0xe0, 0xa1, 0xc3, 0x6f, + 0x44, 0x08, 0x96, 0xfb, 0x94, 0x76, 0x6c, 0x87, 0x95, 0xda, 0x9e, 0x28, 0xd6, 0x59, 0xf6, 0x50, + 0x93, 0x3b, 0x0a, 0xcb, 0x79, 0xb6, 0x39, 0x78, 0xe0, 0x3c, 0x16, 0x16, 0x2f, 0xfa, 0x57, 0x15, + 0x96, 0x3c, 0x0a, 0xb2, 0xe5, 0x3c, 0xb6, 0x7a, 0x49, 0xc8, 0xd7, 0xe0, 0xac, 0x50, 0xdc, 0x4c, + 0x89, 0x05, 0x7e, 0x53, 0x7e, 0x71, 0x01, 0x79, 0xbd, 0x7c, 0x74, 0x58, 0x39, 0x2f, 0xd4, 0xbe, + 0xdd, 0xe0, 0x18, 0x99, 0x5f, 0x9d, 0xcd, 0x85, 0xac, 0xb3, 0x85, 0x2c, 0xd5, 0x1d, 0x0f, 0x68, + 0x18, 0x3a, 0xbb, 0xd2, 0x3a, 0xe6, 0x27, 0x4a, 0x4a, 0x67, 0xda, 0x2d, 0x5e, 0x6e, 0xf5, 0xa5, + 0x24, 0xf7, 0x60, 0x62, 0x8b, 0x6e, 0xab, 0xe3, 0x33, 0x1c, 0x4f, 0xf1, 0xf2, 0x23, 0xba, 0xdd, + 0x7f, 0x70, 0x52, 0x74, 0xc4, 0x83, 0xa9, 0xb5, 0xc0, 0x7f, 0x7c, 0xc0, 0xb6, 0x7a, 0xb4, 0x4d, + 0x03, 0x0c, 0xee, 0x1a, 0x41, 0x77, 0xd5, 0x6c, 0x62, 0x59, 0xea, 0xe5, 0xb5, 0xe7, 0x8f, 0x0e, + 0x2b, 0xcf, 0x76, 0x18, 0xd8, 0x6e, 0x0a, 0xb8, 0x9d, 0xba, 0x17, 0xd8, 0xcb, 0x95, 0xfc, 0x34, + 0x4c, 0x5a, 0x7e, 0x37, 0xf2, 0xda, 0xbb, 0xf5, 0x28, 0x70, 0x22, 0xba, 0xcb, 0x15, 0x79, 0x12, + 0x45, 0x96, 0x2a, 0xe5, 0x8e, 0xe9, 0x80, 0x03, 0xed, 0x50, 0x40, 0x35, 0x4d, 0xaa, 0x13, 0x98, + 0xbf, 0x9e, 0x83, 0x59, 0x31, 0x0c, 0x16, 0x6d, 0xf8, 0x81, 0xfb, 0xf4, 0x4f, 0xfb, 0x45, 0x6d, + 0xda, 0xbf, 0x10, 0xc7, 0x28, 0x65, 0x7d, 0xe4, 0x31, 0xb3, 0xfe, 0x0f, 0x0c, 0xb8, 0x74, 0x1c, + 0x11, 0xeb, 0x9d, 0x38, 0xae, 0x6f, 0xb4, 0x27, 0x7e, 0xaf, 0x03, 0xd3, 0x38, 0x9e, 0xe8, 0x38, + 0x0e, 0xef, 0xf9, 0x61, 0x84, 0xde, 0xbb, 0x9c, 0x16, 0x48, 0x50, 0xf3, 0xfd, 0x26, 0xea, 0xf9, + 0xda, 0x6b, 0x4c, 0x9d, 0xff, 0xe0, 0xb0, 0x02, 0x0c, 0xc4, 0x23, 0xf1, 0xd8, 0x9a, 0xcf, 0x25, + 0x06, 0xfd, 0xd2, 0xa1, 0x8d, 0xd1, 0x1f, 0xfb, 0xf4, 0x20, 0xb4, 0xb2, 0x58, 0xa3, 0x87, 0xa6, + 0xda, 0x8d, 0xf6, 0xd6, 0x02, 0xba, 0x43, 0x03, 0xda, 0x6e, 0xd0, 0xcf, 0x98, 0x87, 0x46, 0xff, + 0xb8, 0x81, 0xb6, 0x27, 0x7f, 0x35, 0x02, 0x33, 0x59, 0x64, 0xac, 0x5f, 0x14, 0x8b, 0x38, 0x7d, + 0x89, 0xfc, 0xff, 0x36, 0xa0, 0x54, 0xa7, 0x0d, 0xbf, 0xed, 0xde, 0x71, 0x1a, 0x91, 0x2f, 0x43, + 0x32, 0x6c, 0xae, 0xd9, 0x18, 0xdc, 0xde, 0xc1, 0x02, 0xcd, 0x33, 0xf0, 0xfe, 0x60, 0x86, 0x68, + 0xc3, 0xc7, 0x40, 0xd8, 0x88, 0xc9, 0x64, 0x52, 0x05, 0x9e, 0x6a, 0x68, 0x95, 0x92, 0x1a, 0x8c, + 0xcf, 0xfb, 0xed, 0x36, 0x65, 0x3f, 0x94, 0xb0, 0x4e, 0x0c, 0xc4, 0x6c, 0xc8, 0x82, 0xb4, 0x87, + 0x40, 0x27, 0x21, 0xb7, 0x20, 0xbf, 0x31, 0x77, 0x47, 0x8c, 0x81, 0x0c, 0x56, 0xdb, 0x98, 0xbb, + 0x83, 0x7b, 0x5d, 0x66, 0x3f, 0x8c, 0x77, 0xe7, 0x76, 0x54, 0x1f, 0xe8, 0xc6, 0xdc, 0x1d, 0xb2, + 0x0a, 0x53, 0x16, 0xfd, 0x7a, 0xd7, 0x0b, 0xa8, 0x98, 0x00, 0x0f, 0xee, 0x54, 0x71, 0x2c, 0x8a, + 0x5c, 0x8f, 0x05, 0xbc, 0x50, 0xda, 0xf6, 0x76, 0x6b, 0x47, 0xbd, 0x38, 0xd9, 0x4b, 0x4b, 0x7e, + 0x1e, 0xce, 0x2e, 0x78, 0xa1, 0x68, 0x33, 0x77, 0x3e, 0xba, 0x78, 0x0e, 0x39, 0xdc, 0x67, 0x3a, + 0x7c, 0x21, 0x73, 0x3a, 0x3c, 0xef, 0xc6, 0x4c, 0x6c, 0xee, 0xd9, 0x74, 0xd3, 0xf1, 0xb0, 0xd9, + 0xf5, 0x90, 0x8f, 0x60, 0x02, 0xbd, 0x3d, 0xe8, 0x8f, 0xc5, 0x78, 0xf3, 0x91, 0x3e, 0x35, 0x7f, + 0x2e, 0xb3, 0xe6, 0x8b, 0xe8, 0x3c, 0xb2, 0xd1, 0xab, 0x8b, 0xb1, 0xe9, 0xda, 0x1e, 0x41, 0xe3, + 0x4c, 0x3e, 0x80, 0x49, 0xb1, 0xe8, 0xac, 0xee, 0xac, 0xef, 0xd1, 0x05, 0xe7, 0x40, 0x04, 0x21, + 0xa0, 0xfd, 0x27, 0x56, 0x2a, 0xdb, 0xdf, 0xb1, 0xa3, 0x3d, 0x6a, 0xbb, 0x8e, 0xa6, 0x9e, 0x53, + 0x84, 0xe4, 0x67, 0x61, 0x6c, 0xd9, 0xc7, 0x83, 0x27, 0x54, 0x35, 0xa3, 0xc8, 0xe7, 0xcb, 0x78, + 0x71, 0x9a, 0x83, 0x53, 0x8b, 0xc8, 0x8f, 0x0f, 0x2b, 0x6f, 0x9d, 0x56, 0x0a, 0x95, 0x0a, 0x2c, + 0xb5, 0x36, 0x32, 0x0f, 0xc5, 0x2d, 0xba, 0xcd, 0xbe, 0x36, 0x7d, 0xe9, 0x4f, 0x82, 0xb9, 0xbe, + 0x78, 0x24, 0x7e, 0xa9, 0xa7, 0x3a, 0x12, 0x83, 0x04, 0x30, 0x85, 0xfd, 0xb3, 0xe6, 0x84, 0xe1, + 0x23, 0x3f, 0x70, 0x9b, 0x34, 0x94, 0xc7, 0x23, 0xbd, 0x9d, 0x3f, 0x97, 0xd9, 0xf9, 0x97, 0x78, + 0xe7, 0x77, 0x14, 0x0e, 0xaa, 0xb8, 0xf5, 0xb0, 0x37, 0xff, 0x85, 0x81, 0x52, 0x4f, 0xae, 0x61, + 0xf0, 0x59, 0x1c, 0xd5, 0x8e, 0xbb, 0x59, 0xa7, 0x93, 0xba, 0x0b, 0xc0, 0x51, 0xd8, 0xd6, 0xf7, + 0x8e, 0xd3, 0xa0, 0x91, 0xf4, 0x91, 0x22, 0xf2, 0x0e, 0x42, 0xd4, 0xad, 0x2f, 0xc7, 0x21, 0x5f, + 0x82, 0x99, 0x05, 0xfa, 0xd0, 0x6b, 0xd0, 0x6a, 0x14, 0xd1, 0x90, 0xf7, 0xf0, 0x7c, 0x95, 0x1f, + 0x26, 0x8e, 0xd6, 0x5e, 0x3c, 0x3a, 0xac, 0x5c, 0x76, 0xb1, 0xdc, 0x76, 0x12, 0x04, 0xbb, 0xe1, + 0xa8, 0xbc, 0x32, 0x39, 0x98, 0xff, 0xc3, 0x48, 0x7a, 0x9d, 0xbc, 0x0c, 0x05, 0x6b, 0x2d, 0x6e, + 0x3f, 0x3f, 0x27, 0x4c, 0x35, 0x1f, 0x11, 0xc8, 0x57, 0xe0, 0xac, 0xc2, 0x07, 0x7b, 0x84, 0xba, + 0xac, 0x41, 0xfc, 0x63, 0x5e, 0xc2, 0x83, 0x21, 0xa5, 0x25, 0x0e, 0xc7, 0x48, 0xb5, 0x28, 0x9b, + 0x07, 0xfb, 0x58, 0xa5, 0x60, 0x81, 0xb6, 0x3d, 0xce, 0x5b, 0xf9, 0x58, 0x95, 0xb7, 0x8b, 0x08, + 0xe9, 0x8f, 0xcd, 0xe2, 0xf0, 0x41, 0xa1, 0x58, 0x28, 0x0f, 0x99, 0x7f, 0x6d, 0x28, 0x59, 0x2f, + 0x9e, 0xd2, 0x15, 0xeb, 0xb6, 0xb6, 0x62, 0xcd, 0x08, 0xd2, 0xf8, 0xab, 0x58, 0x59, 0xa6, 0x95, + 0x31, 0x09, 0xe3, 0x1a, 0x12, 0x86, 0xd9, 0x6e, 0x84, 0x34, 0xe0, 0x3e, 0xc9, 0xcf, 0x56, 0x98, + 0x6d, 0xfc, 0x5d, 0x03, 0x45, 0x4f, 0xfe, 0x85, 0x01, 0x93, 0x29, 0x0a, 0xd6, 0x1b, 0x0c, 0xa4, + 0xf6, 0x46, 0x37, 0xa4, 0x81, 0x85, 0x50, 0x1e, 0x94, 0xb7, 0xac, 0x07, 0xe5, 0x35, 0x2d, 0x06, + 0x23, 0xef, 0xc3, 0xd0, 0x06, 0xee, 0x20, 0xf4, 0xb8, 0x8e, 0x98, 0x3f, 0x16, 0xf2, 0x19, 0xd6, + 0x65, 0xff, 0xaa, 0x0a, 0x02, 0xcb, 0x48, 0x1d, 0x46, 0xe6, 0x03, 0x8a, 0xf9, 0x2d, 0x0a, 0x83, + 0x1f, 0xc0, 0x35, 0x38, 0x49, 0xfa, 0x00, 0x4e, 0x70, 0x32, 0x7f, 0x2d, 0x07, 0x24, 0xf9, 0x46, + 0xda, 0x08, 0x68, 0x14, 0x3e, 0xb5, 0x83, 0xfe, 0x9e, 0x36, 0xe8, 0xcf, 0xf6, 0x0c, 0x3a, 0xff, + 0xbc, 0x81, 0xc6, 0xfe, 0x4f, 0x0c, 0x38, 0x97, 0x4d, 0x48, 0x5e, 0x80, 0xe1, 0xd5, 0xf5, 0x35, + 0x19, 0x1a, 0x24, 0x3e, 0xc5, 0xef, 0xa0, 0x65, 0x6c, 0x89, 0x22, 0xf2, 0x3a, 0x0c, 0x7f, 0x68, + 0xcd, 0xb3, 0x25, 0x53, 0xb9, 0x3d, 0xf3, 0xf5, 0xc0, 0x6e, 0xe8, 0x5b, 0x2e, 0x81, 0xa4, 0x8e, + 0x6d, 0xfe, 0x89, 0x8d, 0xed, 0xb7, 0x73, 0x30, 0x59, 0x6d, 0x34, 0x68, 0x18, 0x32, 0x83, 0x88, + 0x86, 0xd1, 0x53, 0x3b, 0xb0, 0xd9, 0x41, 0x3f, 0xda, 0xb7, 0x0d, 0x34, 0xaa, 0x7f, 0x6a, 0xc0, + 0x59, 0x49, 0xf5, 0xd0, 0xa3, 0x8f, 0xd6, 0xf7, 0x02, 0x1a, 0xee, 0xf9, 0x4d, 0x77, 0xd0, 0x7b, + 0x60, 0xb8, 0x4a, 0x7b, 0xcd, 0x88, 0x06, 0xaa, 0x83, 0x7a, 0x07, 0x21, 0xda, 0x2a, 0x8d, 0x10, + 0x72, 0x03, 0x46, 0xaa, 0x9d, 0x4e, 0xe0, 0x3f, 0xe4, 0xd3, 0x7e, 0x5c, 0x9c, 0x47, 0x72, 0x90, + 0x76, 0x7e, 0xc9, 0x41, 0xac, 0x19, 0x0b, 0xb4, 0xcd, 0x23, 0x9a, 0xc7, 0x79, 0x33, 0x5c, 0xda, + 0x56, 0x2d, 0x34, 0x2c, 0x37, 0xbf, 0x55, 0x80, 0x92, 0xfa, 0x21, 0xc4, 0x84, 0x61, 0x1e, 0x9e, + 0xa2, 0x86, 0x09, 0x38, 0x08, 0xb1, 0x44, 0x49, 0x12, 0xf5, 0x93, 0x3b, 0x31, 0xea, 0x67, 0x0b, + 0xc6, 0xd7, 0x02, 0xbf, 0xe3, 0x87, 0xd4, 0xe5, 0x29, 0x8a, 0xb8, 0xd6, 0x9a, 0x8e, 0x43, 0x61, + 0x79, 0x9f, 0xb3, 0x22, 0xbe, 0x1d, 0xe8, 0x08, 0x6c, 0x3b, 0x9d, 0xc0, 0x48, 0xe7, 0xc3, 0x1d, + 0xfc, 0x4e, 0x28, 0xae, 0x0b, 0xc4, 0x0e, 0x7e, 0x06, 0xd1, 0x1d, 0xfc, 0x0c, 0xa2, 0x4e, 0x8b, + 0xa1, 0x27, 0x35, 0x2d, 0xc8, 0xaf, 0x19, 0x30, 0x56, 0x6d, 0xb7, 0x45, 0xd4, 0x8f, 0xbc, 0xe7, + 0x7f, 0x36, 0x71, 0xf2, 0xf3, 0xb0, 0x50, 0xee, 0xe3, 0xff, 0xaa, 0xf0, 0xf1, 0xbf, 0xf5, 0xb1, + 0x7c, 0xfc, 0xeb, 0x81, 0xe3, 0x45, 0x21, 0x1e, 0xe6, 0x26, 0x15, 0xaa, 0xa1, 0xbf, 0x4a, 0x3b, + 0xc8, 0x5b, 0x50, 0x8e, 0xe5, 0x71, 0xa9, 0xed, 0xd2, 0xc7, 0x94, 0x07, 0x49, 0x8d, 0xf3, 0x1b, + 0x86, 0xda, 0xe1, 0x45, 0x1a, 0xd1, 0xfc, 0xb6, 0x01, 0xe7, 0x54, 0x81, 0xa8, 0x77, 0xb7, 0x5b, + 0x1e, 0x6e, 0x7f, 0xc8, 0x75, 0x18, 0x15, 0xe3, 0x15, 0x1b, 0x72, 0xbd, 0x79, 0xad, 0x12, 0x14, + 0xb2, 0xc8, 0x86, 0x88, 0xf1, 0x10, 0xbe, 0x82, 0xe9, 0xd4, 0x74, 0x63, 0x45, 0xb5, 0x59, 0xd1, + 0xd9, 0xe5, 0x00, 0x7f, 0xeb, 0x63, 0xc7, 0x20, 0xe6, 0xbb, 0x30, 0xa5, 0xb7, 0xb2, 0x4e, 0xf1, + 0x0a, 0x9a, 0xfc, 0x34, 0x23, 0xfb, 0xd3, 0x64, 0xb9, 0xb9, 0x05, 0xa4, 0x87, 0x3e, 0xc4, 0x83, + 0x2a, 0x1a, 0xc9, 0x83, 0x54, 0xe9, 0xee, 0xea, 0x41, 0x8c, 0x33, 0xbc, 0x8d, 0xa9, 0xdd, 0x8d, + 0xa4, 0xe6, 0x5f, 0x02, 0x4c, 0x67, 0xa8, 0x8e, 0x13, 0x96, 0xf6, 0x8a, 0x3e, 0x79, 0x46, 0xe3, + 0x88, 0x00, 0x39, 0x65, 0xde, 0x95, 0xd9, 0xbc, 0x8e, 0x99, 0x2a, 0xc7, 0xa5, 0xf8, 0xfa, 0x34, + 0x96, 0x77, 0x35, 0x68, 0x67, 0xe8, 0x89, 0x05, 0xed, 0xd4, 0x60, 0x5c, 0x7c, 0x95, 0x98, 0xca, + 0xc3, 0x89, 0x5b, 0x20, 0xe0, 0x05, 0x76, 0xcf, 0x94, 0xd6, 0x49, 0x38, 0x8f, 0xd0, 0x6f, 0x3e, + 0xa4, 0x82, 0xc7, 0x88, 0xca, 0x03, 0x0b, 0x32, 0x79, 0x28, 0x24, 0xe4, 0x1f, 0x1b, 0x40, 0x04, + 0x44, 0x9d, 0xcf, 0xc5, 0xe3, 0xe6, 0xb3, 0xfb, 0x64, 0xe6, 0xf3, 0xb3, 0xb2, 0x8d, 0xd9, 0xf3, + 0x3a, 0xa3, 0x59, 0xe4, 0x1f, 0x1a, 0x30, 0xc5, 0x23, 0x47, 0xd4, 0xc6, 0x8e, 0x1e, 0xd7, 0xd8, + 0xc6, 0x93, 0x69, 0xec, 0xa5, 0x10, 0xab, 0xed, 0xd3, 0xd6, 0xde, 0x46, 0x91, 0x9f, 0x04, 0x88, + 0x67, 0x94, 0x8c, 0x50, 0xbc, 0x94, 0xa1, 0x05, 0x62, 0xa4, 0xe4, 0x92, 0x65, 0x14, 0xd3, 0xa9, + 0x31, 0x3d, 0x09, 0x37, 0xf2, 0xf3, 0x30, 0xc3, 0xe6, 0x4b, 0x0c, 0x11, 0x71, 0x6e, 0xb3, 0x63, + 0x58, 0xcb, 0xe7, 0xfb, 0x2f, 0xed, 0xd7, 0xb3, 0xc8, 0xf8, 0x3d, 0x91, 0x24, 0xd1, 0x42, 0xd4, + 0x52, 0xb7, 0x7c, 0x59, 0x14, 0x18, 0xd0, 0x8a, 0xad, 0x0f, 0x67, 0x4b, 0x58, 0x67, 0xa6, 0x7e, + 0xbb, 0x20, 0xe7, 0x02, 0xd7, 0x6f, 0xa1, 0x7e, 0xd1, 0x03, 0x41, 0xe4, 0x43, 0x20, 0xf5, 0xee, + 0xee, 0x2e, 0x0d, 0x23, 0xea, 0x72, 0x18, 0x0d, 0xc2, 0xd9, 0x71, 0xd4, 0x0f, 0xe8, 0xa6, 0x0a, + 0x65, 0xa9, 0x1d, 0xc8, 0x62, 0x55, 0x48, 0x7a, 0x89, 0x09, 0x85, 0x19, 0xf1, 0xd1, 0x0c, 0x2a, + 0xd3, 0x14, 0x84, 0xb3, 0x13, 0x5a, 0x14, 0x61, 0x52, 0x52, 0x7b, 0x4e, 0xe6, 0xb1, 0x53, 0x72, + 0x1d, 0x68, 0xdb, 0xde, 0x2c, 0x76, 0x17, 0xb7, 0xe1, 0x42, 0xdf, 0xde, 0xcc, 0xb8, 0x2b, 0x72, + 0x43, 0xbf, 0x2b, 0x72, 0xa1, 0x9f, 0xd6, 0x0d, 0xd5, 0xfb, 0x22, 0xbf, 0x6d, 0xa4, 0xd4, 0xac, + 0xb0, 0x89, 0x78, 0x7e, 0xc5, 0x7e, 0xeb, 0x50, 0x0e, 0x93, 0x32, 0x70, 0x45, 0x9c, 0x4b, 0x6c, + 0x31, 0xa6, 0x88, 0x55, 0x45, 0x8e, 0x2a, 0xf9, 0x13, 0x6a, 0x5c, 0xf3, 0x9f, 0x1a, 0x40, 0x78, + 0x0b, 0xe7, 0x9d, 0x8e, 0xb3, 0xed, 0x35, 0xbd, 0xc8, 0xa3, 0x21, 0xb9, 0x0f, 0x65, 0xc1, 0xc2, + 0xd9, 0x6e, 0x52, 0x35, 0x0c, 0x4c, 0x9c, 0x13, 0xc7, 0x65, 0x76, 0xda, 0x7a, 0xea, 0x21, 0xec, + 0x23, 0x23, 0xb9, 0x4f, 0x20, 0x23, 0xe6, 0x8f, 0x0c, 0xb8, 0xd0, 0xdb, 0x6c, 0x51, 0x73, 0xdc, + 0x79, 0xc6, 0x09, 0x9d, 0x97, 0xf5, 0x95, 0x39, 0xf4, 0xb0, 0x3e, 0xb1, 0xaf, 0xcc, 0x27, 0x0e, + 0xdb, 0xd3, 0x7f, 0xe5, 0x23, 0x35, 0xa9, 0x07, 0x79, 0x3d, 0x2b, 0xa0, 0x87, 0xdf, 0xba, 0xe1, + 0x60, 0x3d, 0x96, 0x47, 0xee, 0x72, 0x72, 0x99, 0xbb, 0x1c, 0x79, 0x81, 0x28, 0x9f, 0x75, 0x81, + 0xc8, 0xfc, 0xa5, 0x1c, 0x94, 0xd6, 0x9a, 0xdd, 0x5d, 0xaf, 0xbd, 0xe0, 0x44, 0xce, 0x53, 0xbb, + 0x65, 0x7a, 0x53, 0xdb, 0x32, 0xc5, 0x11, 0x67, 0xf1, 0x87, 0x0d, 0x96, 0x64, 0xcf, 0x80, 0xc9, + 0x84, 0x84, 0xab, 0x87, 0x7b, 0x50, 0x60, 0x3f, 0x84, 0x05, 0x76, 0xb9, 0x87, 0x31, 0x62, 0x5d, + 0x8f, 0xff, 0x13, 0x9b, 0x18, 0x3d, 0xb5, 0x21, 0x72, 0xb8, 0xf8, 0x05, 0x9e, 0x99, 0xec, 0xf4, + 0x59, 0x54, 0xff, 0xc8, 0x80, 0x72, 0xfa, 0x4b, 0xc8, 0x7d, 0x18, 0x61, 0x9c, 0xbc, 0x38, 0xcb, + 0xd9, 0x8b, 0x7d, 0xbe, 0xf9, 0xba, 0x40, 0xe3, 0xcd, 0xc3, 0xce, 0xa7, 0x1c, 0x62, 0x49, 0x0e, + 0x17, 0x2d, 0x28, 0xa9, 0x58, 0x19, 0xad, 0x7b, 0x4d, 0xd7, 0x89, 0xe7, 0xb2, 0xfb, 0x41, 0x6d, + 0xf5, 0x6f, 0x69, 0xad, 0x16, 0xda, 0x70, 0xd0, 0x74, 0x95, 0x78, 0xe5, 0x8e, 0x4f, 0x07, 0x55, + 0xce, 0xa4, 0xd6, 0xd7, 0xaf, 0xdc, 0x71, 0x18, 0xdb, 0x6b, 0xf1, 0xfa, 0x84, 0x9c, 0xe1, 0x5e, + 0xab, 0x83, 0x10, 0xd5, 0x5e, 0xe7, 0x38, 0xe6, 0xdf, 0xc9, 0xc3, 0xb9, 0xa4, 0x79, 0x3c, 0x79, + 0xe7, 0x9a, 0x13, 0x38, 0xad, 0xf0, 0x84, 0x19, 0x70, 0xb5, 0xa7, 0x69, 0x78, 0xa5, 0x5c, 0x36, + 0x4d, 0x69, 0x90, 0x99, 0x6a, 0x10, 0x6e, 0x52, 0x79, 0x83, 0x64, 0x33, 0xc8, 0x7d, 0xc8, 0xd7, + 0x69, 0x24, 0x2e, 0x9e, 0x5e, 0xe9, 0xe9, 0x55, 0xb5, 0x5d, 0xd7, 0xeb, 0x34, 0xe2, 0x83, 0xc8, + 0x63, 0xf7, 0xa9, 0x16, 0x4b, 0xcf, 0xb6, 0x1b, 0x5b, 0x30, 0xbc, 0xf8, 0xb8, 0x43, 0x1b, 0x91, + 0xb8, 0x6f, 0xfa, 0xca, 0xf1, 0xfc, 0x38, 0xae, 0x72, 0xab, 0x95, 0x22, 0x40, 0xed, 0x2c, 0x8e, + 0x72, 0xf1, 0x36, 0x14, 0x65, 0xe5, 0xa7, 0xba, 0x9d, 0xf9, 0x26, 0x8c, 0x29, 0x95, 0x9c, 0x4a, + 0xe8, 0xff, 0xc6, 0x80, 0x61, 0xa6, 0x6d, 0x37, 0xdf, 0x78, 0x4a, 0x35, 0xd2, 0x2d, 0x4d, 0x23, + 0x4d, 0x29, 0xd7, 0x88, 0x70, 0x5e, 0xbe, 0x71, 0x82, 0x2e, 0x3a, 0x34, 0x00, 0x12, 0x64, 0x72, + 0x17, 0x46, 0x44, 0xbe, 0x18, 0x91, 0x19, 0x57, 0xbd, 0x97, 0x24, 0x33, 0x87, 0xc5, 0x56, 0x9c, + 0xdf, 0x49, 0x9b, 0xbd, 0x92, 0x9a, 0x2c, 0x24, 0xb1, 0xdb, 0xea, 0x45, 0x58, 0xc6, 0x66, 0xde, + 0x6f, 0xf3, 0x7b, 0x2a, 0x4a, 0x0e, 0xb2, 0x3e, 0x41, 0xdc, 0x55, 0xe1, 0xb8, 0xc9, 0x1f, 0xc7, + 0xe4, 0x9c, 0x60, 0x92, 0xed, 0xd3, 0xf9, 0xab, 0x12, 0xbf, 0xf9, 0x21, 0x1b, 0xf6, 0x0e, 0x94, + 0xee, 0xf8, 0xc1, 0x23, 0x27, 0x70, 0xab, 0xbb, 0x54, 0x44, 0xdd, 0x17, 0x31, 0x74, 0x7e, 0x7c, + 0x87, 0xc3, 0x6d, 0x87, 0x15, 0xfc, 0xf8, 0xb0, 0x52, 0xa8, 0xf9, 0x7e, 0xd3, 0xd2, 0xd0, 0xc9, + 0x2a, 0x8c, 0x3f, 0x70, 0x1e, 0x8b, 0x33, 0xd0, 0xf5, 0xf5, 0x65, 0x11, 0xbb, 0xf3, 0xca, 0xd1, + 0x61, 0xe5, 0x42, 0xcb, 0x79, 0x1c, 0x9f, 0x9d, 0xf6, 0x0f, 0x2f, 0xd7, 0xe9, 0x89, 0x07, 0x13, + 0x6b, 0x7e, 0x10, 0x89, 0x4a, 0x98, 0xcd, 0x9e, 0xef, 0x73, 0x8a, 0x76, 0x23, 0xf3, 0x14, 0xed, + 0x02, 0xdb, 0xa8, 0xd8, 0x3b, 0x31, 0xb9, 0x76, 0x5d, 0x51, 0x63, 0x4c, 0xde, 0x81, 0xa9, 0x79, + 0x1a, 0x44, 0xde, 0x8e, 0xd7, 0x70, 0x22, 0x7a, 0xc7, 0x0f, 0x5a, 0x4e, 0x24, 0x1c, 0x46, 0xe8, + 0x30, 0x68, 0x50, 0xce, 0xa9, 0xe5, 0x44, 0x56, 0x2f, 0x26, 0xf9, 0x4a, 0x56, 0x34, 0xd4, 0x10, + 0x7e, 0xfe, 0xeb, 0xcc, 0x1a, 0xc9, 0x88, 0x86, 0xea, 0xd3, 0x05, 0x19, 0x71, 0x51, 0xbb, 0xc7, + 0x1d, 0x25, 0x17, 0x6b, 0x37, 0xc5, 0xb1, 0xf6, 0xc9, 0x47, 0xc5, 0xf1, 0xb8, 0xf5, 0x39, 0x32, + 0x9e, 0x83, 0x7c, 0x6d, 0xed, 0x0e, 0xba, 0x80, 0xc4, 0xd1, 0x2d, 0x6d, 0xef, 0x39, 0xed, 0x06, + 0x1a, 0x51, 0x22, 0x1e, 0x44, 0x55, 0x78, 0xb5, 0xb5, 0x3b, 0xc4, 0x81, 0xe9, 0x35, 0x1a, 0xb4, + 0xbc, 0xe8, 0x4b, 0x37, 0x6f, 0x2a, 0x03, 0x55, 0xc4, 0xa6, 0xdd, 0x10, 0x4d, 0xab, 0x74, 0x10, + 0xc5, 0x7e, 0x7c, 0xf3, 0x66, 0xe6, 0x70, 0xc4, 0x0d, 0xcb, 0xe2, 0x45, 0x16, 0x61, 0xe2, 0x81, + 0xf3, 0x58, 0x1c, 0xf2, 0xc7, 0x7b, 0xd8, 0x3c, 0xde, 0x36, 0x40, 0xc1, 0x6a, 0x24, 0x45, 0xea, + 0x10, 0xeb, 0x44, 0xe4, 0x6d, 0x18, 0x4b, 0xc4, 0x2b, 0xc4, 0xe3, 0xdd, 0x3c, 0x0f, 0x33, 0x55, + 0x84, 0x53, 0xf3, 0x95, 0x29, 0xe8, 0x64, 0x23, 0x76, 0x41, 0x70, 0x4b, 0x18, 0x0f, 0x74, 0x47, + 0x6b, 0x37, 0x54, 0x17, 0x84, 0x83, 0x25, 0xda, 0x67, 0x4d, 0xc6, 0x7b, 0x03, 0x1e, 0x7d, 0x64, + 0xe9, 0x5c, 0x14, 0xcf, 0xc6, 0x5a, 0xe0, 0xb7, 0x3a, 0x11, 0x46, 0x61, 0xa6, 0x3c, 0x1b, 0x1d, + 0x2c, 0xc9, 0xf0, 0x6c, 0x70, 0x92, 0xec, 0xd8, 0x85, 0xf1, 0x4f, 0x10, 0xbb, 0x40, 0xa1, 0xb0, + 0xec, 0x37, 0xf6, 0x31, 0xec, 0x72, 0xb4, 0xf6, 0x21, 0xd3, 0x1f, 0x4d, 0xbf, 0xb1, 0xff, 0xe4, + 0xce, 0xdc, 0x91, 0x3d, 0x59, 0x61, 0xdf, 0xce, 0xc4, 0x4a, 0x54, 0x3d, 0x3b, 0xa9, 0x9d, 0x24, + 0x6a, 0x65, 0xdc, 0x50, 0xe1, 0x52, 0x28, 0x3f, 0xc4, 0xd2, 0xc9, 0x09, 0x85, 0xf2, 0x02, 0x0d, + 0xf7, 0x23, 0xbf, 0x33, 0xdf, 0xf4, 0x3a, 0xdb, 0xbe, 0x13, 0xb8, 0xb3, 0xe5, 0x3e, 0x0a, 0xe3, + 0xe5, 0x4c, 0x85, 0x31, 0xe5, 0x72, 0x7a, 0xbb, 0x21, 0x19, 0x58, 0x3d, 0x2c, 0xc9, 0x57, 0x60, + 0x82, 0xcd, 0x96, 0xc5, 0xc7, 0x11, 0x6d, 0x73, 0x51, 0x9a, 0xc2, 0xa5, 0x7e, 0x46, 0xb9, 0xb8, + 0x19, 0x17, 0x72, 0x21, 0x45, 0xed, 0x41, 0x63, 0x02, 0x55, 0x48, 0x75, 0x56, 0xc4, 0x85, 0xd9, + 0x07, 0xce, 0x63, 0x25, 0xcd, 0x91, 0x22, 0xf5, 0x04, 0x25, 0x16, 0x13, 0xea, 0x31, 0x89, 0xdd, + 0x8f, 0x91, 0xfa, 0x4c, 0x80, 0xbe, 0x9c, 0xc8, 0xcf, 0xc2, 0x79, 0xf1, 0x59, 0x0b, 0x98, 0x2b, + 0xc1, 0x0f, 0x0e, 0xea, 0x7b, 0x4e, 0xc0, 0x26, 0xee, 0xf4, 0xe9, 0x34, 0xac, 0xec, 0x30, 0x57, + 0xf2, 0xb1, 0x43, 0xce, 0xc8, 0xea, 0x57, 0x83, 0xf9, 0x93, 0xa9, 0x61, 0x27, 0x4b, 0x30, 0x22, + 0x70, 0xc5, 0xc2, 0xda, 0x5b, 0xfb, 0xb3, 0x99, 0xb5, 0x8f, 0x88, 0xda, 0x2d, 0x49, 0x6f, 0xfe, + 0xb1, 0x01, 0xe3, 0x5a, 0x8f, 0x92, 0xdb, 0x4a, 0xd4, 0x53, 0x12, 0xad, 0xa8, 0xe1, 0x64, 0x3e, + 0xaa, 0x71, 0x5b, 0x84, 0xba, 0xe5, 0xfa, 0xd3, 0x65, 0x26, 0xb1, 0x3b, 0x76, 0xab, 0x97, 0xe4, + 0x8a, 0x28, 0xf4, 0xc9, 0x15, 0xf1, 0xab, 0xe3, 0x30, 0xa1, 0xaf, 0xe1, 0xcc, 0xa8, 0x5e, 0xf6, + 0x77, 0xbd, 0xb6, 0xf4, 0x09, 0xf0, 0xec, 0x27, 0x08, 0xd1, 0x5e, 0xa8, 0x40, 0x08, 0x79, 0x09, + 0x20, 0x3e, 0x5d, 0x97, 0xdb, 0x7e, 0xf1, 0x9e, 0x86, 0x52, 0x40, 0x7e, 0x0a, 0x60, 0xc5, 0x77, + 0x69, 0x9c, 0x40, 0xe7, 0x18, 0x9f, 0xe0, 0xcb, 0xc2, 0x27, 0x28, 0xde, 0xc0, 0x38, 0x3a, 0xac, + 0x9c, 0x6d, 0xfb, 0x2e, 0xed, 0xcd, 0x9c, 0xa3, 0x70, 0x24, 0x5f, 0x84, 0x21, 0xab, 0xdb, 0xa4, + 0x32, 0x9f, 0xcb, 0x98, 0x9c, 0xd3, 0xdd, 0xa6, 0x92, 0x1b, 0x37, 0xe8, 0xa6, 0x8f, 0x82, 0x18, + 0x80, 0xbc, 0x07, 0xc0, 0xc4, 0x16, 0x93, 0x4c, 0xca, 0x0b, 0xe3, 0xe8, 0x22, 0x50, 0x24, 0x1e, + 0x53, 0x53, 0x6a, 0x95, 0x27, 0x24, 0x64, 0x15, 0x46, 0x84, 0x86, 0x14, 0x47, 0x2d, 0xcf, 0x65, + 0x39, 0xf9, 0x14, 0x33, 0x49, 0x24, 0x58, 0x41, 0xb0, 0xee, 0x77, 0xe3, 0x2e, 0x8e, 0xb7, 0x61, + 0x94, 0xb1, 0xdf, 0x08, 0xa9, 0xb8, 0x46, 0x3e, 0xca, 0xc3, 0x4e, 0x95, 0x06, 0x75, 0x43, 0xdd, + 0xc3, 0x90, 0x10, 0x90, 0xaf, 0x60, 0x4a, 0x24, 0xd1, 0xd5, 0xc7, 0xfa, 0x8a, 0xaf, 0xf4, 0x74, + 0xf5, 0x8c, 0xd3, 0xe9, 0x64, 0xe4, 0x90, 0x8b, 0xf9, 0x91, 0xdd, 0xf8, 0x6a, 0x56, 0x9c, 0x20, + 0xfd, 0x98, 0x0a, 0xae, 0xf5, 0x54, 0x30, 0x2b, 0x6f, 0x1b, 0xf5, 0x26, 0x42, 0xd2, 0xf8, 0x92, + 0x0e, 0x94, 0x13, 0x65, 0x22, 0xea, 0x82, 0xe3, 0xea, 0x7a, 0xbd, 0xa7, 0x2e, 0x75, 0x00, 0x7b, + 0xaa, 0xeb, 0xe1, 0x4e, 0xdc, 0x24, 0x99, 0xb5, 0xa8, 0x6f, 0xec, 0xb8, 0xfa, 0x5e, 0xea, 0xa9, + 0x6f, 0xda, 0xdd, 0xee, 0xad, 0x27, 0xc5, 0x93, 0xbc, 0x0d, 0xe3, 0x12, 0x82, 0xf3, 0x03, 0x7d, + 0xb4, 0x62, 0x0b, 0xe3, 0x6e, 0x63, 0xac, 0xa1, 0x9e, 0x05, 0x48, 0x45, 0x56, 0xa9, 0xb9, 0x74, + 0x8c, 0x6b, 0xd4, 0x69, 0xa9, 0xd0, 0x91, 0xc9, 0x97, 0x61, 0x6c, 0xa9, 0xc5, 0x3e, 0xc4, 0x6f, + 0x3b, 0x11, 0xc5, 0xf5, 0x36, 0xf1, 0x7b, 0x2b, 0x25, 0x8a, 0xa8, 0xf2, 0x74, 0xa3, 0x49, 0x91, + 0x6a, 0xaf, 0x28, 0x14, 0xac, 0xf3, 0xb8, 0x6b, 0x4b, 0xc8, 0x70, 0x28, 0x56, 0xd7, 0x67, 0x33, + 0x7c, 0xcf, 0x0a, 0x7b, 0x5c, 0xae, 0xb8, 0xc7, 0xcc, 0x16, 0x13, 0x42, 0xeb, 0x3c, 0x9d, 0x27, + 0x79, 0x07, 0xc6, 0xc4, 0x45, 0xd8, 0xaa, 0xb5, 0x12, 0xce, 0x96, 0xf1, 0xe3, 0x31, 0x85, 0x9f, + 0xbc, 0x33, 0x6b, 0x3b, 0x41, 0xea, 0x00, 0x32, 0xc1, 0x27, 0x5f, 0x82, 0x99, 0x2d, 0xaf, 0xed, + 0xfa, 0x8f, 0x42, 0xa1, 0xc0, 0x85, 0xa2, 0x9b, 0x4a, 0xc2, 0xac, 0x1e, 0xf1, 0x72, 0x5b, 0x2e, + 0x34, 0x3d, 0x8a, 0x2f, 0x93, 0x03, 0xf9, 0xb9, 0x1e, 0xce, 0x5c, 0x82, 0xc8, 0x71, 0x12, 0x34, + 0xd7, 0x23, 0x41, 0xbd, 0xd5, 0xa7, 0xc5, 0x29, 0xb3, 0x1a, 0xe2, 0x03, 0xd1, 0xcd, 0xaa, 0x0f, + 0x7c, 0xaf, 0x3d, 0x3b, 0xad, 0x3d, 0x3f, 0x14, 0x47, 0x5a, 0x23, 0xde, 0x9a, 0xdf, 0xf4, 0x1a, + 0x07, 0x32, 0x95, 0xaf, 0x6e, 0xb0, 0x7d, 0xe4, 0x6b, 0xfe, 0x93, 0x0c, 0xd6, 0xe4, 0xcb, 0x50, + 0x62, 0x7f, 0x63, 0xeb, 0x76, 0x46, 0x3b, 0xad, 0x54, 0x30, 0x45, 0x3d, 0x38, 0x46, 0x78, 0x53, + 0x37, 0xc3, 0xf0, 0xd5, 0x58, 0x99, 0x3f, 0x32, 0x60, 0x26, 0xab, 0xad, 0x27, 0xa4, 0x45, 0x32, + 0x53, 0x71, 0x0b, 0xe8, 0x7a, 0xe1, 0x71, 0x0b, 0x71, 0xb4, 0x42, 0x05, 0x86, 0xee, 0x7b, 0x6d, + 0x57, 0xc6, 0xd5, 0xe1, 0x72, 0xb8, 0xcf, 0x00, 0x16, 0x87, 0x33, 0x04, 0xbc, 0x83, 0x81, 0xeb, + 0xe5, 0x10, 0x47, 0xc0, 0x8b, 0x1a, 0x16, 0x87, 0x33, 0x04, 0xb6, 0xec, 0xca, 0x65, 0x02, 0x11, + 0xd8, 0x6a, 0x1c, 0x5a, 0x1c, 0x4e, 0xae, 0xc0, 0xc8, 0x6a, 0x7b, 0x99, 0x3a, 0x0f, 0xa9, 0x38, + 0x34, 0x44, 0x57, 0x91, 0xdf, 0xb6, 0x9b, 0x0c, 0x66, 0xc9, 0x42, 0xf3, 0xbb, 0x06, 0x4c, 0xf5, + 0x74, 0xd3, 0xc9, 0x99, 0x9f, 0x8e, 0x3f, 0xa1, 0x1d, 0xe4, 0xfb, 0x78, 0xf3, 0x0b, 0xd9, 0xcd, + 0x37, 0xff, 0xa0, 0x00, 0xe7, 0xfb, 0xac, 0x5a, 0x49, 0x74, 0x85, 0x71, 0x62, 0x74, 0xc5, 0x57, + 0xd9, 0x2a, 0xe1, 0x78, 0xad, 0x70, 0xdd, 0x4f, 0x5a, 0x9c, 0x1c, 0x44, 0x61, 0x99, 0x4c, 0xad, + 0x22, 0xd3, 0x80, 0x5c, 0x68, 0x20, 0x85, 0x1d, 0xf9, 0x3d, 0xfe, 0x78, 0x9d, 0x59, 0x4f, 0x7c, + 0x43, 0xfe, 0x6f, 0x49, 0x7c, 0x83, 0x7e, 0xaa, 0x58, 0x78, 0xa2, 0xa7, 0x8a, 0xd9, 0x07, 0x10, + 0x43, 0x9f, 0xe4, 0x28, 0x6e, 0x1e, 0xc6, 0xeb, 0xd4, 0x09, 0x1a, 0x7b, 0xd5, 0x90, 0x0f, 0xd2, + 0x30, 0x72, 0x43, 0x95, 0x1c, 0x62, 0x81, 0xed, 0x84, 0xbd, 0x63, 0xa1, 0xd1, 0x98, 0xff, 0x3e, + 0x15, 0x96, 0xf1, 0xb7, 0x51, 0x5e, 0x5e, 0x81, 0xa1, 0xad, 0x3d, 0x1a, 0x48, 0x23, 0x19, 0x1b, + 0xf2, 0x88, 0x01, 0xd4, 0x86, 0x20, 0x86, 0xf9, 0xb3, 0x50, 0x52, 0x2b, 0x43, 0x85, 0xc0, 0x7e, + 0x8b, 0x19, 0xc9, 0x15, 0x02, 0x03, 0x58, 0x1c, 0x7e, 0x62, 0x36, 0xb6, 0xa4, 0x17, 0xf2, 0x27, + 0xf5, 0x02, 0xab, 0x1c, 0xe5, 0x4d, 0xa9, 0x1c, 0x7f, 0xab, 0x95, 0x47, 0x0c, 0x60, 0x71, 0xf8, + 0x13, 0xad, 0xfc, 0xdf, 0x18, 0x50, 0xc0, 0x4c, 0x18, 0x6f, 0xc0, 0xa8, 0xf4, 0x67, 0xab, 0xd9, + 0x21, 0xa6, 0xa5, 0xbb, 0x3b, 0xd4, 0x83, 0x6a, 0x04, 0x90, 0x55, 0xb5, 0x49, 0x83, 0x6d, 0x2d, + 0xf6, 0xea, 0x21, 0x03, 0xa8, 0x55, 0x21, 0xc6, 0x29, 0xc6, 0x03, 0xe3, 0xcb, 0xc4, 0x76, 0x94, + 0xab, 0x2c, 0x1e, 0x5f, 0xd6, 0xb3, 0xf7, 0x94, 0x58, 0xe6, 0x6f, 0x18, 0x70, 0x36, 0xd3, 0x92, + 0x61, 0xb5, 0x72, 0x93, 0x49, 0x11, 0xc7, 0xb4, 0xbd, 0xc4, 0x31, 0x4e, 0x13, 0x47, 0x76, 0x0a, + 0xd9, 0x7a, 0x1e, 0x46, 0xe3, 0x1d, 0x26, 0x99, 0x91, 0x43, 0x87, 0x4e, 0x4f, 0xb9, 0x1d, 0xfb, + 0x1b, 0x03, 0x86, 0x59, 0x13, 0x9e, 0xda, 0x6b, 0x45, 0xd9, 0x2e, 0x70, 0xf6, 0x49, 0x03, 0x5d, + 0x26, 0xfa, 0xdd, 0x61, 0x80, 0x04, 0x99, 0x6c, 0xc3, 0xc4, 0xea, 0xd2, 0xc2, 0xfc, 0x92, 0x4b, + 0xdb, 0x11, 0x9e, 0x01, 0xa7, 0xd2, 0x4b, 0xb0, 0xad, 0x71, 0xd0, 0x76, 0x9a, 0x02, 0xe1, 0x20, + 0xd1, 0x0d, 0xbe, 0xe7, 0x36, 0x6c, 0x2f, 0xa6, 0x53, 0x4d, 0x4a, 0x9d, 0x23, 0xab, 0xa3, 0x5e, + 0x7d, 0xb0, 0xac, 0xd4, 0x91, 0x1b, 0xb0, 0x8e, 0xd0, 0x69, 0x35, 0xfb, 0xd4, 0xa1, 0x73, 0x24, + 0x7b, 0x50, 0xbe, 0x8b, 0xab, 0x8f, 0x52, 0x4b, 0xfe, 0xf8, 0x5a, 0x5e, 0x10, 0xb5, 0x3c, 0xc3, + 0x97, 0xad, 0xec, 0x7a, 0x7a, 0xb8, 0x26, 0x92, 0x5b, 0x38, 0x51, 0x72, 0xff, 0x1f, 0x03, 0x86, + 0xf9, 0xf2, 0x16, 0xbf, 0x21, 0x94, 0xb9, 0x80, 0x6e, 0x3d, 0x99, 0x05, 0xb4, 0x8c, 0x9a, 0x4b, + 0x73, 0x21, 0xf0, 0x32, 0xb2, 0x90, 0x7a, 0x90, 0x48, 0x9e, 0x73, 0xa0, 0x69, 0xcd, 0x4b, 0x92, + 0x68, 0x3c, 0xfe, 0x16, 0x91, 0xca, 0x85, 0x63, 0xa8, 0xcf, 0xa3, 0x8e, 0x7c, 0xc2, 0xe7, 0x51, + 0x97, 0x61, 0x54, 0x84, 0x97, 0xd5, 0x0e, 0xc4, 0x06, 0x5a, 0xba, 0x88, 0x62, 0xb8, 0x92, 0x75, + 0x9d, 0x83, 0xec, 0x6d, 0x2d, 0x67, 0x62, 0x8c, 0x48, 0x56, 0x61, 0x34, 0xb9, 0x13, 0x35, 0xaa, + 0x1d, 0x56, 0xc7, 0x70, 0x11, 0x7f, 0xcd, 0xaf, 0xdd, 0x66, 0x5e, 0x81, 0x4a, 0x78, 0x98, 0xdf, + 0x32, 0xa0, 0x9c, 0x96, 0x17, 0xf2, 0x36, 0x8c, 0xc5, 0xd7, 0xd2, 0xe2, 0xe8, 0x13, 0xf4, 0x36, + 0x27, 0xf7, 0xd8, 0xb4, 0x38, 0x14, 0x15, 0x9d, 0xcc, 0x41, 0x91, 0x4d, 0x3b, 0x25, 0x69, 0x36, + 0xea, 0x93, 0xae, 0x80, 0xa9, 0x87, 0xaf, 0x12, 0x4f, 0x99, 0xb5, 0xff, 0x31, 0x0f, 0x63, 0xca, + 0x60, 0x91, 0x57, 0xa0, 0xb8, 0x14, 0x2e, 0xfb, 0x8d, 0x7d, 0xea, 0x8a, 0x33, 0x1d, 0x7c, 0xfd, + 0xd6, 0x0b, 0xed, 0x26, 0x02, 0xad, 0xb8, 0x98, 0xd4, 0x60, 0x9c, 0xff, 0x27, 0xaf, 0x1f, 0xe7, + 0x12, 0x7f, 0x34, 0x47, 0x96, 0x17, 0x8f, 0xd5, 0xe5, 0x5d, 0x23, 0x21, 0x5f, 0x03, 0xe0, 0x00, + 0x36, 0xbe, 0x03, 0x44, 0x97, 0xcb, 0x09, 0x7c, 0x56, 0x54, 0x10, 0x79, 0xea, 0x17, 0xa2, 0x28, + 0x28, 0x0c, 0xf1, 0xe5, 0x4d, 0xbf, 0xb1, 0x3f, 0xf8, 0xdb, 0xbb, 0xc9, 0xcb, 0x9b, 0x7e, 0x63, + 0xdf, 0xce, 0x0e, 0x35, 0x54, 0x59, 0x92, 0x6f, 0x1b, 0x70, 0xd1, 0xa2, 0x0d, 0xff, 0x21, 0x0d, + 0x0e, 0xaa, 0x11, 0x62, 0xa9, 0x35, 0x9e, 0x1c, 0xd7, 0x78, 0x4b, 0xd4, 0xf8, 0x72, 0x20, 0xb8, + 0xe0, 0x9d, 0xa8, 0x56, 0x27, 0xb2, 0x8f, 0x69, 0xc2, 0x31, 0x55, 0x9a, 0x7f, 0x6e, 0x28, 0x53, + 0x80, 0xac, 0xc0, 0x68, 0x2c, 0x2c, 0xc2, 0x65, 0x1a, 0x5b, 0x66, 0x12, 0x6e, 0xd1, 0x9d, 0xda, + 0x33, 0xe2, 0xf8, 0x65, 0x3a, 0x16, 0x39, 0x6d, 0x46, 0x48, 0x20, 0x79, 0x1f, 0x0a, 0x38, 0x54, + 0x27, 0x67, 0x59, 0x93, 0x4b, 0x4d, 0x81, 0x8d, 0x11, 0xb6, 0x1a, 0x29, 0xc9, 0xe7, 0x44, 0x0c, + 0x50, 0x5e, 0xcb, 0x5f, 0xcc, 0x40, 0xac, 0x1d, 0xf1, 0x1a, 0x93, 0x44, 0xb7, 0x2a, 0xd2, 0xfa, + 0xff, 0xe5, 0xa0, 0x9c, 0x9e, 0x78, 0xe4, 0x3d, 0x28, 0xc9, 0xfb, 0x6d, 0xf7, 0x9c, 0x70, 0x4f, + 0xe4, 0x44, 0xc5, 0x5d, 0xab, 0xbc, 0x14, 0x67, 0xef, 0x39, 0x5a, 0xee, 0x3c, 0x8d, 0x80, 0x2d, + 0xc8, 0xeb, 0xe2, 0xd2, 0x84, 0x32, 0x81, 0x22, 0x3f, 0xea, 0xa4, 0x72, 0xa2, 0x4a, 0x34, 0xf2, + 0x06, 0xe4, 0xf9, 0xa5, 0x4f, 0x35, 0xa1, 0xd6, 0x83, 0x3b, 0x55, 0x7e, 0x67, 0x8d, 0x9f, 0xf8, + 0xeb, 0x47, 0x27, 0x0c, 0x9f, 0x2c, 0x2b, 0x57, 0x06, 0x87, 0xb5, 0xc4, 0x42, 0x12, 0x1c, 0x7f, + 0xdc, 0xc9, 0x77, 0x07, 0x3f, 0x28, 0x14, 0xf3, 0xe5, 0x82, 0xb8, 0x24, 0xf6, 0xfb, 0x79, 0x18, + 0x8d, 0xeb, 0x27, 0x04, 0xd0, 0xde, 0x10, 0x47, 0xf7, 0xf8, 0x3f, 0xb9, 0x00, 0x45, 0x69, 0x62, + 0x88, 0xe3, 0xfb, 0x91, 0x50, 0x98, 0x17, 0xb3, 0x20, 0x6d, 0x09, 0x6e, 0x5e, 0x58, 0xf2, 0x27, + 0xb9, 0x09, 0xb1, 0xa1, 0xd0, 0xcf, 0xa2, 0x28, 0xb0, 0x01, 0xb3, 0x62, 0x34, 0x32, 0x01, 0x39, + 0x8f, 0x07, 0xc4, 0x8f, 0x5a, 0x39, 0xcf, 0x25, 0xef, 0x41, 0xd1, 0x71, 0x5d, 0xea, 0xda, 0x4e, + 0x34, 0xc0, 0x3b, 0xc8, 0x45, 0xc6, 0x8d, 0x6b, 0x74, 0xa4, 0xaa, 0x46, 0xa4, 0x0a, 0xa3, 0xf8, + 0x0c, 0x6e, 0x37, 0x1c, 0xe8, 0xed, 0xdc, 0x84, 0x43, 0x91, 0x91, 0x6d, 0x84, 0xd4, 0x25, 0x2f, + 0x43, 0x81, 0x8d, 0xa6, 0x58, 0x0f, 0xe2, 0x34, 0x89, 0xab, 0xeb, 0x6b, 0xbc, 0xc3, 0xee, 0x9d, + 0xb1, 0x10, 0x81, 0xbc, 0x08, 0xf9, 0xee, 0xdc, 0x8e, 0xd0, 0xf4, 0xe5, 0xe4, 0x3e, 0x70, 0x8c, + 0xc6, 0x8a, 0xc9, 0x2d, 0x28, 0x3e, 0xd2, 0x6f, 0x7e, 0x9e, 0x4d, 0x0d, 0x63, 0x8c, 0x1f, 0x23, + 0xd6, 0x8a, 0x30, 0xcc, 0xef, 0x3c, 0x9a, 0xcf, 0x01, 0x24, 0x55, 0xf7, 0x46, 0x59, 0x98, 0x5f, + 0x83, 0xd1, 0xb8, 0x4a, 0xf2, 0x2c, 0xc0, 0x3e, 0x3d, 0xb0, 0xf7, 0x9c, 0xb6, 0x2b, 0xde, 0xf4, + 0x29, 0x59, 0xa3, 0xfb, 0xf4, 0xe0, 0x1e, 0x02, 0xc8, 0x79, 0x18, 0xe9, 0xb0, 0x51, 0x95, 0x19, + 0x7d, 0xad, 0xe1, 0x4e, 0x77, 0x9b, 0x49, 0xe8, 0x2c, 0x8c, 0xa0, 0xf3, 0x43, 0x4c, 0xb4, 0x71, + 0x4b, 0xfe, 0x34, 0x7f, 0x3b, 0x87, 0xb9, 0x1e, 0x94, 0x76, 0x92, 0x17, 0x60, 0xbc, 0x11, 0x50, + 0x5c, 0x8e, 0x1c, 0x66, 0x16, 0x89, 0x7a, 0x4a, 0x09, 0x70, 0xc9, 0x25, 0x57, 0x60, 0x32, 0x49, + 0x31, 0x6c, 0x37, 0xb6, 0xc5, 0xbd, 0xef, 0x92, 0x35, 0xde, 0x91, 0x39, 0x86, 0xe7, 0xb7, 0xf1, + 0x22, 0x47, 0x59, 0xbd, 0xef, 0x18, 0xc9, 0x74, 0xc1, 0xa3, 0xd6, 0xa4, 0x02, 0xc7, 0x83, 0x93, + 0x73, 0x30, 0xec, 0x38, 0xbb, 0x5d, 0x8f, 0x07, 0x95, 0x97, 0x2c, 0xf1, 0x8b, 0xbc, 0x0a, 0x53, + 0xa1, 0xb7, 0xdb, 0x76, 0xa2, 0x6e, 0x20, 0x92, 0x6d, 0xd0, 0x00, 0x45, 0x6a, 0xdc, 0x2a, 0xc7, + 0x05, 0xf3, 0x1c, 0x4e, 0x5e, 0x07, 0xa2, 0xd6, 0xe7, 0x6f, 0x7f, 0x44, 0x1b, 0x5c, 0xd4, 0x4a, + 0xd6, 0x94, 0x52, 0xb2, 0x8a, 0x05, 0xe4, 0x79, 0x28, 0x05, 0x34, 0x44, 0x93, 0x0c, 0xbb, 0x0d, + 0x53, 0x08, 0x59, 0x63, 0x12, 0x76, 0x9f, 0x1e, 0x98, 0x35, 0x98, 0xea, 0x99, 0x8f, 0xe4, 0x75, + 0x6e, 0xdd, 0x8b, 0xf5, 0xb9, 0xc4, 0x37, 0x33, 0xf8, 0xf4, 0x95, 0xfe, 0x72, 0x3a, 0x47, 0x32, + 0xdb, 0x50, 0x52, 0xf5, 0xeb, 0x09, 0x37, 0xea, 0xcf, 0x61, 0xd8, 0x29, 0x57, 0x3e, 0xc3, 0x47, + 0x87, 0x95, 0x9c, 0xe7, 0x62, 0xb0, 0xe9, 0x55, 0x28, 0x4a, 0x2b, 0x41, 0x7d, 0x7f, 0x46, 0x18, + 0x94, 0x07, 0x56, 0x5c, 0x6a, 0xbe, 0x0c, 0x23, 0x42, 0x85, 0x1e, 0xef, 0x88, 0x32, 0xbf, 0x91, + 0x83, 0x49, 0x8b, 0xb2, 0x09, 0x2e, 0x5e, 0x76, 0xf9, 0x8c, 0x25, 0x5b, 0xd6, 0xbe, 0xed, 0x98, + 0x04, 0x16, 0xdf, 0x33, 0x60, 0x3a, 0x03, 0xf7, 0x63, 0x65, 0x67, 0xbb, 0x0d, 0xa3, 0x0b, 0x9e, + 0xd3, 0xac, 0xba, 0x6e, 0x1c, 0x3e, 0x8b, 0xd6, 0xa0, 0xcb, 0xa6, 0x93, 0xc3, 0xa0, 0xea, 0x62, + 0x1a, 0xa3, 0x92, 0x6b, 0x42, 0x28, 0x92, 0xfc, 0x91, 0x32, 0x9d, 0x33, 0xf0, 0x36, 0x25, 0xc9, + 0x9c, 0xf1, 0x2e, 0x24, 0x07, 0x26, 0xa7, 0xb3, 0x4f, 0xed, 0xd0, 0x65, 0xdf, 0x85, 0x4c, 0x7f, + 0xde, 0x40, 0xdb, 0xce, 0x6f, 0xe5, 0xe0, 0x5c, 0x36, 0xe1, 0xc7, 0x4d, 0xb4, 0x87, 0xd9, 0x43, + 0x94, 0x8c, 0xd9, 0x98, 0x68, 0x8f, 0xa7, 0x1a, 0x41, 0xfc, 0x04, 0x81, 0xec, 0xc0, 0xf8, 0xb2, + 0x13, 0x46, 0xf7, 0xa8, 0x13, 0x44, 0xdb, 0xd4, 0x89, 0x06, 0xb0, 0x60, 0xe3, 0xf7, 0xc9, 0x71, + 0x51, 0xdb, 0x93, 0x94, 0xe9, 0xf7, 0xc9, 0x35, 0xb6, 0xb1, 0xa0, 0x14, 0x06, 0x10, 0x94, 0xaf, + 0xc3, 0x64, 0x9d, 0xb6, 0x9c, 0xce, 0x9e, 0x1f, 0x50, 0xe1, 0x3b, 0xbf, 0x0e, 0xe3, 0x31, 0x28, + 0x53, 0x5a, 0xf4, 0x62, 0x0d, 0x5f, 0xe9, 0x88, 0x44, 0x95, 0xe8, 0xc5, 0xe6, 0x6f, 0xe6, 0xe0, + 0x7c, 0xb5, 0x21, 0x0e, 0x1a, 0x44, 0x81, 0x3c, 0x0f, 0xfd, 0x94, 0xeb, 0x26, 0x37, 0x60, 0xf4, + 0x81, 0xf3, 0x78, 0x99, 0xe2, 0x0b, 0xc2, 0x3c, 0x5d, 0x13, 0x37, 0xbf, 0x9c, 0xc7, 0x76, 0xec, + 0xf6, 0xb2, 0x12, 0x1c, 0x75, 0xb3, 0x59, 0xf8, 0x84, 0x9b, 0x4d, 0x13, 0x86, 0xef, 0xf9, 0x4d, + 0x57, 0x2c, 0x4e, 0xe2, 0xdc, 0x62, 0x0f, 0x21, 0x96, 0x28, 0x31, 0x7f, 0x64, 0xc0, 0x44, 0xdc, + 0x62, 0x6c, 0xc2, 0xa7, 0xde, 0x25, 0x57, 0x60, 0x04, 0x2b, 0x8a, 0x5f, 0x47, 0xc2, 0x45, 0xa3, + 0xc9, 0x40, 0xb6, 0xe7, 0x5a, 0xb2, 0x50, 0xed, 0x89, 0xa1, 0x4f, 0xd6, 0x13, 0xe6, 0x3f, 0xc2, + 0x23, 0x11, 0xf5, 0x2b, 0xd9, 0x4a, 0xa4, 0x34, 0xc4, 0x18, 0xb0, 0x21, 0xb9, 0x27, 0x36, 0x24, + 0xf9, 0xbe, 0x43, 0xf2, 0xcd, 0x1c, 0x8c, 0xc5, 0x8d, 0xfd, 0x8c, 0x25, 0x11, 0x88, 0xbf, 0x6b, + 0xa0, 0x10, 0xfa, 0xba, 0xa2, 0x2b, 0x44, 0xa4, 0xfa, 0xfb, 0x30, 0x2c, 0x26, 0x93, 0x91, 0x3a, + 0x17, 0x4c, 0x8d, 0x6e, 0xf2, 0xc6, 0x34, 0x0e, 0x68, 0x68, 0x09, 0x3a, 0xbc, 0xa3, 0xb0, 0x45, + 0xb7, 0xc5, 0x09, 0xd9, 0x53, 0xbb, 0x46, 0x65, 0xdf, 0x51, 0x48, 0x3e, 0x6c, 0xa0, 0xd5, 0xe9, + 0xef, 0x15, 0xa0, 0x9c, 0x26, 0x39, 0x39, 0x4d, 0xc3, 0x5a, 0x77, 0x5b, 0x3c, 0xd0, 0x81, 0x69, + 0x1a, 0x3a, 0xdd, 0x6d, 0x8b, 0xc1, 0xc8, 0x15, 0x28, 0xac, 0x05, 0xde, 0x43, 0xfc, 0x6a, 0xf1, + 0x3e, 0x49, 0x27, 0xf0, 0x1e, 0xaa, 0xc1, 0xba, 0xac, 0x1c, 0x37, 0xb4, 0xcb, 0x75, 0xe5, 0x79, + 0x7f, 0xbe, 0xa1, 0x6d, 0x86, 0xe9, 0x7c, 0x40, 0x12, 0x8d, 0x2d, 0x95, 0x35, 0xea, 0x04, 0x22, + 0xa5, 0x80, 0x50, 0x67, 0xb8, 0x54, 0x6e, 0x23, 0x98, 0x27, 0xfb, 0xb5, 0x54, 0x24, 0xd2, 0x04, + 0xa2, 0xfc, 0x94, 0x13, 0xf8, 0xe4, 0x3d, 0x9e, 0x7c, 0x57, 0x6b, 0x46, 0x65, 0x6d, 0xab, 0xb3, + 0x39, 0x83, 0xef, 0x93, 0xf4, 0x11, 0xae, 0xc1, 0x28, 0xba, 0xbc, 0xd0, 0x91, 0x51, 0x3c, 0x91, + 0x99, 0x0c, 0x8c, 0x06, 0x8c, 0x27, 0xb0, 0x63, 0x77, 0x46, 0xc2, 0x84, 0xbc, 0x0b, 0x63, 0x6a, + 0x34, 0x2f, 0x8f, 0x39, 0xbd, 0xc4, 0xef, 0x8f, 0xf5, 0xc9, 0x9b, 0xa7, 0x12, 0x98, 0x9f, 0x53, + 0xa5, 0x44, 0x2c, 0xda, 0xc7, 0x4a, 0x89, 0xf9, 0xeb, 0x68, 0xc6, 0xb7, 0xfc, 0x88, 0x0a, 0xeb, + 0xe5, 0xa9, 0xd5, 0x63, 0x89, 0x0b, 0x79, 0x48, 0x8b, 0x69, 0xd1, 0xbe, 0xee, 0x14, 0x0f, 0xdb, + 0xff, 0x9e, 0x01, 0x67, 0x33, 0x69, 0xc9, 0x75, 0x80, 0xc4, 0x46, 0x14, 0xbd, 0xc4, 0xb3, 0x28, + 0xc7, 0x50, 0x4b, 0xc1, 0x20, 0x5f, 0x4d, 0x5b, 0x77, 0x27, 0x2f, 0x4e, 0xf2, 0xad, 0x91, 0x09, + 0xdd, 0xba, 0xcb, 0xb0, 0xe9, 0xcc, 0xef, 0xe5, 0x61, 0xaa, 0xe7, 0x8d, 0xca, 0x13, 0xa2, 0x08, + 0xf6, 0x53, 0x2f, 0xa0, 0xf1, 0xe3, 0x8e, 0x6b, 0xfd, 0x5e, 0xc8, 0xcc, 0x78, 0x0f, 0x0d, 0xdd, + 0x62, 0x22, 0x81, 0xf7, 0x09, 0xcf, 0xa2, 0x85, 0xd9, 0x6f, 0xe7, 0xbd, 0xda, 0xb7, 0xb6, 0x27, + 0xf0, 0x86, 0xde, 0xdf, 0xe2, 0x27, 0xc6, 0x7e, 0x3d, 0x07, 0xd3, 0x3d, 0xdf, 0xfc, 0xd4, 0xce, + 0xba, 0xf7, 0xb5, 0xd5, 0xed, 0xb9, 0x7e, 0x63, 0x3a, 0x90, 0x15, 0xf1, 0xdf, 0x0d, 0x38, 0xdf, + 0x87, 0x92, 0x1c, 0xa4, 0x85, 0x88, 0x5b, 0x15, 0x37, 0x8f, 0xaf, 0xf0, 0x89, 0x88, 0xd2, 0xa7, + 0x26, 0x09, 0xdf, 0xc8, 0x01, 0x6c, 0xd1, 0xed, 0xa7, 0x3b, 0x07, 0xd5, 0x17, 0x34, 0x01, 0x50, + 0x1c, 0x98, 0x83, 0xa7, 0xa0, 0x5a, 0x45, 0x47, 0xe2, 0xe0, 0x09, 0xa8, 0xe2, 0xf7, 0x54, 0x72, + 0xd9, 0xef, 0xa9, 0x98, 0xdb, 0x30, 0x73, 0x97, 0x46, 0xc9, 0x4a, 0x28, 0xf7, 0x90, 0xc7, 0xb3, + 0x7d, 0x0d, 0x46, 0x05, 0xbe, 0x9e, 0x1b, 0x5f, 0x86, 0xc4, 0x79, 0xae, 0x95, 0x20, 0x98, 0x14, + 0xce, 0x2f, 0xd0, 0x26, 0x8d, 0xe8, 0xa7, 0x5b, 0x4d, 0x1d, 0x08, 0xff, 0x14, 0xfe, 0xcc, 0xc6, + 0x40, 0x35, 0x9c, 0xd8, 0x3f, 0x9b, 0x70, 0x36, 0x6e, 0xfb, 0x93, 0xe4, 0x7b, 0x83, 0xd9, 0x12, + 0xe2, 0x42, 0x64, 0xc2, 0xf1, 0x18, 0x27, 0xe2, 0x63, 0xb8, 0x28, 0x09, 0xb6, 0xbc, 0xf8, 0x24, + 0x66, 0x20, 0x5a, 0xf2, 0x36, 0x8c, 0x29, 0x34, 0xe2, 0x5a, 0x37, 0x9e, 0x76, 0x3e, 0xf2, 0xa2, + 0x3d, 0x3b, 0xe4, 0x70, 0xf5, 0xb4, 0x53, 0x41, 0x37, 0xbf, 0x02, 0xcf, 0xc4, 0x71, 0x2b, 0x19, + 0x55, 0xa7, 0x98, 0x1b, 0xa7, 0x63, 0xbe, 0x92, 0x7c, 0xd6, 0x52, 0x3b, 0x8e, 0x80, 0x97, 0xbc, + 0x89, 0xfa, 0x59, 0xe2, 0x63, 0x2e, 0x29, 0xb9, 0xf9, 0xc4, 0x5a, 0x94, 0x00, 0xcc, 0xb7, 0x94, + 0xc6, 0x66, 0x30, 0xd4, 0x88, 0x8d, 0x34, 0xf1, 0x37, 0x72, 0x30, 0xb9, 0xba, 0xb4, 0x30, 0x1f, + 0xbb, 0x91, 0x3f, 0x63, 0x09, 0xb2, 0xb4, 0x6f, 0xeb, 0xaf, 0x6f, 0xcc, 0x0d, 0x98, 0x4e, 0x75, + 0x03, 0xbe, 0x22, 0xf4, 0x2e, 0x8f, 0x2f, 0x89, 0xc1, 0x72, 0x65, 0x39, 0x97, 0xc5, 0x7e, 0xf3, + 0x96, 0x95, 0xc2, 0x36, 0xbf, 0x37, 0x9c, 0xe2, 0x2b, 0x54, 0xd8, 0x6b, 0x30, 0xba, 0x14, 0x86, + 0x5d, 0x1a, 0x6c, 0x58, 0xcb, 0xaa, 0x8d, 0xe8, 0x21, 0xd0, 0xee, 0x06, 0x4d, 0x2b, 0x41, 0x20, + 0xaf, 0x40, 0x51, 0x5c, 0xc2, 0x93, 0x3a, 0x01, 0x8f, 0xcb, 0xe3, 0x3b, 0x7c, 0x56, 0x5c, 0x4c, + 0xde, 0x80, 0x12, 0xff, 0x9f, 0x4b, 0x9b, 0xe8, 0x70, 0xf4, 0x55, 0x09, 0x74, 0x2e, 0x9d, 0x96, + 0x86, 0xc6, 0x76, 0x66, 0xf2, 0x99, 0x52, 0xd6, 0xa2, 0x42, 0xb2, 0x33, 0x93, 0x2f, 0x9a, 0x62, + 0x9b, 0x54, 0x24, 0x72, 0x0d, 0xf2, 0xd5, 0x79, 0x4b, 0x4d, 0x07, 0xee, 0x34, 0x02, 0x9e, 0x4e, + 0x5f, 0x7b, 0x09, 0xac, 0x3a, 0x6f, 0x91, 0x39, 0x28, 0xe2, 0x4b, 0x2f, 0x2e, 0x0d, 0x44, 0xd4, + 0x2b, 0x4a, 0x4d, 0x47, 0xc0, 0xd4, 0x93, 0x47, 0x89, 0x47, 0x6e, 0xc0, 0xc8, 0x82, 0x17, 0x76, + 0x9a, 0xce, 0x81, 0xc8, 0x8c, 0x83, 0x87, 0x21, 0x2e, 0x07, 0xa9, 0x72, 0x26, 0xb0, 0xc8, 0x2b, + 0x30, 0x54, 0x6f, 0xf8, 0x1d, 0xb6, 0xdb, 0x8a, 0x43, 0x5b, 0x42, 0x06, 0xd0, 0xf2, 0x5e, 0x30, + 0x00, 0xde, 0x0b, 0xe7, 0xd7, 0xdb, 0x46, 0x95, 0x7b, 0xe1, 0xe9, 0x6b, 0x6d, 0x02, 0xa7, 0x37, + 0xf8, 0x10, 0x9e, 0x64, 0xf0, 0xe1, 0x36, 0x9c, 0xbf, 0x8b, 0xa6, 0x7e, 0x9d, 0x06, 0x98, 0x8c, + 0x94, 0xbf, 0x1a, 0xb5, 0x61, 0x2d, 0x89, 0x2b, 0x7d, 0x78, 0xc1, 0x8a, 0xef, 0x06, 0xec, 0x90, + 0xe3, 0xc8, 0x07, 0xa7, 0x52, 0x4f, 0x65, 0xf4, 0x63, 0x44, 0xbe, 0x04, 0x33, 0x59, 0x45, 0xe2, + 0x72, 0x1f, 0xc6, 0xb5, 0x67, 0x57, 0xa0, 0x06, 0x96, 0x67, 0x71, 0x20, 0xcb, 0x50, 0xe6, 0xf0, + 0xaa, 0xdb, 0xf2, 0xda, 0x8b, 0x2d, 0xc7, 0x6b, 0xe2, 0x55, 0x3f, 0x71, 0x5f, 0x53, 0x70, 0x75, + 0x58, 0xa1, 0x4d, 0x59, 0xa9, 0x16, 0x9d, 0x94, 0xa2, 0x44, 0x75, 0x54, 0xaf, 0x3e, 0x58, 0x4e, + 0xe6, 0xd4, 0x67, 0xeb, 0xdc, 0x48, 0xfb, 0xb6, 0x63, 0xce, 0x8d, 0x36, 0x60, 0x3a, 0xd5, 0x0d, + 0x52, 0x1d, 0x69, 0xe0, 0xb4, 0x3a, 0x4a, 0xd1, 0x58, 0x29, 0x6c, 0xf3, 0x3f, 0x0d, 0xa7, 0xf8, + 0x0a, 0x5f, 0x91, 0x09, 0xc3, 0x5c, 0xdb, 0xa8, 0xa9, 0xf3, 0xb8, 0x2e, 0xb2, 0x44, 0x09, 0xb9, + 0x00, 0xf9, 0x7a, 0x7d, 0x55, 0x4d, 0xec, 0x19, 0x86, 0xbe, 0xc5, 0x60, 0x6c, 0x84, 0xd0, 0x0d, + 0xa4, 0x5c, 0x31, 0x6b, 0xd0, 0x20, 0x12, 0xef, 0xd8, 0xbe, 0x94, 0xcc, 0xe3, 0x42, 0xd2, 0xdf, + 0x62, 0x1e, 0x27, 0xb3, 0x77, 0x1e, 0x66, 0xab, 0x61, 0x48, 0x83, 0x88, 0xbf, 0x46, 0x10, 0x76, + 0x5b, 0x34, 0x10, 0xb2, 0x26, 0x74, 0x0c, 0x7f, 0x05, 0xbf, 0x11, 0x5a, 0x7d, 0x11, 0xc9, 0x55, + 0x28, 0x56, 0xbb, 0xae, 0x47, 0xdb, 0x0d, 0x2d, 0xba, 0xde, 0x11, 0x30, 0x2b, 0x2e, 0x25, 0x1f, + 0xc2, 0x59, 0x41, 0x24, 0x15, 0x8e, 0xe8, 0x01, 0xae, 0x6b, 0xf8, 0x0e, 0x56, 0xcc, 0x05, 0xa9, + 0xa6, 0x6c, 0xd1, 0x25, 0xd9, 0x94, 0xa4, 0x0a, 0xe5, 0x45, 0x3c, 0x27, 0x95, 0xaf, 0x59, 0xfb, + 0x81, 0xc8, 0x3a, 0x8d, 0x9a, 0x8b, 0x9f, 0xa1, 0xda, 0x6e, 0x5c, 0x68, 0xf5, 0xa0, 0x93, 0xfb, + 0x30, 0x9d, 0x86, 0x31, 0x7d, 0x3c, 0x9a, 0xbc, 0x36, 0xd7, 0xc3, 0x05, 0x15, 0x73, 0x16, 0x15, + 0xd9, 0x86, 0xa9, 0x6a, 0x14, 0x05, 0xde, 0x76, 0x37, 0xa2, 0x29, 0xd5, 0x25, 0x1d, 0x8d, 0x71, + 0xb9, 0x54, 0x5f, 0xcf, 0x08, 0x61, 0x9c, 0x76, 0x62, 0xca, 0x58, 0x85, 0x59, 0xbd, 0xec, 0x88, + 0x1b, 0x3f, 0x58, 0x29, 0x1e, 0x75, 0x14, 0x57, 0xa2, 0xa4, 0x43, 0xb7, 0x1a, 0x1e, 0xb4, 0x5a, + 0x34, 0x0a, 0xf0, 0xe4, 0x1e, 0x1f, 0x7d, 0x34, 0x45, 0x0c, 0xd0, 0x45, 0xe5, 0x9d, 0x56, 0x7c, + 0xd8, 0x53, 0x0b, 0x8f, 0xd4, 0x78, 0x6a, 0xcb, 0x47, 0x69, 0xc0, 0xe5, 0xa3, 0x09, 0x53, 0x8b, + 0xed, 0x46, 0x70, 0x80, 0x77, 0x33, 0x65, 0xe3, 0xc6, 0x4f, 0x68, 0x9c, 0x7c, 0xd1, 0xe5, 0x92, + 0x23, 0x25, 0x2c, 0xab, 0x79, 0xbd, 0x8c, 0xcd, 0xff, 0x0b, 0xca, 0xe9, 0xbe, 0xfc, 0x84, 0xaf, + 0x74, 0x9f, 0x26, 0x34, 0x9b, 0x8d, 0x74, 0xfa, 0x5b, 0xc8, 0x0d, 0xed, 0x29, 0x66, 0x23, 0x49, + 0x1d, 0xa0, 0x3c, 0x9a, 0xac, 0x3d, 0xc0, 0x2c, 0xa7, 0x71, 0x2e, 0x6b, 0x1a, 0x9b, 0xbf, 0x94, + 0x83, 0x29, 0x1e, 0x4d, 0xfa, 0xf4, 0xdb, 0x8a, 0xef, 0x6a, 0xca, 0x59, 0xfa, 0x02, 0x53, 0x5f, + 0x77, 0x8c, 0xb5, 0xf8, 0x35, 0x38, 0xdb, 0xd3, 0x15, 0xa8, 0xa0, 0x17, 0x64, 0x1c, 0x6f, 0x8f, + 0x8a, 0x9e, 0xcd, 0xae, 0x64, 0xf3, 0x96, 0xd5, 0x43, 0x61, 0xfe, 0x83, 0x5c, 0x0f, 0x7f, 0x61, + 0x37, 0xaa, 0x96, 0xa0, 0x71, 0x3a, 0x4b, 0x30, 0xf7, 0xb1, 0x2c, 0xc1, 0xfc, 0x20, 0x96, 0xe0, + 0x87, 0x30, 0xbe, 0x4e, 0x1d, 0x66, 0xd1, 0x88, 0xeb, 0x72, 0x05, 0xed, 0x99, 0x64, 0x56, 0x26, + 0xf5, 0x4b, 0x7c, 0xd5, 0x36, 0x62, 0x04, 0x4c, 0xb5, 0xf0, 0xfb, 0x73, 0x96, 0xce, 0x41, 0x5d, + 0x34, 0x86, 0xfa, 0x2f, 0x1a, 0x66, 0x00, 0x63, 0xf5, 0xfa, 0xea, 0x96, 0x13, 0x30, 0x6d, 0x11, + 0x32, 0x93, 0x51, 0x86, 0x89, 0x1a, 0x89, 0xe2, 0xed, 0x8d, 0x0f, 0x95, 0x58, 0x4c, 0xb1, 0x48, + 0x62, 0x11, 0x52, 0xc1, 0x23, 0xe2, 0x04, 0x4c, 0x8b, 0x88, 0x13, 0x30, 0xf3, 0xef, 0x17, 0xa0, + 0xcc, 0x43, 0x1f, 0xd9, 0xbe, 0x57, 0xe4, 0x07, 0xea, 0x79, 0x27, 0xc2, 0x38, 0xfd, 0x3b, 0x11, + 0x1f, 0x23, 0xc6, 0x56, 0xb9, 0x8b, 0x9d, 0x1f, 0xe0, 0x2e, 0xf6, 0x9b, 0xda, 0x45, 0xe6, 0x42, + 0xf2, 0x10, 0xe9, 0x7e, 0x77, 0x9b, 0x1e, 0x7f, 0x85, 0xf9, 0xb6, 0x7a, 0xe3, 0x78, 0x28, 0x89, + 0x3e, 0x41, 0xca, 0x63, 0xee, 0x1a, 0xc7, 0x5a, 0x6c, 0xf8, 0x34, 0xf1, 0xe6, 0x23, 0xff, 0x47, + 0xe3, 0xcd, 0x17, 0x01, 0x94, 0xa4, 0x31, 0xc5, 0xe4, 0x3d, 0xd2, 0x93, 0x13, 0xc6, 0x28, 0x84, + 0xe6, 0xbf, 0x2d, 0xc3, 0x54, 0xbd, 0xbe, 0xba, 0xe0, 0x39, 0xbb, 0x6d, 0x3f, 0x8c, 0xbc, 0xc6, + 0x52, 0x7b, 0xc7, 0x67, 0x53, 0x78, 0x9d, 0x86, 0xd1, 0x9d, 0xa6, 0xff, 0x48, 0x8d, 0x7d, 0x8e, + 0x68, 0x18, 0xd9, 0x3b, 0x4d, 0xff, 0x91, 0x15, 0x17, 0xb3, 0x25, 0x62, 0x31, 0x08, 0xe2, 0xa7, + 0x4f, 0x70, 0x89, 0xa0, 0x0c, 0x60, 0x71, 0x38, 0x9b, 0x25, 0xf5, 0x2e, 0xcf, 0xfe, 0xc1, 0x33, + 0xcd, 0xe1, 0x2c, 0x09, 0x39, 0xc8, 0x92, 0x65, 0x84, 0xf6, 0x0a, 0xac, 0xd0, 0x9a, 0xe7, 0xb5, + 0xa8, 0xf5, 0xa4, 0x58, 0xbc, 0xa1, 0x87, 0x50, 0x1c, 0x5d, 0xbb, 0x83, 0x70, 0xd5, 0x78, 0xef, + 0x99, 0x03, 0x07, 0x70, 0x96, 0x19, 0x97, 0xa7, 0xb6, 0x39, 0xae, 0x09, 0x9d, 0x60, 0xe2, 0x7d, + 0x89, 0x0c, 0xc3, 0x43, 0x7d, 0x1e, 0x21, 0xb3, 0x06, 0xf2, 0x4b, 0x06, 0x3c, 0x9b, 0x59, 0x12, + 0xcf, 0xee, 0x31, 0xed, 0xe6, 0x80, 0xa2, 0x34, 0x30, 0x63, 0xca, 0xab, 0xfd, 0xaa, 0xb6, 0x33, + 0x54, 0xc1, 0xf1, 0x35, 0x91, 0x7f, 0x69, 0xc0, 0x79, 0x0d, 0x03, 0x53, 0x2d, 0xb6, 0x68, 0x3b, + 0x92, 0x0f, 0x87, 0xf7, 0x91, 0xeb, 0x8f, 0x9e, 0x8c, 0x5c, 0xbf, 0xa0, 0x7f, 0x0b, 0x4f, 0x47, + 0x8d, 0xd5, 0xab, 0x3b, 0xc5, 0x3e, 0x2d, 0x24, 0x3f, 0x0d, 0x53, 0x58, 0x24, 0xed, 0x1f, 0x26, + 0xb3, 0x68, 0x36, 0x95, 0x6a, 0x73, 0x3f, 0x38, 0xac, 0x8c, 0x6b, 0x05, 0x78, 0xad, 0x10, 0x6b, + 0x8b, 0xcd, 0x25, 0xaf, 0xbd, 0xe3, 0x6b, 0xa9, 0x55, 0xd3, 0xcc, 0xc8, 0xbf, 0x36, 0x60, 0x96, + 0x41, 0x79, 0x83, 0xef, 0x04, 0x7e, 0x2b, 0x2e, 0x0f, 0xc5, 0x8d, 0xf3, 0x3e, 0x1d, 0xd4, 0x7c, + 0x32, 0x1d, 0xf4, 0x12, 0x36, 0x99, 0xcf, 0x7e, 0x7b, 0x27, 0xf0, 0x5b, 0x49, 0xf3, 0xb5, 0x6c, + 0x25, 0xfd, 0x1a, 0x49, 0x7e, 0xd1, 0x80, 0x0b, 0xda, 0x36, 0x4a, 0xbd, 0x94, 0x37, 0x3b, 0xa9, + 0x39, 0x07, 0xd4, 0xa2, 0xda, 0x75, 0x21, 0xe9, 0x57, 0xb0, 0x05, 0xc9, 0xba, 0x80, 0x6d, 0xb1, + 0x5b, 0x1c, 0x4b, 0x69, 0x42, 0xff, 0x5a, 0x88, 0x07, 0x53, 0xe8, 0x58, 0xd2, 0xfc, 0x12, 0x33, + 0xfd, 0xfd, 0x12, 0x57, 0x44, 0xd5, 0xcf, 0xe1, 0xc5, 0xa7, 0xfe, 0xce, 0x89, 0x5e, 0xae, 0xe4, + 0xe7, 0xe0, 0x42, 0x0f, 0x30, 0x9e, 0x57, 0x67, 0xfb, 0xce, 0xab, 0x57, 0x8f, 0x0e, 0x2b, 0x2f, + 0x67, 0xd5, 0x96, 0x35, 0xa7, 0xfa, 0xd7, 0x40, 0xee, 0x03, 0x24, 0x85, 0xb3, 0xe7, 0x50, 0x14, + 0x5f, 0x15, 0x92, 0xa0, 0x94, 0x30, 0xfd, 0xac, 0xd4, 0xa6, 0x2e, 0x63, 0x09, 0x12, 0x59, 0x85, + 0x92, 0x72, 0xbd, 0xeb, 0x60, 0xf6, 0x3c, 0x67, 0xf7, 0x83, 0xc3, 0x8a, 0x06, 0x67, 0xbb, 0x3f, + 0xf5, 0x86, 0x98, 0xea, 0x6f, 0xd2, 0x10, 0xc9, 0x1f, 0x19, 0x30, 0xc3, 0x00, 0x89, 0xa0, 0x88, + 0x86, 0xce, 0x1e, 0x27, 0xc9, 0x7b, 0x4f, 0x46, 0x92, 0x9f, 0xc7, 0x36, 0xaa, 0x92, 0xdc, 0xf3, + 0xf1, 0x99, 0x8d, 0x43, 0x09, 0xd6, 0xfc, 0x92, 0x9a, 0x04, 0x5f, 0x18, 0x40, 0x82, 0x79, 0x57, + 0x9f, 0x2c, 0xc1, 0x7d, 0x6b, 0x21, 0xeb, 0x50, 0x12, 0x46, 0x2e, 0xef, 0xb0, 0xe7, 0xb4, 0x7b, + 0x23, 0x6a, 0x11, 0xdf, 0x8d, 0x8b, 0x7b, 0x6e, 0x3d, 0x5f, 0xa8, 0x71, 0x21, 0x6d, 0x98, 0xe6, + 0xbf, 0x75, 0x8b, 0xb4, 0xd2, 0xd7, 0x22, 0xbd, 0x2a, 0xbe, 0xe8, 0xb2, 0xe0, 0x9f, 0x32, 0x4c, + 0x95, 0x8a, 0xb2, 0x18, 0x13, 0x07, 0x26, 0x05, 0xd8, 0xdf, 0xa7, 0x5c, 0x5b, 0x5e, 0xd6, 0x62, + 0xa5, 0x52, 0xa5, 0x3c, 0x71, 0x8c, 0xac, 0x0b, 0x83, 0x52, 0x52, 0x2a, 0x33, 0xcd, 0xcf, 0xfc, + 0xa6, 0xd1, 0x53, 0x07, 0x79, 0x4d, 0x3c, 0x4d, 0xaf, 0x84, 0x7b, 0xa3, 0x03, 0x99, 0x73, 0xc4, + 0xa0, 0xef, 0x04, 0x81, 0xd9, 0x09, 0x6a, 0xe8, 0x5b, 0x5e, 0xa4, 0xfc, 0xe4, 0xa0, 0x24, 0x64, + 0xa5, 0x22, 0x1d, 0xa8, 0xf9, 0xc4, 0xde, 0x40, 0x07, 0xaa, 0x70, 0x9b, 0x9a, 0xbf, 0x98, 0xd3, + 0xc7, 0x8c, 0x5c, 0x55, 0x4c, 0x56, 0x25, 0xf8, 0x4e, 0x9a, 0xac, 0x8a, 0xa1, 0xfa, 0x7b, 0x06, + 0x4c, 0xaf, 0x06, 0xbb, 0x4e, 0xdb, 0xfb, 0x19, 0x1e, 0x9a, 0xef, 0x63, 0x3f, 0xc6, 0xc7, 0xa1, + 0x9f, 0xea, 0xd5, 0x7c, 0x5f, 0xa9, 0x98, 0x0d, 0x2d, 0x8e, 0xb1, 0x95, 0xd5, 0x1e, 0x3c, 0xc6, + 0xc2, 0x86, 0x29, 0x19, 0x12, 0x38, 0x3a, 0x87, 0x9b, 0xdf, 0xca, 0xc1, 0x98, 0x22, 0x3f, 0xe4, + 0xf3, 0x50, 0x52, 0xf9, 0xa8, 0x01, 0xc0, 0x6a, 0xb5, 0x96, 0x86, 0x85, 0x91, 0xfa, 0xd4, 0x69, + 0xa9, 0x7b, 0x6d, 0x56, 0x8b, 0x85, 0xd0, 0x53, 0x5a, 0xf5, 0xef, 0x65, 0x58, 0xf5, 0xa7, 0x4a, + 0x4f, 0xf4, 0x76, 0xaf, 0x6d, 0x3f, 0x78, 0x36, 0x21, 0xf3, 0x57, 0x72, 0x50, 0x5e, 0x0f, 0xba, + 0x61, 0x44, 0x5d, 0x19, 0xaf, 0xf0, 0xd9, 0x7a, 0xf3, 0x50, 0xff, 0xb8, 0x63, 0x22, 0xf2, 0x0a, + 0xbf, 0xf1, 0x3b, 0x95, 0x33, 0xe6, 0x97, 0x61, 0x26, 0xdd, 0x1d, 0xe8, 0x1b, 0xa8, 0xc2, 0xa4, + 0x0e, 0x4f, 0x5f, 0x56, 0x4e, 0x53, 0x59, 0x69, 0x7c, 0xf3, 0xcf, 0x72, 0x69, 0xde, 0xc2, 0x81, + 0xcb, 0x26, 0x38, 0x3e, 0x41, 0x2d, 0xef, 0x53, 0x8a, 0x9c, 0xbe, 0x08, 0xb2, 0x64, 0xd9, 0x69, + 0xae, 0xad, 0xc7, 0xc7, 0xb9, 0xf9, 0xec, 0xe3, 0x5c, 0x72, 0x1b, 0x4a, 0x18, 0xa2, 0x5e, 0x75, + 0xdd, 0x80, 0x6d, 0x40, 0x0a, 0x49, 0xfa, 0xde, 0x47, 0x74, 0xdb, 0xe6, 0xa1, 0xec, 0x8e, 0xeb, + 0x06, 0x96, 0x86, 0x47, 0xe6, 0x61, 0x46, 0xbb, 0x11, 0x21, 0xe9, 0x87, 0x12, 0x07, 0x55, 0x84, + 0x05, 0x9c, 0x38, 0x13, 0x19, 0x33, 0xd8, 0xfb, 0x4d, 0xb6, 0x83, 0xc0, 0x7d, 0xa5, 0x9e, 0xfa, + 0x54, 0x6a, 0x76, 0x19, 0x26, 0x45, 0x30, 0xed, 0x4f, 0xcb, 0xe9, 0x68, 0x99, 0xb4, 0x38, 0xa2, + 0xf9, 0x57, 0x06, 0x9b, 0x6b, 0x8d, 0xfd, 0xcf, 0xd8, 0x85, 0x7a, 0xf6, 0x49, 0xc7, 0x9c, 0x2f, + 0xfc, 0x07, 0x83, 0x5f, 0x89, 0x15, 0xe2, 0xf3, 0x26, 0x0c, 0xf3, 0x07, 0xaf, 0xc5, 0xe5, 0x4d, + 0x95, 0x0b, 0x2f, 0x48, 0x42, 0xe2, 0xf8, 0xb3, 0xd9, 0x96, 0x20, 0x50, 0x5d, 0x2e, 0xb9, 0x81, + 0x5c, 0x2e, 0xca, 0x7b, 0x1c, 0x83, 0xbd, 0xf3, 0x64, 0x9c, 0xfc, 0x1e, 0x87, 0xf9, 0xbf, 0x72, + 0xfc, 0x7b, 0x44, 0xa3, 0x06, 0xcd, 0x00, 0x7f, 0x05, 0x0a, 0x4c, 0x0e, 0xd4, 0x34, 0xfb, 0x4c, + 0x56, 0xb4, 0xa7, 0xfd, 0xfc, 0x26, 0xba, 0x62, 0x51, 0xd7, 0xaa, 0x39, 0x1c, 0x50, 0x1d, 0xab, + 0xf3, 0x06, 0x31, 0xf0, 0x15, 0x25, 0xdf, 0xa5, 0xea, 0x74, 0x68, 0xeb, 0x0f, 0x5e, 0x61, 0x39, + 0xb9, 0xad, 0x5c, 0xa5, 0x54, 0xcf, 0x50, 0x5b, 0x3b, 0x8e, 0xcd, 0xaf, 0xf0, 0xa9, 0xda, 0x36, + 0xb9, 0x75, 0xb9, 0x08, 0x13, 0x7a, 0x82, 0x27, 0x71, 0xce, 0x81, 0x49, 0x59, 0x52, 0xc9, 0xa1, + 0x54, 0x8f, 0xba, 0x4e, 0x44, 0x6a, 0x30, 0xae, 0x65, 0xf1, 0x51, 0x1f, 0x1f, 0xe1, 0x59, 0x43, + 0xed, 0xde, 0xf4, 0x73, 0x3a, 0x89, 0x12, 0xa3, 0xf3, 0x39, 0x28, 0x8b, 0x99, 0x19, 0xa7, 0xd3, + 0x40, 0x6f, 0xf2, 0xd2, 0x82, 0xa5, 0xce, 0xa6, 0x86, 0xe7, 0x06, 0x16, 0x42, 0xcd, 0xef, 0x1a, + 0x70, 0x41, 0x3c, 0xe4, 0x6d, 0xd1, 0x30, 0x0a, 0x3c, 0x9e, 0x9d, 0x03, 0xe5, 0xf1, 0xf3, 0xe4, + 0x6d, 0x99, 0x90, 0x58, 0x57, 0x90, 0xe9, 0x3a, 0x6a, 0xe3, 0x42, 0x28, 0x87, 0x30, 0x25, 0xb1, + 0x4c, 0x44, 0xfc, 0xa6, 0x48, 0x44, 0x9c, 0x3b, 0x9e, 0x38, 0x9e, 0x17, 0x2e, 0x6d, 0xcb, 0x04, + 0xc4, 0xdf, 0xc9, 0xc1, 0xd9, 0x8c, 0x66, 0x6d, 0x7e, 0xfe, 0x29, 0x55, 0x0e, 0x35, 0x4d, 0x39, + 0xc8, 0x4c, 0xf5, 0x7d, 0x3b, 0x3e, 0x53, 0x57, 0xfc, 0x96, 0x01, 0xe7, 0x75, 0xe9, 0x11, 0xc7, + 0x5f, 0x9b, 0xb7, 0xc8, 0x5b, 0x30, 0x7c, 0x8f, 0x3a, 0x2e, 0x95, 0xb7, 0xbe, 0xcf, 0xa6, 0x9e, + 0xe7, 0xe0, 0x85, 0x9c, 0xed, 0x9f, 0xf1, 0xa9, 0x7c, 0xc6, 0x12, 0x24, 0x64, 0x41, 0x34, 0x8e, + 0x9b, 0x80, 0xa6, 0x0c, 0x0e, 0xcb, 0xaa, 0xea, 0x18, 0x5f, 0xfc, 0xef, 0x19, 0xf0, 0xcc, 0x31, + 0x34, 0x6c, 0xe0, 0xd8, 0xd0, 0xab, 0x03, 0x87, 0x0b, 0x0b, 0x42, 0xc9, 0xbb, 0x30, 0xb9, 0x2e, + 0x4c, 0x48, 0x39, 0x1c, 0xca, 0xab, 0x66, 0xd2, 0xba, 0xb4, 0xe5, 0xb8, 0xa4, 0x91, 0x99, 0x01, + 0x7c, 0xcf, 0x0f, 0xa3, 0x76, 0x92, 0x61, 0x13, 0x0d, 0xe0, 0x3d, 0x01, 0xb3, 0xe2, 0x52, 0x66, + 0x16, 0xe8, 0xcd, 0x14, 0x11, 0xd8, 0x2f, 0xc0, 0x30, 0xc3, 0x89, 0x1d, 0xfa, 0x28, 0x07, 0xf8, + 0xcc, 0xb4, 0xe7, 0x5a, 0xa2, 0x28, 0x3e, 0x4a, 0xca, 0x65, 0x06, 0x4a, 0x7d, 0xcb, 0x80, 0xb2, + 0xce, 0xfb, 0x93, 0x0e, 0xcd, 0x3b, 0xda, 0xd0, 0x3c, 0x93, 0x3d, 0x34, 0xfd, 0xc7, 0xa4, 0x27, + 0xd9, 0xdd, 0x40, 0x63, 0x61, 0xc2, 0xf0, 0x82, 0xdf, 0x72, 0xbc, 0xb6, 0x9a, 0xa0, 0xcd, 0x45, + 0x88, 0x25, 0x4a, 0x94, 0xde, 0xca, 0xf7, 0xed, 0x2d, 0xf3, 0x57, 0x0b, 0x70, 0xc1, 0xa2, 0xbb, + 0x1e, 0x33, 0x90, 0x36, 0x42, 0xaf, 0xbd, 0xab, 0x85, 0xb1, 0x99, 0xa9, 0x0e, 0x17, 0x97, 0x77, + 0x18, 0x24, 0xee, 0xef, 0x57, 0xa0, 0xc8, 0xb4, 0xb4, 0xd2, 0xe7, 0xe8, 0xa4, 0xc5, 0x34, 0xa3, + 0x7c, 0x5c, 0x65, 0x31, 0xb9, 0x26, 0xd6, 0x10, 0xe5, 0x7a, 0x25, 0x5b, 0x43, 0x7e, 0x7c, 0x58, + 0x01, 0xfe, 0xac, 0x10, 0x2b, 0x15, 0xeb, 0x48, 0x6c, 0x54, 0x15, 0xfa, 0x18, 0x55, 0x0f, 0x60, + 0xa6, 0xea, 0x72, 0xfd, 0xe4, 0x34, 0xd7, 0x02, 0xaf, 0xdd, 0xf0, 0x3a, 0x4e, 0x53, 0x1a, 0xe5, + 0xe8, 0xaa, 0x77, 0xe2, 0x72, 0xbb, 0x13, 0x23, 0x58, 0x99, 0x64, 0xec, 0x33, 0x16, 0x56, 0xea, + 0x3c, 0x8b, 0x24, 0xf7, 0xbf, 0xe3, 0x67, 0xb8, 0xed, 0x90, 0xa7, 0x91, 0xb4, 0xe2, 0x62, 0x34, + 0xe7, 0xf0, 0x86, 0xf5, 0xfa, 0x72, 0xfd, 0xbe, 0xb8, 0xb1, 0x2c, 0x6f, 0x7f, 0xf0, 0x0b, 0xd9, + 0x51, 0x33, 0xc4, 0x23, 0x43, 0x0d, 0x2f, 0xa1, 0xab, 0xd7, 0xef, 0x31, 0xba, 0x62, 0x0f, 0x5d, + 0x18, 0xee, 0xa9, 0x74, 0x1c, 0x8f, 0xdc, 0x00, 0xe0, 0xf1, 0xf3, 0x28, 0x10, 0xa3, 0x89, 0xf1, + 0x17, 0x20, 0x94, 0x1b, 0x7f, 0x0a, 0x0a, 0x79, 0x1b, 0xa6, 0x17, 0xe7, 0xe7, 0xa4, 0xdf, 0x65, + 0xc1, 0x6f, 0x74, 0x5b, 0xb4, 0x1d, 0xe1, 0xd5, 0xf8, 0x12, 0x1f, 0x43, 0xda, 0x98, 0x63, 0x52, + 0x90, 0x85, 0x26, 0x2e, 0x24, 0xf3, 0x74, 0x16, 0xf3, 0xbe, 0x4b, 0xc3, 0xcd, 0x9b, 0x9f, 0xb1, + 0x0b, 0xc9, 0xca, 0xb7, 0xe1, 0x6c, 0xbb, 0x99, 0x39, 0x33, 0xff, 0x7f, 0xbc, 0x90, 0xdc, 0x83, + 0x4b, 0x7e, 0x02, 0x86, 0xf0, 0xa7, 0x58, 0x71, 0xa7, 0x33, 0xd8, 0x26, 0xab, 0x6d, 0x83, 0x27, + 0x04, 0x44, 0x02, 0xb2, 0x94, 0x3c, 0xda, 0x76, 0x8a, 0x6b, 0x75, 0x22, 0x27, 0x8e, 0xfe, 0x5a, + 0xa7, 0x0b, 0x25, 0xb5, 0x42, 0x26, 0x23, 0xf7, 0x9c, 0x70, 0x8f, 0xba, 0xf3, 0xf2, 0x8d, 0xff, + 0x12, 0x97, 0x91, 0x3d, 0x84, 0xe2, 0x4b, 0xa2, 0x96, 0x82, 0xc2, 0xb4, 0xc3, 0x52, 0xb8, 0x11, + 0x8a, 0xa6, 0x88, 0x5d, 0x90, 0x87, 0xbb, 0x57, 0xd7, 0x12, 0x45, 0xa8, 0x2d, 0xe5, 0x29, 0x4d, + 0xe0, 0x34, 0xf6, 0x69, 0xb0, 0x79, 0xf3, 0xd3, 0xd0, 0x96, 0x7a, 0x1d, 0xc7, 0x8c, 0xc9, 0x37, + 0x20, 0xce, 0x67, 0xa9, 0x21, 0x33, 0x1b, 0x31, 0x09, 0x06, 0x36, 0x12, 0x1b, 0x31, 0x09, 0x06, + 0x56, 0x6d, 0xc4, 0x18, 0x35, 0x7e, 0x51, 0x25, 0x77, 0xc2, 0x8b, 0x2a, 0x7d, 0x5e, 0x8f, 0x92, + 0xf7, 0xc8, 0x3e, 0x43, 0xef, 0xf5, 0x7d, 0x11, 0x4a, 0xd5, 0x28, 0x72, 0x1a, 0x7b, 0xd4, 0xc5, + 0x97, 0x7b, 0x94, 0x18, 0x44, 0x47, 0xc0, 0x55, 0x8f, 0xa2, 0x8a, 0xab, 0xbc, 0xd7, 0x39, 0x32, + 0xc0, 0x7b, 0x9d, 0x37, 0x60, 0x64, 0xa9, 0xfd, 0xd0, 0x63, 0x7d, 0x52, 0x4c, 0xf2, 0xd1, 0x79, + 0x1c, 0xa4, 0x3f, 0xf2, 0x88, 0x20, 0xf2, 0xa6, 0x62, 0x41, 0x8c, 0x26, 0xa6, 0x3c, 0xdf, 0x66, + 0xd9, 0xd2, 0x90, 0x50, 0x0f, 0x7f, 0x25, 0x3a, 0xb9, 0x0d, 0x23, 0x72, 0xf7, 0x0c, 0x89, 0xf9, + 0x2e, 0x28, 0x1d, 0x5e, 0xa2, 0xa5, 0xc0, 0x13, 0xbb, 0xe7, 0xb7, 0xf5, 0x2b, 0xeb, 0x63, 0x4a, + 0x2a, 0x28, 0xe5, 0xca, 0xba, 0x96, 0x0a, 0x4a, 0xb9, 0xbc, 0x1e, 0x6f, 0x86, 0x4a, 0x27, 0x6e, + 0x86, 0x36, 0xa1, 0xb4, 0xe6, 0x04, 0x91, 0xc7, 0x96, 0xa3, 0x76, 0xc4, 0x73, 0x11, 0x27, 0x7b, + 0x75, 0xa5, 0x28, 0x79, 0xc4, 0xad, 0xa3, 0xe0, 0xeb, 0xb9, 0x74, 0x12, 0x38, 0x59, 0xc9, 0xb8, + 0xd4, 0x24, 0x1e, 0x07, 0xc0, 0x83, 0x4b, 0x35, 0x13, 0x3d, 0x2f, 0x55, 0x4f, 0x38, 0x7a, 0xef, + 0x43, 0xdd, 0xe2, 0x63, 0x80, 0x7b, 0xc6, 0x49, 0x64, 0x83, 0x09, 0x0d, 0xd1, 0xae, 0x48, 0x6d, + 0x1c, 0x63, 0x44, 0xf2, 0x55, 0x28, 0xb1, 0xff, 0x31, 0x31, 0xab, 0x47, 0x79, 0xae, 0xe1, 0xe4, + 0x92, 0x8b, 0x3e, 0xa1, 0x79, 0xf6, 0xd6, 0x3a, 0x8d, 0xf8, 0x04, 0x46, 0xc6, 0x69, 0xc7, 0x8b, + 0xc6, 0x8d, 0xbc, 0x07, 0x25, 0x35, 0xb1, 0xf3, 0xec, 0x54, 0x12, 0x96, 0xe6, 0x0a, 0x78, 0x7a, + 0x94, 0x34, 0x02, 0xb6, 0x7e, 0x55, 0x3b, 0x1d, 0xa4, 0x25, 0x8a, 0xb4, 0x77, 0x3a, 0x69, 0x32, + 0x89, 0x46, 0xde, 0x87, 0x52, 0xb5, 0xd3, 0x49, 0x34, 0xce, 0xb4, 0xb2, 0x25, 0xec, 0x74, 0xec, + 0x4c, 0xad, 0xa3, 0x51, 0x30, 0xc1, 0x12, 0x06, 0x1f, 0xd6, 0x3b, 0x93, 0x08, 0x96, 0x4c, 0x57, + 0x9c, 0x16, 0x2c, 0x05, 0xdd, 0xfc, 0x91, 0x01, 0xe7, 0xfb, 0x74, 0xdb, 0xc0, 0x8f, 0x13, 0xdf, + 0x48, 0xd6, 0x60, 0xc5, 0x1d, 0x21, 0xd6, 0x60, 0xf5, 0xa3, 0xe5, 0x6a, 0x9c, 0x9d, 0x18, 0x39, + 0xff, 0xa9, 0x25, 0x46, 0x36, 0x0f, 0x0d, 0x18, 0x53, 0x84, 0xf9, 0x09, 0x3e, 0x06, 0x78, 0x45, + 0xbc, 0x10, 0x90, 0x4f, 0xf0, 0x5a, 0x29, 0xd7, 0x03, 0xbe, 0x08, 0xf0, 0x35, 0x80, 0x65, 0x27, + 0x8c, 0xaa, 0x8d, 0xc8, 0x7b, 0x48, 0x07, 0xd0, 0xdc, 0x49, 0x3a, 0x34, 0x07, 0xdf, 0x14, 0x61, + 0x64, 0x3d, 0xe9, 0xd0, 0x62, 0x86, 0xe6, 0x0a, 0x0c, 0xd7, 0xfd, 0x20, 0xaa, 0x1d, 0xf0, 0xe5, + 0x78, 0x81, 0x86, 0x0d, 0xd5, 0x29, 0xe9, 0xa1, 0x7b, 0xa2, 0x61, 0x89, 0x22, 0x66, 0x13, 0xdf, + 0xf1, 0x68, 0xd3, 0x55, 0x83, 0x1c, 0x76, 0x18, 0xc0, 0xe2, 0xf0, 0x6b, 0xef, 0xc1, 0xa4, 0x14, + 0xec, 0xf5, 0xe5, 0x3a, 0x7e, 0xc1, 0x24, 0x8c, 0x6d, 0x2e, 0x5a, 0x4b, 0x77, 0xbe, 0x6c, 0xdf, + 0xd9, 0x58, 0x5e, 0x2e, 0x9f, 0x21, 0xe3, 0x30, 0x2a, 0x00, 0xf3, 0xd5, 0xb2, 0x41, 0x4a, 0x50, + 0x5c, 0x5a, 0xa9, 0x2f, 0xce, 0x6f, 0x58, 0x8b, 0xe5, 0xdc, 0xb5, 0x97, 0x60, 0x22, 0x89, 0x72, + 0xc3, 0xf3, 0x90, 0x11, 0xc8, 0x5b, 0xd5, 0xad, 0xf2, 0x19, 0x02, 0x30, 0xbc, 0x76, 0x7f, 0xbe, + 0x7e, 0xf3, 0x66, 0xd9, 0xb8, 0xf6, 0x39, 0x98, 0x42, 0x47, 0xe5, 0x32, 0xdb, 0x37, 0xb4, 0x69, + 0x80, 0x35, 0x95, 0xa0, 0x58, 0xa7, 0x1d, 0x27, 0x70, 0x22, 0xca, 0xab, 0x79, 0xd0, 0x6d, 0x46, + 0x5e, 0xa7, 0x49, 0x1f, 0x97, 0x8d, 0x6b, 0x6f, 0xc2, 0xa4, 0xe5, 0x77, 0x23, 0xaf, 0xbd, 0x2b, + 0x5f, 0x53, 0x21, 0x67, 0x61, 0x6a, 0x63, 0xa5, 0xfa, 0xa0, 0xb6, 0x74, 0x77, 0x63, 0x75, 0xa3, + 0x6e, 0x3f, 0xa8, 0xae, 0xcf, 0xdf, 0x2b, 0x9f, 0x61, 0x0d, 0x7e, 0xb0, 0x5a, 0x5f, 0xb7, 0xad, + 0xc5, 0xf9, 0xc5, 0x95, 0xf5, 0xb2, 0x71, 0xed, 0x97, 0x0d, 0x98, 0xd0, 0x1f, 0xbe, 0x27, 0x97, + 0xe1, 0xd2, 0x46, 0x7d, 0xd1, 0xb2, 0xd7, 0x57, 0xef, 0x2f, 0xae, 0xd8, 0x1b, 0xf5, 0xea, 0xdd, + 0x45, 0x7b, 0x63, 0xa5, 0xbe, 0xb6, 0x38, 0xbf, 0x74, 0x67, 0x69, 0x71, 0xa1, 0x7c, 0x86, 0x54, + 0xe0, 0x19, 0x05, 0xc3, 0x5a, 0x9c, 0x5f, 0xdd, 0x5c, 0xb4, 0xec, 0xb5, 0x6a, 0xbd, 0xbe, 0xb5, + 0x6a, 0x2d, 0x94, 0x0d, 0x72, 0x11, 0xce, 0x65, 0x20, 0x3c, 0xb8, 0x53, 0x2d, 0xe7, 0x7a, 0xca, + 0x56, 0x16, 0xb7, 0xaa, 0xcb, 0x76, 0x6d, 0x75, 0xbd, 0x9c, 0xbf, 0xf6, 0x1e, 0x33, 0xbc, 0x92, + 0x27, 0x23, 0x49, 0x11, 0x0a, 0x2b, 0xab, 0x2b, 0x8b, 0xe5, 0x33, 0x64, 0x0c, 0x46, 0xd6, 0x16, + 0x57, 0x16, 0x96, 0x56, 0xee, 0xf2, 0x6e, 0xad, 0xae, 0xad, 0x59, 0xab, 0x9b, 0x8b, 0x0b, 0xe5, + 0x1c, 0xeb, 0xbb, 0x85, 0xc5, 0x15, 0xd6, 0xb2, 0xfc, 0x35, 0x93, 0xbf, 0x56, 0xa4, 0xbd, 0x44, + 0xc1, 0x7a, 0x6b, 0xf1, 0x4b, 0xeb, 0x8b, 0x2b, 0xf5, 0xa5, 0xd5, 0x95, 0xf2, 0x99, 0x6b, 0x97, + 0x52, 0x38, 0x72, 0x24, 0xea, 0xf5, 0x7b, 0xe5, 0x33, 0xd7, 0xbe, 0x0a, 0x25, 0xd5, 0xee, 0x20, + 0xe7, 0x61, 0x5a, 0xfd, 0xbd, 0x46, 0xdb, 0xae, 0xd7, 0xde, 0x2d, 0x9f, 0x49, 0x17, 0x58, 0xdd, + 0x76, 0x9b, 0x15, 0xe0, 0xc7, 0xab, 0x05, 0xeb, 0x34, 0x68, 0x79, 0x6d, 0x66, 0x52, 0x94, 0x73, + 0xb5, 0xf2, 0xf7, 0xff, 0xe2, 0xb9, 0x33, 0xdf, 0xff, 0xe1, 0x73, 0xc6, 0x9f, 0xfd, 0xf0, 0x39, + 0xe3, 0xbf, 0xfd, 0xf0, 0x39, 0x63, 0x7b, 0x18, 0x05, 0xfd, 0xd6, 0xff, 0x0e, 0x00, 0x00, 0xff, + 0xff, 0xd8, 0xa2, 0x1a, 0x21, 0x4d, 0xc8, 0x00, 0x00, } func (m *KeepAlive) Marshal() (dAtA []byte, err error) { @@ -19840,6 +19949,50 @@ func (m *SSODiagnosticInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.GithubTokenInfo != nil { + { + size, err := m.GithubTokenInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x82 + } + if len(m.GithubTeamsToLogins) > 0 { + for iNdEx := len(m.GithubTeamsToLogins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.GithubTeamsToLogins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xfa + } + } + if m.GithubClaims != nil { + { + size, err := m.GithubClaims.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf2 + } if len(m.OIDCConnectorTraitMapping) > 0 { for iNdEx := len(m.OIDCConnectorTraitMapping) - 1; iNdEx >= 0; iNdEx-- { { @@ -20038,6 +20191,105 @@ func (m *SSODiagnosticInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *GithubTokenInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GithubTokenInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GithubTokenInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Scope) > 0 { + i -= len(m.Scope) + copy(dAtA[i:], m.Scope) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Scope))) + i-- + dAtA[i] = 0x1a + } + if m.Expires != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Expires)) + i-- + dAtA[i] = 0x10 + } + if len(m.TokenType) > 0 { + i -= len(m.TokenType) + copy(dAtA[i:], m.TokenType) + i = encodeVarintTypes(dAtA, i, uint64(len(m.TokenType))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GithubClaims) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GithubClaims) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GithubClaims) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Teams) > 0 { + for iNdEx := len(m.Teams) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Teams[iNdEx]) + copy(dAtA[i:], m.Teams[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Teams[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + { + size := m.OrganizationToTeams.Size() + i -= size + if _, err := m.OrganizationToTeams.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Username) > 0 { + i -= len(m.Username) + copy(dAtA[i:], m.Username) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Username))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *TeamMapping) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -20389,12 +20641,12 @@ func (m *LockSpecV2) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if m.Expires != nil { - n181, err181 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) - if err181 != nil { - return 0, err181 + n184, err184 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) + if err184 != nil { + return 0, err184 } - i -= n181 - i = encodeVarintTypes(dAtA, i, uint64(n181)) + i -= n184 + i = encodeVarintTypes(dAtA, i, uint64(n184)) i-- dAtA[i] = 0x1a } @@ -21075,12 +21327,12 @@ func (m *RecoveryCodesSpecV1) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n191, err191 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) - if err191 != nil { - return 0, err191 + n194, err194 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + if err194 != nil { + return 0, err194 } - i -= n191 - i = encodeVarintTypes(dAtA, i, uint64(n191)) + i -= n194 + i = encodeVarintTypes(dAtA, i, uint64(n194)) i-- dAtA[i] = 0x12 if len(m.Codes) > 0 { @@ -21346,20 +21598,20 @@ func (m *SessionTrackerSpecV1) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n194, err194 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err194 != nil { - return 0, err194 + n197, err197 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err197 != nil { + return 0, err197 } - i -= n194 - i = encodeVarintTypes(dAtA, i, uint64(n194)) + i -= n197 + i = encodeVarintTypes(dAtA, i, uint64(n197)) i-- dAtA[i] = 0x2a - n195, err195 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) - if err195 != nil { - return 0, err195 + n198, err198 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Created, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Created):]) + if err198 != nil { + return 0, err198 } - i -= n195 - i = encodeVarintTypes(dAtA, i, uint64(n195)) + i -= n198 + i = encodeVarintTypes(dAtA, i, uint64(n198)) i-- dAtA[i] = 0x22 if m.State != 0 { @@ -21463,12 +21715,12 @@ func (m *Participant) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n196, err196 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastActive, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastActive):]) - if err196 != nil { - return 0, err196 + n199, err199 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastActive, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastActive):]) + if err199 != nil { + return 0, err199 } - i -= n196 - i = encodeVarintTypes(dAtA, i, uint64(n196)) + i -= n199 + i = encodeVarintTypes(dAtA, i, uint64(n199)) i-- dAtA[i] = 0x22 if len(m.Mode) > 0 { @@ -25931,6 +26183,67 @@ func (m *SSODiagnosticInfo) Size() (n int) { n += 2 + l + sovTypes(uint64(l)) } } + if m.GithubClaims != nil { + l = m.GithubClaims.Size() + n += 2 + l + sovTypes(uint64(l)) + } + if len(m.GithubTeamsToLogins) > 0 { + for _, e := range m.GithubTeamsToLogins { + l = e.Size() + n += 2 + l + sovTypes(uint64(l)) + } + } + if m.GithubTokenInfo != nil { + l = m.GithubTokenInfo.Size() + n += 2 + l + sovTypes(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *GithubTokenInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TokenType) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Expires != 0 { + n += 1 + sovTypes(uint64(m.Expires)) + } + l = len(m.Scope) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *GithubClaims) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Username) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = m.OrganizationToTeams.Size() + n += 1 + l + sovTypes(uint64(l)) + if len(m.Teams) > 0 { + for _, s := range m.Teams { + l = len(s) + n += 1 + l + sovTypes(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -56271,6 +56584,394 @@ func (m *SSODiagnosticInfo) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 30: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GithubClaims", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.GithubClaims == nil { + m.GithubClaims = &GithubClaims{} + } + if err := m.GithubClaims.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 31: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GithubTeamsToLogins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GithubTeamsToLogins = append(m.GithubTeamsToLogins, TeamMapping{}) + if err := m.GithubTeamsToLogins[len(m.GithubTeamsToLogins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 32: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GithubTokenInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.GithubTokenInfo == nil { + m.GithubTokenInfo = &GithubTokenInfo{} + } + if err := m.GithubTokenInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GithubTokenInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GithubTokenInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GithubTokenInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + } + m.Expires = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Expires |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Scope", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Scope = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GithubClaims) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GithubClaims: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GithubClaims: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Username = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OrganizationToTeams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.OrganizationToTeams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Teams", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Teams = append(m.Teams, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/api/types/types.proto b/api/types/types.proto index 8ffaf2721fef4..75bec33768f7f 100644 --- a/api/types/types.proto +++ b/api/types/types.proto @@ -2812,6 +2812,40 @@ message SSODiagnosticInfo { (gogoproto.nullable) = false, (gogoproto.jsontag) = "oidc_connector_trait_mapping,omitempty" ]; + + // GithubClaims represents Github user information obtained during OAuth2 flow. + GithubClaims GithubClaims = 30 [ (gogoproto.jsontag) = "github_claims,omitempty" ]; + + // GithubTeamsToLogins is TeamsToLogins mapping from Github connector used in the SSO flow. + repeated TeamMapping GithubTeamsToLogins = 31 + [ (gogoproto.nullable) = false, (gogoproto.jsontag) = "github_teams_to_logins,omitempty" ]; + + // GithubTokenInfo stores diagnostic info about Github OAuth2 token obtained during SSO flow. + GithubTokenInfo GithubTokenInfo = 32 [ (gogoproto.jsontag) = "github_token_info,omitempty" ]; +} + +// GithubTokenInfo stores diagnostic info about Github OAuth2 token obtained during SSO flow. +// The token itself is secret and therefore not included. +message GithubTokenInfo { + string TokenType = 1 [ (gogoproto.jsontag) = "token_type" ]; + int64 Expires = 2 [ (gogoproto.jsontag) = "expires" ]; + string Scope = 3 [ (gogoproto.jsontag) = "scope" ]; +} + +// GithubClaims represents Github user information obtained during OAuth2 flow +message GithubClaims { + // Username is the user's username + string Username = 1 [ (gogoproto.jsontag) = "username" ]; + + // OrganizationToTeams is the user's organization and team membership + wrappers.LabelValues OrganizationToTeams = 2 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "organization_to_teams", + (gogoproto.customtype) = "github.com/gravitational/teleport/api/types/wrappers.Traits" + ]; + + // Teams is the users team membership + repeated string Teams = 3 [ (gogoproto.jsontag) = "teams" ]; } // TeamMapping represents a single team membership mapping. diff --git a/lib/auth/apiserver.go b/lib/auth/apiserver.go index 7d00ca1f273c8..7e7765c818fbf 100644 --- a/lib/auth/apiserver.go +++ b/lib/auth/apiserver.go @@ -215,6 +215,7 @@ func NewAPIServer(config *APIConfig) (http.Handler, error) { srv.DELETE("/:version/github/connectors/:id", srv.withAuth(srv.deleteGithubConnector)) srv.POST("/:version/github/requests/create", srv.withAuth(srv.createGithubAuthRequest)) srv.POST("/:version/github/requests/validate", srv.withAuth(srv.validateGithubAuthCallback)) + srv.GET("/:version/github/requests/get/:id", srv.withAuth(srv.getGithubAuthRequest)) // SSO diag info srv.GET("/:version/sso/diag/:auth/:id", srv.withAuth(srv.getSSODiagnosticInfo)) @@ -1589,6 +1590,14 @@ func (s *APIServer) createGithubAuthRequest(auth ClientI, w http.ResponseWriter, return response, nil } +func (s *APIServer) getGithubAuthRequest(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) { + request, err := auth.GetGithubAuthRequest(r.Context(), p.ByName("id")) + if err != nil { + return nil, trace.Wrap(err) + } + return request, nil +} + // validateGithubAuthCallbackReq is a request to validate Github OAuth2 callback type validateGithubAuthCallbackReq struct { // Query is the callback query string @@ -1626,7 +1635,7 @@ func (s *APIServer) validateGithubAuthCallback(auth ClientI, w http.ResponseWrit if err := httplib.ReadJSON(r, &req); err != nil { return nil, trace.Wrap(err) } - response, err := auth.ValidateGithubAuthCallback(req.Query) + response, err := auth.ValidateGithubAuthCallback(r.Context(), req.Query) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/auth/auth_test.go b/lib/auth/auth_test.go index 6feda6bf01c36..edea65e88ec02 100644 --- a/lib/auth/auth_test.go +++ b/lib/auth/auth_test.go @@ -920,7 +920,15 @@ func TestGithubConnectorCRUDEventsEmitted(t *testing.T) { ctx := context.Background() // test github create event - github, err := types.NewGithubConnector("test", types.GithubConnectorSpecV3{}) + github, err := types.NewGithubConnector("test", types.GithubConnectorSpecV3{ + TeamsToLogins: []types.TeamMapping{ + { + Organization: "octocats", + Team: "dummy", + Logins: []string{"dummy"}, + }, + }, + }) require.NoError(t, err) err = s.a.upsertGithubConnector(ctx, github) require.NoError(t, err) diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 2f30373a4f5c8..c3f30e5f10e97 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -2712,18 +2712,33 @@ func (a *ServerWithRoles) CreateGithubAuthRequest(req services.GithubAuthRequest return nil, trace.Wrap(err) } + // require additional permissions for executing SSO test flow. + if req.SSOTestFlow { + if err := a.authConnectorAction(apidefaults.Namespace, types.KindGithub, types.VerbCreate); err != nil { + return nil, trace.Wrap(err) + } + } + githubReq, err := a.authServer.CreateGithubAuthRequest(req) if err != nil { - // TODO(Tener): Update `testFlow` flag once GitHub SSO starts supporting test flows. - emitSSOLoginFailureEvent(a.authServer.closeCtx, a.authServer.emitter, events.LoginMethodGithub, err, false) + emitSSOLoginFailureEvent(a.authServer.closeCtx, a.authServer.emitter, events.LoginMethodGithub, err, req.SSOTestFlow) return nil, trace.Wrap(err) } return githubReq, nil } -func (a *ServerWithRoles) ValidateGithubAuthCallback(q url.Values) (*GithubAuthResponse, error) { - return a.authServer.ValidateGithubAuthCallback(q) +// GetGithubAuthRequest returns Github auth request if found. +func (a *ServerWithRoles) GetGithubAuthRequest(ctx context.Context, id string) (*services.GithubAuthRequest, error) { + if err := a.action(apidefaults.Namespace, types.KindGithubRequest, types.VerbRead); err != nil { + return nil, trace.Wrap(err) + } + + return a.authServer.GetGithubAuthRequest(ctx, id) +} + +func (a *ServerWithRoles) ValidateGithubAuthCallback(ctx context.Context, q url.Values) (*GithubAuthResponse, error) { + return a.authServer.ValidateGithubAuthCallback(ctx, q) } // EmitAuditEvent emits a single audit event diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index f0d8aaa8e9103..11de0814e6fc0 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -24,6 +24,8 @@ import ( "testing" "time" + "golang.org/x/crypto/ssh" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/constants" @@ -37,7 +39,6 @@ import ( "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" - "golang.org/x/crypto/ssh" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -629,6 +630,180 @@ func TestOIDCAuthRequest(t *testing.T) { } } +func TestGithubAuthRequest(t *testing.T) { + ctx := context.Background() + srv := newTestTLSServer(t) + + emptyRole, err := CreateRole(ctx, srv.Auth(), "test-empty", types.RoleSpecV5{}) + require.NoError(t, err) + + access1Role, err := CreateRole(ctx, srv.Auth(), "test-access-1", types.RoleSpecV5{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + { + Resources: []string{types.KindGithubRequest}, + Verbs: []string{types.VerbCreate}, + }, + }, + }, + }) + require.NoError(t, err) + + access2Role, err := CreateRole(ctx, srv.Auth(), "test-access-2", types.RoleSpecV5{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + { + Resources: []string{types.KindGithub}, + Verbs: []string{types.VerbCreate}, + }, + }, + }, + }) + require.NoError(t, err) + + access3Role, err := CreateRole(ctx, srv.Auth(), "test-access-3", types.RoleSpecV5{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + { + Resources: []string{types.KindGithub, types.KindGithubRequest}, + Verbs: []string{types.VerbCreate}, + }, + }, + }, + }) + require.NoError(t, err) + + readerRole, err := CreateRole(ctx, srv.Auth(), "test-access-4", types.RoleSpecV5{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + { + Resources: []string{types.KindGithubRequest}, + Verbs: []string{types.VerbRead}, + }, + }, + }, + }) + require.NoError(t, err) + + conn, err := types.NewGithubConnector("example", types.GithubConnectorSpecV3{ + ClientID: "example-client-id", + ClientSecret: "example-client-secret", + RedirectURL: "https://localhost:3080/v1/webapi/github/callback", + Display: "sign in with github", + TeamsToLogins: []types.TeamMapping{ + { + Organization: "octocats", + Team: "idp-admin", + Logins: []string{"access"}, + }, + }, + }) + require.NoError(t, err) + + err = srv.Auth().UpsertGithubConnector(context.Background(), conn) + require.NoError(t, err) + + reqNormal := services.GithubAuthRequest{ConnectorID: conn.GetName(), Type: constants.Github} + reqTest := services.GithubAuthRequest{ConnectorID: conn.GetName(), Type: constants.Github, SSOTestFlow: true, ConnectorSpec: &types.GithubConnectorSpecV3{ + ClientID: "example-client-id", + ClientSecret: "example-client-secret", + RedirectURL: "https://localhost:3080/v1/webapi/github/callback", + Display: "sign in with github", + TeamsToLogins: []types.TeamMapping{ + { + Organization: "octocats", + Team: "idp-admin", + Logins: []string{"access"}, + }, + }, + }} + + tests := []struct { + desc string + roles []string + request services.GithubAuthRequest + expectAccessDenied bool + }{ + { + desc: "empty role - no access", + roles: []string{emptyRole.GetName()}, + request: reqNormal, + expectAccessDenied: true, + }, + { + desc: "can create regular request with normal access", + roles: []string{access1Role.GetName()}, + request: reqNormal, + expectAccessDenied: false, + }, + { + desc: "cannot create sso test request with normal access", + roles: []string{access1Role.GetName()}, + request: reqTest, + expectAccessDenied: true, + }, + { + desc: "cannot create normal request with connector access", + roles: []string{access2Role.GetName()}, + request: reqNormal, + expectAccessDenied: true, + }, + { + desc: "cannot create sso test request with connector access", + roles: []string{access2Role.GetName()}, + request: reqTest, + expectAccessDenied: true, + }, + { + desc: "can create regular request with combined access", + roles: []string{access3Role.GetName()}, + request: reqNormal, + expectAccessDenied: false, + }, + { + desc: "can create sso test request with combined access", + roles: []string{access3Role.GetName()}, + request: reqTest, + expectAccessDenied: false, + }, + } + + user, err := CreateUser(srv.Auth(), "dummy") + require.NoError(t, err) + + userReader, err := CreateUser(srv.Auth(), "dummy-reader", readerRole) + require.NoError(t, err) + + clientReader, err := srv.NewClient(TestUser(userReader.GetName())) + require.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + user.SetRoles(tt.roles) + err = srv.Auth().UpsertUser(user) + require.NoError(t, err) + + client, err := srv.NewClient(TestUser(user.GetName())) + require.NoError(t, err) + + request, err := client.CreateGithubAuthRequest(tt.request) + if tt.expectAccessDenied { + require.Error(t, err) + require.True(t, trace.IsAccessDenied(err), "expected access denied, got: %v", err) + return + } + + require.NoError(t, err) + require.NotEmpty(t, request.StateToken) + require.Equal(t, tt.request.ConnectorID, request.ConnectorID) + + requestCopy, err := clientReader.GetGithubAuthRequest(ctx, request.StateToken) + require.NoError(t, err) + require.Equal(t, request, requestCopy) + }) + } +} + func TestSSODiagnosticInfo(t *testing.T) { ctx := context.Background() srv := newTestTLSServer(t) diff --git a/lib/auth/clt.go b/lib/auth/clt.go index e4fc949afb55a..cc494f882212e 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -1219,9 +1219,22 @@ func (c *Client) CreateGithubAuthRequest(req services.GithubAuthRequest) (*servi return &response, nil } +// GetGithubAuthRequest gets Github AuthnRequest +func (c *Client) GetGithubAuthRequest(ctx context.Context, id string) (*services.GithubAuthRequest, error) { + out, err := c.Get(ctx, c.Endpoint("github", "requests", "get", id), url.Values{}) + if err != nil { + return nil, trace.Wrap(err) + } + var response services.GithubAuthRequest + if err := json.Unmarshal(out.Bytes(), &response); err != nil { + return nil, trace.Wrap(err) + } + return &response, nil +} + // ValidateGithubAuthCallback validates Github auth callback returned from redirect -func (c *Client) ValidateGithubAuthCallback(q url.Values) (*GithubAuthResponse, error) { - out, err := c.PostJSON(context.TODO(), c.Endpoint("github", "requests", "validate"), +func (c *Client) ValidateGithubAuthCallback(ctx context.Context, q url.Values) (*GithubAuthResponse, error) { + out, err := c.PostJSON(ctx, c.Endpoint("github", "requests", "validate"), validateGithubAuthCallbackReq{Query: q}) if err != nil { return nil, trace.Wrap(err) @@ -1706,8 +1719,10 @@ type IdentityService interface { DeleteGithubConnector(ctx context.Context, id string) error // CreateGithubAuthRequest creates a new request for Github OAuth2 flow CreateGithubAuthRequest(services.GithubAuthRequest) (*services.GithubAuthRequest, error) + // GetGithubAuthRequest returns Github auth request if found + GetGithubAuthRequest(ctx context.Context, id string) (*services.GithubAuthRequest, error) // ValidateGithubAuthCallback validates Github auth callback - ValidateGithubAuthCallback(q url.Values) (*GithubAuthResponse, error) + ValidateGithubAuthCallback(ctx context.Context, q url.Values) (*GithubAuthResponse, error) // GetUser returns user by name GetUser(name string, withSecrets bool) (types.User, error) diff --git a/lib/auth/github.go b/lib/auth/github.go index 2872aeee55bfb..989699195cff7 100644 --- a/lib/auth/github.go +++ b/lib/auth/github.go @@ -42,11 +42,7 @@ import ( // CreateGithubAuthRequest creates a new request for Github OAuth2 flow func (a *Server) CreateGithubAuthRequest(req services.GithubAuthRequest) (*services.GithubAuthRequest, error) { ctx := context.TODO() - connector, err := a.Identity.GetGithubConnector(ctx, req.ConnectorID, true) - if err != nil { - return nil, trace.Wrap(err) - } - client, err := a.getGithubOAuth2Client(connector) + _, client, err := a.getGithubConnectorAndClient(ctx, req) if err != nil { return nil, trace.Wrap(err) } @@ -128,12 +124,14 @@ type GithubAuthResponse struct { } type githubManager interface { - validateGithubAuthCallback(q url.Values) (*githubAuthResponse, error) + validateGithubAuthCallback(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) + newSSODiagContext(authKind string) *ssoDiagContext } // ValidateGithubAuthCallback validates Github auth callback redirect -func (a *Server) ValidateGithubAuthCallback(q url.Values) (*GithubAuthResponse, error) { - return validateGithubAuthCallbackHelper(a.closeCtx, a, q, a.emitter) +func (a *Server) ValidateGithubAuthCallback(ctx context.Context, q url.Values) (*GithubAuthResponse, error) { + + return validateGithubAuthCallbackHelper(ctx, a, q, a.emitter) } func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, q url.Values, emitter apievents.Emitter) (*GithubAuthResponse, error) { @@ -144,9 +142,16 @@ func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, q ur Method: events.LoginMethodGithub, } - re, err := m.validateGithubAuthCallback(q) - if re != nil && re.claims != nil { - attributes, err := apievents.EncodeMapStrings(re.claims) + diagCtx := m.newSSODiagContext(types.KindGithub) + + auth, err := m.validateGithubAuthCallback(ctx, diagCtx, q) + diagCtx.info.Error = trace.UserMessage(err) + + diagCtx.writeToBackend(ctx) + + claims := diagCtx.info.GithubClaims + if claims != nil { + attributes, err := apievents.EncodeMapStrings(claims.OrganizationToTeams) if err != nil { event.Status.UserMessage = fmt.Sprintf("Failed to encode identity attributes: %v", err.Error()) log.WithError(err).Debug("Failed to encode identity attributes.") @@ -157,6 +162,9 @@ func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, q ur if err != nil { event.Code = events.UserSSOLoginFailureCode + if diagCtx.info.TestFlow { + event.Code = events.UserSSOTestFlowLoginFailureCode + } event.Status.Success = false event.Status.Error = trace.Unwrap(err).Error() event.Status.UserMessage = err.Error() @@ -167,64 +175,163 @@ func validateGithubAuthCallbackHelper(ctx context.Context, m githubManager, q ur return nil, trace.Wrap(err) } event.Code = events.UserSSOLoginCode + if diagCtx.info.TestFlow { + event.Code = events.UserSSOTestFlowLoginCode + } event.Status.Success = true - event.User = re.auth.Username + event.User = auth.Username if err := emitter.EmitAuditEvent(ctx, event); err != nil { log.WithError(err).Warn("Failed to emit Github login event.") } - return &re.auth, nil + return auth, nil } -type githubAuthResponse struct { - auth GithubAuthResponse - claims map[string][]string +func (a *Server) getGithubConnectorAndClient(ctx context.Context, request services.GithubAuthRequest) (types.GithubConnector, *oauth2.Client, error) { + if request.SSOTestFlow { + if request.ConnectorSpec == nil { + return nil, nil, trace.BadParameter("ConnectorSpec cannot be nil when SSOTestFlow is true") + } + + if request.ConnectorID == "" { + return nil, nil, trace.BadParameter("ConnectorID cannot be empty") + } + + // stateless test flow + connector, err := types.NewGithubConnector(request.ConnectorID, *request.ConnectorSpec) + if err != nil { + return nil, nil, trace.Wrap(err) + } + + // construct client directly. + config := newGithubOAuth2Config(connector) + client, err := oauth2.NewClient(http.DefaultClient, config) + if err != nil { + return nil, nil, trace.Wrap(err) + } + + return connector, client, nil + } + + // regular execution flow + connector, err := a.Identity.GetGithubConnector(ctx, request.ConnectorID, true) + if err != nil { + return nil, nil, trace.Wrap(err) + } + + client, err := a.getGithubOAuth2Client(connector) + if err != nil { + return nil, nil, trace.Wrap(err) + } + + return connector, client, nil +} + +func newGithubOAuth2Config(connector types.GithubConnector) oauth2.Config { + return oauth2.Config{ + Credentials: oauth2.ClientCredentials{ + ID: connector.GetClientID(), + Secret: connector.GetClientSecret(), + }, + RedirectURL: connector.GetRedirectURL(), + Scope: GithubScopes, + AuthURL: GithubAuthURL, + TokenURL: GithubTokenURL, + } +} + +func (a *Server) getGithubOAuth2Client(connector types.GithubConnector) (*oauth2.Client, error) { + config := newGithubOAuth2Config(connector) + + a.lock.Lock() + defer a.lock.Unlock() + + cachedClient, ok := a.githubClients[connector.GetName()] + if ok && oauth2ConfigsEqual(cachedClient.config, config) { + return cachedClient.client, nil + } + + delete(a.githubClients, connector.GetName()) + client, err := oauth2.NewClient(http.DefaultClient, config) + if err != nil { + return nil, trace.Wrap(err) + } + a.githubClients[connector.GetName()] = &githubClient{ + client: client, + config: config, + } + return client, nil } // ValidateGithubAuthCallback validates Github auth callback redirect -func (a *Server) validateGithubAuthCallback(q url.Values) (*githubAuthResponse, error) { - ctx := context.TODO() +func (a *Server) validateGithubAuthCallback(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) { logger := log.WithFields(logrus.Fields{trace.Component: "github"}) - error := q.Get("error") - if error != "" { - return nil, trace.OAuth2(oauth2.ErrorInvalidRequest, error, q) + + if errParam := q.Get("error"); errParam != "" { + // try to find request so the error gets logged against it. + state := q.Get("state") + if state != "" { + diagCtx.requestID = state + req, err := a.Identity.GetGithubAuthRequest(ctx, state) + if err == nil { + diagCtx.info.TestFlow = req.SSOTestFlow + } + } + + // optional parameter: error_description + errDesc := q.Get("error_description") + return nil, trace.OAuth2(oauth2.ErrorInvalidRequest, errParam, q).AddUserMessage("Github returned error: %v [%v]", errDesc, errParam) } + code := q.Get("code") if code == "" { return nil, trace.OAuth2(oauth2.ErrorInvalidRequest, - "code query param must be set", q) + "code query param must be set", q).AddUserMessage("Invalid parameters received from Github.") } + stateToken := q.Get("state") if stateToken == "" { return nil, trace.OAuth2(oauth2.ErrorInvalidRequest, - "missing state query param", q) + "missing state query param", q).AddUserMessage("Invalid parameters received from Github.") } - req, err := a.Identity.GetGithubAuthRequest(stateToken) + diagCtx.requestID = stateToken + + req, err := a.Identity.GetGithubAuthRequest(ctx, stateToken) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to get OIDC Auth Request.") } - connector, err := a.Identity.GetGithubConnector(ctx, req.ConnectorID, true) + diagCtx.info.TestFlow = req.SSOTestFlow + + connector, client, err := a.getGithubConnectorAndClient(ctx, *req) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to get Github connector and client.") } + diagCtx.info.GithubTeamsToLogins = connector.GetTeamsToLogins() + logger.Debugf("Connector %q teams to logins: %v", connector.GetName(), connector.GetTeamsToLogins()) + if len(connector.GetTeamsToLogins()) == 0 { logger.Warnf("Github connector %q has empty teams_to_logins mapping, cannot populate claims.", connector.GetName()) return nil, trace.BadParameter( "connector %q has empty teams_to_logins mapping", connector.GetName()) } - client, err := a.getGithubOAuth2Client(connector) - if err != nil { - return nil, trace.Wrap(err) - } + // exchange the authorization code received by the callback for an access token token, err := client.RequestToken(oauth2.GrantTypeAuthCode, code) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "Requesting Github OAuth2 token failed.") + } + + diagCtx.info.GithubTokenInfo = &types.GithubTokenInfo{ + TokenType: token.TokenType, + Expires: int64(token.Expires), + Scope: token.Scope, } + logger.Debugf("Obtained OAuth2 token: Type=%v Expires=%v Scope=%v.", token.TokenType, token.Expires, token.Scope) + // Github does not support OIDC so user claims have to be populated // by making requests to Github API using the access token claims, err := populateGithubClaims(&githubAPIClient{ @@ -232,26 +339,35 @@ func (a *Server) validateGithubAuthCallback(q url.Values) (*githubAuthResponse, authServer: a, }) if err != nil { - return nil, trace.Wrap(err) - } - - re := &githubAuthResponse{ - claims: claims.OrganizationToTeams, + return nil, trace.Wrap(err, "Failed to query Github API for user claims.") } + diagCtx.info.GithubClaims = claims // Calculate (figure out name, roles, traits, session TTL) of user and // create the user in the backend. params, err := a.calculateGithubUser(connector, claims, req) if err != nil { - return re, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to calculate user attributes.") + } + + diagCtx.info.CreateUserParams = &types.CreateUserParams{ + ConnectorName: params.connectorName, + Username: params.username, + Logins: params.logins, + KubeGroups: params.kubeGroups, + KubeUsers: params.kubeUsers, + Roles: params.roles, + Traits: params.traits, + SessionTTL: types.Duration(params.sessionTTL), } - user, err := a.createGithubUser(params) + + user, err := a.createGithubUser(ctx, params, req.SSOTestFlow) if err != nil { - return re, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to create user from provided parameters.") } // Auth was successful, return session, certificate, etc. to caller. - re.auth = GithubAuthResponse{ + auth := GithubAuthResponse{ Req: *req, Identity: types.ExternalIdentity{ ConnectorID: params.connectorName, @@ -259,7 +375,12 @@ func (a *Server) validateGithubAuthCallback(q url.Values) (*githubAuthResponse, }, Username: user.GetName(), } - re.auth.Username = user.GetName() + + // In test flow skip signing and creating web sessions. + if req.SSOTestFlow { + diagCtx.info.Success = true + return &auth, nil + } // If the request is coming from a browser, create a web session. if req.CreateWebSession { @@ -271,26 +392,26 @@ func (a *Server) validateGithubAuthCallback(q url.Values) (*githubAuthResponse, LoginTime: a.clock.Now().UTC(), }) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to create web session.") } - re.auth.Session = session + auth.Session = session } // If a public key was provided, sign it and return a certificate. if len(req.PublicKey) != 0 { sshCert, tlsCert, err := a.createSessionCert(user, params.sessionTTL, req.PublicKey, req.Compatibility, req.RouteToCluster, req.KubernetesCluster) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to create session certificate.") } clusterName, err := a.GetClusterName() if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to obtain cluster name.") } - re.auth.Cert = sshCert - re.auth.TLSCert = tlsCert + auth.Cert = sshCert + auth.TLSCert = tlsCert // Return the host CA for this cluster only. authority, err := a.GetCertAuthority(ctx, types.CertAuthID{ @@ -298,12 +419,12 @@ func (a *Server) validateGithubAuthCallback(q url.Values) (*githubAuthResponse, DomainName: clusterName.GetClusterName(), }, false) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "Failed to obtain cluster's host CA.") } - re.auth.HostSigners = append(re.auth.HostSigners, authority) + auth.HostSigners = append(auth.HostSigners, authority) } - return re, nil + return &auth, nil } // createUserParams is a set of parameters used to create a user for an @@ -344,12 +465,12 @@ func (a *Server) calculateGithubUser(connector types.GithubConnector, claims *ty p.logins, p.kubeGroups, p.kubeUsers = connector.MapClaims(*claims) if len(p.logins) == 0 { return nil, trace.BadParameter( - "user %q does not belong to any teams configured in %q connector", + "user %q does not belong to any teams configured in %q connector; the configuration may have typos.", claims.Username, connector.GetName()) } p.roles = p.logins p.traits = map[string][]string{ - teleport.TraitLogins: []string{p.username}, + teleport.TraitLogins: {p.username}, teleport.TraitKubeGroups: p.kubeGroups, teleport.TraitKubeUsers: p.kubeUsers, teleport.TraitTeams: claims.Teams, @@ -366,11 +487,10 @@ func (a *Server) calculateGithubUser(connector types.GithubConnector, claims *ty return &p, nil } -func (a *Server) createGithubUser(p *createUserParams) (types.User, error) { - +func (a *Server) createGithubUser(ctx context.Context, p *createUserParams, dryRun bool) (types.User, error) { log.WithFields(logrus.Fields{trace.Component: "github"}).Debugf( - "Generating dynamic identity %v/%v with logins: %v.", - p.connectorName, p.username, p.logins) + "Generating dynamic GitHub identity %v/%v with logins: %v. Dry run: %v.", + p.connectorName, p.username, p.logins, dryRun) expires := a.GetClock().Now().UTC().Add(p.sessionTTL) @@ -401,13 +521,15 @@ func (a *Server) createGithubUser(p *createUserParams) (types.User, error) { }, } + if dryRun { + return user, nil + } + existingUser, err := a.GetUser(p.username, false) if err != nil && !trace.IsNotFound(err) { return nil, trace.Wrap(err) } - ctx := context.TODO() - if existingUser != nil { ref := user.GetCreatedBy().Connector if !ref.IsSameProvider(existingUser.GetCreatedBy().Connector) { @@ -463,35 +585,6 @@ func populateGithubClaims(client githubAPIClientI) (*types.GithubClaims, error) return claims, nil } -func (a *Server) getGithubOAuth2Client(connector types.GithubConnector) (*oauth2.Client, error) { - a.lock.Lock() - defer a.lock.Unlock() - config := oauth2.Config{ - Credentials: oauth2.ClientCredentials{ - ID: connector.GetClientID(), - Secret: connector.GetClientSecret(), - }, - RedirectURL: connector.GetRedirectURL(), - Scope: GithubScopes, - AuthURL: GithubAuthURL, - TokenURL: GithubTokenURL, - } - cachedClient, ok := a.githubClients[connector.GetName()] - if ok && oauth2ConfigsEqual(cachedClient.config, config) { - return cachedClient.client, nil - } - delete(a.githubClients, connector.GetName()) - client, err := oauth2.NewClient(http.DefaultClient, config) - if err != nil { - return nil, trace.Wrap(err) - } - a.githubClients[connector.GetName()] = &githubClient{ - client: client, - config: config, - } - return client, nil -} - // githubAPIClientI defines an interface for Github API wrapper // so it can be substituted in tests type githubAPIClientI interface { diff --git a/lib/auth/github_test.go b/lib/auth/github_test.go index ab71e6db1de0f..a0e5b417b768d 100644 --- a/lib/auth/github_test.go +++ b/lib/auth/github_test.go @@ -29,8 +29,10 @@ import ( "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/events/eventstest" "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/trace" + "github.com/google/uuid" "github.com/jonboulle/clockwork" "gopkg.in/check.v1" ) @@ -89,14 +91,29 @@ func (s *GithubSuite) TestPopulateClaims(c *check.C) { } func (s *GithubSuite) TestCreateGithubUser(c *check.C) { + // Dry-run creation of Github user. + user, err := s.a.createGithubUser(context.Background(), &createUserParams{ + connectorName: "github", + username: "foo@example.com", + logins: []string{"foo"}, + roles: []string{"admin"}, + sessionTTL: 1 * time.Minute, + }, true) + c.Assert(err, check.IsNil) + c.Assert(user.GetName(), check.Equals, "foo@example.com") + + // Dry-run must not create a user. + _, err = s.a.GetUser("foo@example.com", false) + c.Assert(err, check.NotNil) + // Create GitHub user with 1 minute expiry. - _, err := s.a.createGithubUser(&createUserParams{ + _, err = s.a.createGithubUser(context.Background(), &createUserParams{ connectorName: "github", username: "foo", logins: []string{"foo"}, roles: []string{"admin"}, sessionTTL: 1 * time.Minute, - }) + }, false) c.Assert(err, check.IsNil) // Within that 1 minute period the user should still exist. @@ -136,41 +153,94 @@ func (c *testGithubAPIClient) getTeams() ([]teamResponse, error) { } func (s *GithubSuite) TestValidateGithubAuthCallbackEventsEmitted(c *check.C) { - auth := &githubAuthResponse{ - auth: GithubAuthResponse{ - Username: "test-name", - }, - claims: map[string][]string{ + auth := &GithubAuthResponse{ + Username: "test-name", + } + + claims := &types.GithubClaims{ + OrganizationToTeams: map[string][]string{ "test": {}, }, } + ssoDiagInfoCalls := 0 + m := &mockedGithubManager{} + m.createSSODiagnosticInfo = func(ctx context.Context, authKind string, authRequestID string, info types.SSODiagnosticInfo) error { + ssoDiagInfoCalls++ + return nil + } + + // TestFlow: false + m.testFlow = false // Test success event. - m.mockValidateGithubAuthCallback = func(q url.Values) (*githubAuthResponse, error) { + m.mockValidateGithubAuthCallback = func(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) { + diagCtx.info.GithubClaims = claims return auth, nil } _, _ = validateGithubAuthCallbackHelper(context.Background(), m, nil, s.a.emitter) c.Assert(s.mockEmitter.LastEvent().GetType(), check.Equals, events.UserLoginEvent) c.Assert(s.mockEmitter.LastEvent().GetCode(), check.Equals, events.UserSSOLoginCode) + c.Assert(ssoDiagInfoCalls, check.Equals, 0) s.mockEmitter.Reset() // Test failure event. - m.mockValidateGithubAuthCallback = func(q url.Values) (*githubAuthResponse, error) { + m.mockValidateGithubAuthCallback = func(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) { + diagCtx.info.GithubClaims = claims return auth, trace.BadParameter("") } _, _ = validateGithubAuthCallbackHelper(context.Background(), m, nil, s.a.emitter) c.Assert(s.mockEmitter.LastEvent().GetCode(), check.Equals, events.UserSSOLoginFailureCode) + c.Assert(ssoDiagInfoCalls, check.Equals, 0) + + // TestFlow: true + m.testFlow = true + + // Test success event. + m.mockValidateGithubAuthCallback = func(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) { + diagCtx.info.GithubClaims = claims + return auth, nil + } + _, _ = validateGithubAuthCallbackHelper(context.Background(), m, nil, s.a.emitter) + c.Assert(s.mockEmitter.LastEvent().GetType(), check.Equals, events.UserLoginEvent) + c.Assert(s.mockEmitter.LastEvent().GetCode(), check.Equals, events.UserSSOTestFlowLoginCode) + c.Assert(ssoDiagInfoCalls, check.Equals, 1) + s.mockEmitter.Reset() + + // Test failure event. + m.mockValidateGithubAuthCallback = func(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) { + diagCtx.info.GithubClaims = claims + return auth, trace.BadParameter("") + } + _, _ = validateGithubAuthCallbackHelper(context.Background(), m, nil, s.a.emitter) + c.Assert(s.mockEmitter.LastEvent().GetCode(), check.Equals, events.UserSSOTestFlowLoginFailureCode) + c.Assert(ssoDiagInfoCalls, check.Equals, 2) } type mockedGithubManager struct { - mockValidateGithubAuthCallback func(q url.Values) (*githubAuthResponse, error) + mockValidateGithubAuthCallback func(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) + createSSODiagnosticInfo func(ctx context.Context, authKind string, authRequestID string, info types.SSODiagnosticInfo) error + + testFlow bool +} + +func (m *mockedGithubManager) newSSODiagContext(authKind string) *ssoDiagContext { + if m.createSSODiagnosticInfo == nil { + panic("mockedGithubManager.createSSODiagnosticInfo is nil, newSSODiagContext cannot succeed.") + } + + return &ssoDiagContext{ + authKind: authKind, + createSSODiagnosticInfo: m.createSSODiagnosticInfo, + requestID: uuid.New().String(), + info: types.SSODiagnosticInfo{TestFlow: m.testFlow}, + } } -func (m *mockedGithubManager) validateGithubAuthCallback(q url.Values) (*githubAuthResponse, error) { +func (m *mockedGithubManager) validateGithubAuthCallback(ctx context.Context, diagCtx *ssoDiagContext, q url.Values) (*GithubAuthResponse, error) { if m.mockValidateGithubAuthCallback != nil { - return m.mockValidateGithubAuthCallback(q) + return m.mockValidateGithubAuthCallback(ctx, diagCtx, q) } return nil, trace.NotImplemented("mockValidateGithubAuthCallback not implemented") diff --git a/lib/auth/oidc.go b/lib/auth/oidc.go index 1ff07268b211f..227248fe3b4c5 100644 --- a/lib/auth/oidc.go +++ b/lib/auth/oidc.go @@ -277,9 +277,7 @@ func (a *Server) ValidateOIDCAuthCallback(ctx context.Context, q url.Values) (*O diagCtx := a.newSSODiagContext(types.KindOIDC) auth, err := a.validateOIDCAuthCallback(ctx, diagCtx, q) - if err != nil { - diagCtx.info.Error = trace.UserMessage(err) - } + diagCtx.info.Error = trace.UserMessage(err) diagCtx.writeToBackend(ctx) @@ -593,7 +591,7 @@ func (a *Server) calculateOIDCUser(diagCtx *ssoDiagContext, connector types.OIDC func (a *Server) createOIDCUser(p *createUserParams, dryRun bool) (types.User, error) { expires := a.GetClock().Now().UTC().Add(p.sessionTTL) - log.Debugf("Generating dynamic OIDC identity %v/%v with roles: %v.", p.connectorName, p.username, p.roles) + log.Debugf("Generating dynamic OIDC identity %v/%v with roles: %v. Dry run: %v.", p.connectorName, p.username, p.roles, dryRun) user := &types.UserV2{ Kind: types.KindUser, Version: types.V2, @@ -775,7 +773,7 @@ func (a *Server) getClaims(oidcClient *oidc.Client, connector types.OIDCConnecto return a.getClaimsFun(a.closeCtx, oidcClient, connector, code) } -// getClaimsFun implements Server.getClaims, but allows that code path to be overridden for testing. +// getClaims implements Server.getClaims, but allows that code path to be overridden for testing. func getClaims(closeCtx context.Context, oidcClient *oidc.Client, connector types.OIDCConnector, code string) (jose.Claims, error) { oac, err := getOAuthClient(oidcClient, connector) diff --git a/lib/auth/saml.go b/lib/auth/saml.go index 795d8e152e67c..0b03d452fc604 100644 --- a/lib/auth/saml.go +++ b/lib/auth/saml.go @@ -84,7 +84,7 @@ func (a *Server) DeleteSAMLConnector(ctx context.Context, connectorName string) func (a *Server) CreateSAMLAuthRequest(req services.SAMLAuthRequest) (*services.SAMLAuthRequest, error) { ctx := context.TODO() - connector, provider, err := a.getConnectorAndProvider(ctx, req) + connector, provider, err := a.getSAMLConnectorAndProvider(ctx, req) if err != nil { return nil, trace.Wrap(err) } @@ -123,7 +123,7 @@ func (a *Server) CreateSAMLAuthRequest(req services.SAMLAuthRequest) (*services. return &req, nil } -func (a *Server) getConnectorAndProvider(ctx context.Context, req services.SAMLAuthRequest) (types.SAMLConnector, *saml2.SAMLServiceProvider, error) { +func (a *Server) getSAMLConnectorAndProvider(ctx context.Context, req services.SAMLAuthRequest) (types.SAMLConnector, *saml2.SAMLServiceProvider, error) { if req.SSOTestFlow { if req.ConnectorSpec == nil { return nil, nil, trace.BadParameter("ConnectorSpec cannot be nil when SSOTestFlow is true") @@ -230,7 +230,7 @@ func (a *Server) calculateSAMLUser(diagCtx *ssoDiagContext, connector types.SAML func (a *Server) createSAMLUser(p *createUserParams, dryRun bool) (types.User, error) { expires := a.GetClock().Now().UTC().Add(p.sessionTTL) - log.Debugf("Generating dynamic SAML identity %v/%v with roles: %v.", p.connectorName, p.username, p.roles) + log.Debugf("Generating dynamic SAML identity %v/%v with roles: %v. Dry run: %v.", p.connectorName, p.username, p.roles, dryRun) user := &types.UserV2{ Kind: types.KindUser, @@ -372,9 +372,7 @@ func (a *Server) ValidateSAMLResponse(ctx context.Context, samlResponse string) diagCtx := a.newSSODiagContext(types.KindSAML) auth, err := a.validateSAMLResponse(ctx, diagCtx, samlResponse) - if err != nil { - diagCtx.info.Error = trace.UserMessage(err) - } + diagCtx.info.Error = trace.UserMessage(err) diagCtx.writeToBackend(ctx) @@ -430,7 +428,7 @@ func (a *Server) validateSAMLResponse(ctx context.Context, diagCtx *ssoDiagConte } diagCtx.info.TestFlow = request.SSOTestFlow - connector, provider, err := a.getConnectorAndProvider(ctx, *request) + connector, provider, err := a.getSAMLConnectorAndProvider(ctx, *request) if err != nil { return nil, trace.Wrap(err, "Failed to get SAML connector and provider") } diff --git a/lib/auth/saml_test.go b/lib/auth/saml_test.go index 60bd679e958ce..543577f182f7b 100644 --- a/lib/auth/saml_test.go +++ b/lib/auth/saml_test.go @@ -318,7 +318,7 @@ func TestServer_getConnectorAndProvider(t *testing.T) { }, } - connector, provider, err := a.getConnectorAndProvider(context.Background(), request) + connector, provider, err := a.getSAMLConnectorAndProvider(context.Background(), request) require.NoError(t, err) require.NotNil(t, connector) require.NotNil(t, provider) @@ -353,7 +353,7 @@ func TestServer_getConnectorAndProvider(t *testing.T) { SSOTestFlow: false, } - connector, provider, err = a.getConnectorAndProvider(context.Background(), request2) + connector, provider, err = a.getSAMLConnectorAndProvider(context.Background(), request2) require.NoError(t, err) require.NotNil(t, connector) require.NotNil(t, provider) diff --git a/lib/services/identity.go b/lib/services/identity.go index 81a9c4b4c7649..6bb34850bd504 100644 --- a/lib/services/identity.go +++ b/lib/services/identity.go @@ -231,7 +231,7 @@ type Identity interface { CreateGithubAuthRequest(req GithubAuthRequest) error // GetGithubAuthRequest retrieves Github auth request by the token - GetGithubAuthRequest(stateToken string) (*GithubAuthRequest, error) + GetGithubAuthRequest(ctx context.Context, stateToken string) (*GithubAuthRequest, error) // CreateUserToken creates a new user token. CreateUserToken(ctx context.Context, token types.UserToken) (types.UserToken, error) @@ -333,6 +333,10 @@ type GithubAuthRequest struct { RouteToCluster string `json:"route_to_cluster,omitempty"` // KubernetesCluster is the name of Kubernetes cluster to issue credentials for. KubernetesCluster string `json:"kubernetes_cluster,omitempty"` + // SSOTestFlow indicates if the request is part of the test flow. + SSOTestFlow bool `json:"sso_test_flow"` + // ConnectorSpec is embedded connector spec for use in test flow. + ConnectorSpec *types.GithubConnectorSpecV3 `json:"connector_spec,omitempty"` } // SetExpiry sets expiry time for the object @@ -365,6 +369,16 @@ func (r *GithubAuthRequest) Check() error { return trace.BadParameter("wrong CertTTL") } } + + // we could collapse these two checks into one, but the error message would become ambiguous. + if r.SSOTestFlow && r.ConnectorSpec == nil { + return trace.BadParameter("ConnectorSpec cannot be nil when SSOTestFlow is true") + } + + if !r.SSOTestFlow && r.ConnectorSpec != nil { + return trace.BadParameter("ConnectorSpec must be nil when SSOTestFlow is false") + } + return nil } diff --git a/lib/services/identity_test.go b/lib/services/identity_test.go index 89672000cb19e..1548ce10b2925 100644 --- a/lib/services/identity_test.go +++ b/lib/services/identity_test.go @@ -21,6 +21,7 @@ import ( "time" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/trace" "github.com/stretchr/testify/require" ) @@ -218,3 +219,108 @@ func TestOIDCAuthRequest_Check(t *testing.T) { }) } } + +func TestGithubAuthRequest_Check(t *testing.T) { + const exampleSSHCert = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb1srW/W3ZDjYAO45xLYAwzHBDLsJ4Ux6ICFIkTjb1LEAAAADAQABAAAAYQCkoR51poH0wE8w72cqSB8Sszx+vAhzcMdCO0wqHTj7UNENHWEXGrU0E0UQekD7U+yhkhtoyjbPOVIP7hNa6aRk/ezdh/iUnCIt4Jt1v3Z1h1P+hA4QuYFMHNB+rmjPwAcAAAAAAAAAAAAAAAEAAAAEdGVzdAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAHcAAAAHc3NoLXJzYQAAAAMBAAEAAABhANFS2kaktpSGc+CcmEKPyw9mJC4nZKxHKTgLVZeaGbFZOvJTNzBspQHdy7Q1uKSfktxpgjZnksiu/tFF9ngyY2KFoc+U88ya95IZUycBGCUbBQ8+bhDtw/icdDGQD5WnUwAAAG8AAAAHc3NoLXJzYQAAAGC8Y9Z2LQKhIhxf52773XaWrXdxP0t3GBVo4A10vUWiYoAGepr6rQIoGGXFxT4B9Gp+nEBJjOwKDXPrAevow0T9ca8gZN+0ykbhSrXLE5Ao48rqr3zP4O1/9P7e6gp0gw8=` + + tests := []struct { + name string + req GithubAuthRequest + wantErr bool + }{ + { + name: "normal request", + req: GithubAuthRequest{ + ConnectorID: "foo", + StateToken: "bar", + PublicKey: []byte(exampleSSHCert), + CertTTL: 60 * time.Minute, + }, + wantErr: false, + }, + { + name: "missing state token", + req: GithubAuthRequest{ + ConnectorID: "foo", + PublicKey: []byte(exampleSSHCert), + CertTTL: 60 * time.Minute, + }, + wantErr: true, + }, + { + name: "below min CertTTL", + req: GithubAuthRequest{ + ConnectorID: "foo", + StateToken: "bar", + PublicKey: []byte(exampleSSHCert), + CertTTL: 1 * time.Second, + }, + wantErr: true, + }, + { + name: "above max CertTTL", + req: GithubAuthRequest{ + ConnectorID: "foo", + StateToken: "bar", + PublicKey: []byte(exampleSSHCert), + CertTTL: 1000 * time.Hour, + }, + wantErr: true, + }, + { + name: "TTL ignored without cert", + req: GithubAuthRequest{ + ConnectorID: "foo", + StateToken: "bar", + CertTTL: 60 * time.Minute, + }, + wantErr: false, + }, + { + name: "SSOTestFlow requires ConnectorSpec", + req: GithubAuthRequest{ + ConnectorID: "foo", + StateToken: "bar", + PublicKey: []byte(exampleSSHCert), + CertTTL: 60 * time.Minute, + SSOTestFlow: true, + }, + wantErr: true, + }, + { + name: "ConnectorSpec requires SSOTestFlow", + req: GithubAuthRequest{ + ConnectorID: "foo", + StateToken: "bar", + PublicKey: []byte(exampleSSHCert), + CertTTL: 60 * time.Minute, + ConnectorSpec: &types.GithubConnectorSpecV3{Display: "dummy"}, + }, + wantErr: true, + }, + { + name: "ConnectorSpec with SSOTestFlow works", + req: GithubAuthRequest{ + ConnectorID: "foo", + StateToken: "bar", + PublicKey: []byte(exampleSSHCert), + CertTTL: 60 * time.Minute, + SSOTestFlow: true, + ConnectorSpec: &types.GithubConnectorSpecV3{Display: "dummy"}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.req.Check() + if tt.wantErr { + require.Error(t, err) + require.True(t, trace.IsBadParameter(err)) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/lib/services/local/users.go b/lib/services/local/users.go index b4023fb91279a..4718edca1a5d8 100644 --- a/lib/services/local/users.go +++ b/lib/services/local/users.go @@ -25,13 +25,14 @@ import ( "sync" "time" - "github.com/gokyle/hotp" - "github.com/google/go-cmp/cmp" - "github.com/google/uuid" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/services" + + "github.com/gokyle/hotp" + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/sirupsen/logrus" @@ -1388,11 +1389,11 @@ func (s *IdentityService) CreateGithubAuthRequest(req services.GithubAuthRequest } // GetGithubAuthRequest retrieves Github auth request by the token -func (s *IdentityService) GetGithubAuthRequest(stateToken string) (*services.GithubAuthRequest, error) { +func (s *IdentityService) GetGithubAuthRequest(ctx context.Context, stateToken string) (*services.GithubAuthRequest, error) { if stateToken == "" { return nil, trace.BadParameter("missing parameter stateToken") } - item, err := s.Get(context.TODO(), backend.Key(webPrefix, connectorsPrefix, githubPrefix, requestsPrefix, stateToken)) + item, err := s.Get(ctx, backend.Key(webPrefix, connectorsPrefix, githubPrefix, requestsPrefix, stateToken)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 26afe44e756a1..bc6d64b364647 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -1142,9 +1142,22 @@ func (h *Handler) githubCallback(w http.ResponseWriter, r *http.Request, p httpr logger := h.log.WithField("auth", "github") logger.Debugf("Callback start: %v.", r.URL.Query()) - response, err := h.cfg.ProxyClient.ValidateGithubAuthCallback(r.URL.Query()) + response, err := h.cfg.ProxyClient.ValidateGithubAuthCallback(r.Context(), r.URL.Query()) if err != nil { logger.WithError(err).Error("Error while processing callback.") + + // try to find the auth request, which bears the original client redirect URL. + // if found, use it to terminate the flow. + // + // this improves the UX by terminating the failed SSO flow immediately, rather than hoping for a timeout. + if requestID := r.URL.Query().Get("state"); requestID != "" { + if request, errGet := h.cfg.ProxyClient.GetGithubAuthRequest(r.Context(), requestID); errGet == nil && !request.CreateWebSession { + if redURL, errEnc := redirectURLWithError(request.ClientRedirectURL, err); errEnc == nil { + return redURL.String() + } + } + } + return client.LoginFailedBadCallbackRedirectURL } diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 9c3c150b0bf48..4ae9b6f01f191 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -2468,7 +2468,15 @@ func TestGetWebConfig(t *testing.T) { require.NoError(t, err) // Add a test connector. - github, err := types.NewGithubConnector("test-github", types.GithubConnectorSpecV3{}) + github, err := types.NewGithubConnector("test-github", types.GithubConnectorSpecV3{ + TeamsToLogins: []types.TeamMapping{ + { + Organization: "octocats", + Team: "dummy", + Logins: []string{"dummy"}, + }, + }, + }) require.NoError(t, err) err = env.server.Auth().CreateGithubConnector(github) require.NoError(t, err) diff --git a/lib/web/resources_test.go b/lib/web/resources_test.go index 939722032ba64..5a9fc8e0541a1 100644 --- a/lib/web/resources_test.go +++ b/lib/web/resources_test.go @@ -80,19 +80,32 @@ spec: client_secret: "" display: "" redirect_url: "" - teams_to_logins: null + teams_to_logins: + - logins: + - dummy + organization: octocats + team: dummy version: v3 ` - githubConn, err := types.NewGithubConnector("githubName", types.GithubConnectorSpecV3{}) + githubConn, err := types.NewGithubConnector("githubName", types.GithubConnectorSpecV3{ + TeamsToLogins: []types.TeamMapping{ + { + Organization: "octocats", + Team: "dummy", + Logins: []string{"dummy"}, + }, + }, + }) require.NoError(t, err) item, err := ui.NewResourceItem(githubConn) - require.Nil(t, err) - require.Equal(t, item, &ui.ResourceItem{ + require.NoError(t, err) + + require.Equal(t, &ui.ResourceItem{ ID: "github:githubName", Kind: types.KindGithubConnector, Name: "githubName", Content: contents, - }) + }, item) } func TestNewResourceItemRole(t *testing.T) { @@ -232,7 +245,15 @@ func TestGetGithubConnectors(t *testing.T) { m := &mockedResourceAPIGetter{} m.mockGetGithubConnectors = func(ctx context.Context, withSecrets bool) ([]types.GithubConnector, error) { - connector, err := types.NewGithubConnector("test", types.GithubConnectorSpecV3{}) + connector, err := types.NewGithubConnector("test", types.GithubConnectorSpecV3{ + TeamsToLogins: []types.TeamMapping{ + { + Organization: "octocats", + Team: "dummy", + Logins: []string{"dummy"}, + }, + }, + }) require.NoError(t, err) return []types.GithubConnector{connector}, nil diff --git a/rfd/0070-tctl-sso-configure-command.md b/rfd/0070-tctl-sso-configure-command.md index 3a036449465ab..d445bbba46b74 100644 --- a/rfd/0070-tctl-sso-configure-command.md +++ b/rfd/0070-tctl-sso-configure-command.md @@ -1,6 +1,6 @@ --- authors: Krzysztof Skrzętnicki -state: draft +state: implemented --- # RFD 70 - `tctl sso configure` command @@ -55,8 +55,8 @@ For `SAML` the commonly used flags will be: -p, --preset Preset. One of: [okta onelogin ad adfs] -n, --name Connector name. Required, unless implied from preset. -e, --entity-descriptor Set the Entity Descriptor. Valid values: file, URL, XML content. Supplies configuration parameters as single XML instead of individual elements. --r, --attributes-to-roles Sets attribute-to-role mapping in the form 'attr_name,attr_value,role1,role2,...'. Repeatable. - --display Display controls how this connector is displayed. +-r, --attributes-to-roles Sets attribute-to-role mapping using format 'attr_name,attr_value,role1,role2,...'. Repeatable. + --display Sets the connector display name. ``` The `--attributes-to-roles/-a` flag is particularly important as it is used to provide a mapping between the @@ -96,7 +96,7 @@ Advanced features: Flags for ignoring warnings: ``` ---ignore-missing-roles Ignore non-existing roles referenced in --attributes-to-roles. +--ignore-missing-roles Ignore missing roles referenced in --attributes-to-roles. --ignore-missing-certs Ignore the lack of valid certificates from -e and --cert. ``` diff --git a/rfd/0071-tctl-sso-test-command.md b/rfd/0071-tctl-sso-test-command.md index d0a79b0f7bb94..2d908d54532ad 100644 --- a/rfd/0071-tctl-sso-test-command.md +++ b/rfd/0071-tctl-sso-test-command.md @@ -1,6 +1,6 @@ --- authors: Krzysztof Skrzętnicki -state: draft +state: implemented --- # RFD 71 - `tctl sso test` command diff --git a/tool/tctl/main.go b/tool/tctl/main.go index 1d5451e75851b..ec7c616dcc8c6 100644 --- a/tool/tctl/main.go +++ b/tool/tctl/main.go @@ -18,6 +18,8 @@ package main import ( "github.com/gravitational/teleport/tool/tctl/common" + "github.com/gravitational/teleport/tool/tctl/sso/configure" + "github.com/gravitational/teleport/tool/tctl/sso/tester" ) func main() { @@ -31,6 +33,8 @@ func main() { &common.StatusCommand{}, &common.TopCommand{}, &common.AccessRequestCommand{}, + &configure.SSOConfigureCommand{}, + &tester.SSOTestCommand{}, &common.AppsCommand{}, &common.DBCommand{}, &common.KubeCommand{}, diff --git a/tool/tctl/sso/configure/command.go b/tool/tctl/sso/configure/command.go new file mode 100644 index 0000000000000..dfdb591aec324 --- /dev/null +++ b/tool/tctl/sso/configure/command.go @@ -0,0 +1,74 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 configure + +import ( + "os" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/service" + "github.com/gravitational/teleport/lib/utils" + + "github.com/gravitational/kingpin" + "github.com/gravitational/trace" + + "github.com/sirupsen/logrus" +) + +// SSOConfigureCommand implements common.CLICommand interface +type SSOConfigureCommand struct { + config *service.Config + ConfigureCmd *kingpin.CmdClause + AuthCommands []*AuthKindCommand + Logger *logrus.Entry +} + +type AuthKindCommand struct { + Parsed bool + Run func(clt auth.ClientI) error +} + +// Initialize allows a caller-defined command to plug itself into CLI +// argument parsing +func (cmd *SSOConfigureCommand) Initialize(app *kingpin.Application, cfg *service.Config) { + cmd.config = cfg + cmd.Logger = cfg.Log.WithField(trace.Component, teleport.ComponentClient) + + sso := app.Command("sso", "A family of commands for configuring and testing auth connectors (SSO).") + cmd.ConfigureCmd = sso.Command("configure", "Create auth connector configuration.") + cmd.AuthCommands = []*AuthKindCommand{addGithubCommand(cmd)} +} + +// TryRun is executed after the CLI parsing is done. The command must +// determine if selectedCommand belongs to it and return match=true +func (cmd *SSOConfigureCommand) TryRun(selectedCommand string, clt auth.ClientI) (match bool, err error) { + for _, subCommand := range cmd.AuthCommands { + if subCommand.Parsed { + // the default tctl logging behaviour is to ignore all logs, unless --debug is present. + // we want different behaviour: log messages as normal, but with compact format (no time, no caller info). + if !cmd.config.Debug { + formatter := utils.NewDefaultTextFormatter(trace.IsTerminal(os.Stderr)) + formatter.FormatCaller = func() (caller string) { return "" } + cmd.Logger.Logger.SetFormatter(formatter) + cmd.Logger.Logger.SetOutput(os.Stderr) + } + + return true, trace.Wrap(subCommand.Run(clt)) + } + } + + return false, nil +} diff --git a/tool/tctl/sso/configure/github.go b/tool/tctl/sso/configure/github.go new file mode 100644 index 0000000000000..424e6b99827d1 --- /dev/null +++ b/tool/tctl/sso/configure/github.go @@ -0,0 +1,161 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 configure + +import ( + "context" + "fmt" + "os" + + "github.com/sirupsen/logrus" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/utils" + + "github.com/gravitational/kingpin" + "github.com/gravitational/trace" +) + +type ghExtraFlags struct { + connectorName string + ignoreMissingRoles bool +} + +func addGithubCommand(cmd *SSOConfigureCommand) *AuthKindCommand { + spec := types.GithubConnectorSpecV3{} + + gh := &ghExtraFlags{} + + sub := cmd.ConfigureCmd.Command("github", "Configure Github auth connector.") + // commonly used flags + sub.Flag("name", "Connector name.").Default("github").Short('n').StringVar(&gh.connectorName) + sub.Flag("teams-to-logins", "Sets teams-to-logins mapping using format 'organization,name,role1,role2,...'. Repeatable."). + Short('r'). + Required(). + PlaceHolder("org,team,role1,role2,..."). + SetValue(newTeamsToLoginsParser(&spec.TeamsToLogins)) + sub.Flag("display", "Sets the connector display name.").StringVar(&spec.Display) + sub.Flag("id", "Github app client ID.").PlaceHolder("ID").Required().StringVar(&spec.ClientID) + sub.Flag("secret", "Github app client secret.").Required().PlaceHolder("SECRET").StringVar(&spec.ClientSecret) + + // auto + sub.Flag("redirect-url", "Authorization callback URL.").PlaceHolder("URL").StringVar(&spec.RedirectURL) + + // ignores + sub.Flag("ignore-missing-roles", "Ignore missing roles referenced in --teams-to-logins.").BoolVar(&gh.ignoreMissingRoles) + + sub.Alias("gh") + + sub.Alias(` +Examples: + + > tctl sso configure gh -r octocats,admin,access,editor,auditor -r octocats,dev,access --secret GH_SECRET --id CLIENT_ID + + Generate Github auth connector. Two role mappings are defined: + - members of 'admin' team in 'octocats' org will receive 'access', 'editor' and 'auditor' roles. + - members of 'dev' team in 'octocats' org will receive 'access' role. + + The values for --secret and --id are provided by GitHub. + + > tctl sso configure gh ... | tctl sso test + + Generate the configuration and immediately test it using "tctl sso test" command.`) + + preset := &AuthKindCommand{ + Run: func(clt auth.ClientI) error { return ghRunFunc(cmd, &spec, gh, clt) }, + } + + sub.Action(func(ctx *kingpin.ParseContext) error { + preset.Parsed = true + return nil + }) + + return preset +} + +func ghRunFunc(cmd *SSOConfigureCommand, spec *types.GithubConnectorSpecV3, flags *ghExtraFlags, clt auth.ClientI) error { + if err := specCheckRoles(cmd.Logger, spec, flags.ignoreMissingRoles, clt); err != nil { + return trace.Wrap(err) + } + + if spec.RedirectURL == "" { + spec.RedirectURL = ResolveCallbackURL(cmd.Logger, clt, "RedirectURL", "https://%v/v1/webapi/github/callback") + } + + connector, err := types.NewGithubConnector(flags.connectorName, *spec) + if err != nil { + return trace.Wrap(err) + } + return trace.Wrap(utils.WriteYAML(os.Stdout, connector)) +} + +// ResolveCallbackURL deals with common pattern of resolving callback URL for IdP to use. +func ResolveCallbackURL(logger *logrus.Entry, clt auth.ClientI, fieldName string, callbackPattern string) string { + var callbackURL string + + logger.Infof("%v empty, resolving automatically.", fieldName) + proxies, err := clt.GetProxies() + if err != nil { + logger.WithError(err).Error("unable to get proxy list.") + } + + // find first proxy with public addr + for _, proxy := range proxies { + publicAddr := proxy.GetPublicAddr() + if publicAddr != "" { + callbackURL = fmt.Sprintf(callbackPattern, publicAddr) + break + } + } + + // check if successfully set. + if callbackURL == "" { + logger.Warnf("Unable to fill %v automatically, cluster's public address unknown.", fieldName) + } else { + logger.Infof("%v set to %q", fieldName, callbackURL) + } + return callbackURL +} + +func specCheckRoles(logger *logrus.Entry, spec *types.GithubConnectorSpecV3, ignoreMissingRoles bool, clt auth.ClientI) error { + allRoles, err := clt.GetRoles(context.TODO()) + if err != nil { + logger.WithError(err).Warn("unable to get roles list. Skipping teams-to-logins sanity checks.") + return nil + } + + roleMap := map[string]bool{} + var roleNames []string + for _, role := range allRoles { + roleMap[role.GetName()] = true + roleNames = append(roleNames, role.GetName()) + } + + for _, mapping := range spec.TeamsToLogins { + for _, role := range mapping.Logins { + _, found := roleMap[role] + if !found { + if ignoreMissingRoles { + logger.Warnf("teams-to-logins references non-existing role: %q. Available roles: %v.", role, roleNames) + } else { + return trace.BadParameter("teams-to-logins references non-existing role: %v. Correct the mapping, or add --ignore-missing-roles to ignore this error. Available roles: %v.", role, roleNames) + } + } + } + } + + return nil +} diff --git a/tool/tctl/sso/configure/teams_to_logins.go b/tool/tctl/sso/configure/teams_to_logins.go new file mode 100644 index 0000000000000..f8b3851aec937 --- /dev/null +++ b/tool/tctl/sso/configure/teams_to_logins.go @@ -0,0 +1,66 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 configure + +import ( + "fmt" + "strings" + + "github.com/gravitational/kingpin" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/types" +) + +// teamsToLoginsParser parsers 'name,value,role1,role2,...' values into types.AttributeMapping entries. Cumulative, can handle multiple entries. +type teamsToLoginsParser struct { + mappings *[]types.TeamMapping +} + +func (p *teamsToLoginsParser) String() string { + return fmt.Sprintf("%q", p.mappings) +} + +func (p *teamsToLoginsParser) Set(s string) error { + splits := strings.Split(s, ",") + + if len(splits) < 3 { + return trace.BadParameter("Too few elements separated with comma. use syntax: 'name,value,role1,role2,...'.") + } + + name := splits[0] + value := splits[1] + roles := splits[2:] + + mapping := types.TeamMapping{ + Organization: name, + Team: value, + Logins: roles, // note: logins is legacy name, 'roles' is accurate now. + } + + *p.mappings = append(*p.mappings, mapping) + + return nil +} + +// IsCumulative returns true if flag is repeatable. This is checked by kingpin library. +func (p *teamsToLoginsParser) IsCumulative() bool { + return true +} + +// newTeamsToLoginsParser returns a cumulative flag parser for []types.TeamMapping. +func newTeamsToLoginsParser(field *[]types.TeamMapping) kingpin.Value { + return &teamsToLoginsParser{mappings: field} +} diff --git a/tool/tctl/sso/configure/teams_to_logins_test.go b/tool/tctl/sso/configure/teams_to_logins_test.go new file mode 100644 index 0000000000000..561d319ea870c --- /dev/null +++ b/tool/tctl/sso/configure/teams_to_logins_test.go @@ -0,0 +1,89 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 configure + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/types" +) + +func Test_teamsToLoginsParser_Set(t *testing.T) { + tests := []struct { + name string + parser teamsToLoginsParser + arg string + wantErr bool + wantParser teamsToLoginsParser + }{ + { + name: "one set of correct args", + parser: teamsToLoginsParser{mappings: new([]types.TeamMapping)}, + arg: "foo,bar,baz", + wantParser: teamsToLoginsParser{mappings: &[]types.TeamMapping{ + { + Organization: "foo", + Team: "bar", + Logins: []string{"baz"}, + }, + }}, + wantErr: false, + }, + { + name: "two sets of correct args", + parser: teamsToLoginsParser{mappings: &[]types.TeamMapping{ + { + Organization: "foo", + Team: "bar", + Logins: []string{"baz"}, + }, + }}, + arg: "aaa,bbb,ccc,ddd", + wantParser: teamsToLoginsParser{mappings: &[]types.TeamMapping{ + { + Organization: "foo", + Team: "bar", + Logins: []string{"baz"}, + }, + { + Organization: "aaa", + Team: "bbb", + Logins: []string{"ccc", "ddd"}, + }, + }}, + wantErr: false, + }, + { + name: "one set of incorrect args", + parser: teamsToLoginsParser{mappings: new([]types.TeamMapping)}, + arg: "abracadabra", + wantParser: teamsToLoginsParser{mappings: new([]types.TeamMapping)}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.parser.Set(tt.arg) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.parser, tt.wantParser) + } + }) + } +} diff --git a/tool/tctl/sso/tester/command.go b/tool/tctl/sso/tester/command.go new file mode 100644 index 0000000000000..c00fb60b8b548 --- /dev/null +++ b/tool/tctl/sso/tester/command.go @@ -0,0 +1,282 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 tester + +import ( + "context" + "fmt" + "io" + "os" + "strings" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/service" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/utils" + + "github.com/gravitational/kingpin" + "github.com/gravitational/trace" + + kyaml "k8s.io/apimachinery/pkg/util/yaml" +) + +// SSOTestCommand implements common.CLICommand interface +type SSOTestCommand struct { + config *service.Config + + ssoTestCmd *kingpin.CmdClause + + // connectorFileName points at file name with sso connector definition + connectorFileName string + + // Handlers is a mapping between auth kind and appropriate handling function + Handlers map[string]func(c auth.ClientI, connBytes []byte) (*AuthRequestInfo, error) + // GetDiagInfoFields provides auth kind-specific diagnostic info fields. + GetDiagInfoFields map[string]func(diag *types.SSODiagnosticInfo, debug bool) []string +} + +// Initialize allows a caller-defined command to plug itself into CLI +// argument parsing +func (cmd *SSOTestCommand) Initialize(app *kingpin.Application, cfg *service.Config) { + cmd.config = cfg + + sso := app.GetCommand("sso") + cmd.ssoTestCmd = sso.Command("test", "Perform end-to-end test of SSO flow using provided auth connector definition.") + cmd.ssoTestCmd.Arg("filename", "Connector resource definition filename. Empty for stdin.").StringVar(&cmd.connectorFileName) + cmd.ssoTestCmd.Alias(` +Examples: + + Test the auth connector from connector.yaml: + + > tctl sso test connector.yaml + + The command is designed to be used in conjunction with "tctl sso configure" family of commands: + + > tctl sso configure github ... | tctl sso test + + The pipeline may also utilise "tee" to capture the connector generated with "tctl sso configure". + + > tctl sso configure github ... | tee connector.yaml | tctl sso test`) + + cmd.Handlers = map[string]func(c auth.ClientI, connBytes []byte) (*AuthRequestInfo, error){ + types.KindGithubConnector: handleGithubConnector, + } + + cmd.GetDiagInfoFields = map[string]func(diag *types.SSODiagnosticInfo, debug bool) []string{ + types.KindGithubConnector: getGithubDiagInfoFields, + } +} + +func (cmd *SSOTestCommand) getSupportedKinds() []string { + var kinds []string + + for kind := range cmd.Handlers { + kinds = append(kinds, kind) + } + + return kinds +} + +func (cmd *SSOTestCommand) ssoTestCommand(c auth.ClientI) error { + reader := os.Stdin + if cmd.connectorFileName != "" { + f, err := utils.OpenFile(cmd.connectorFileName) + if err != nil { + return trace.Wrap(err, "could not open connector spec file %v", cmd.connectorFileName) + } + defer f.Close() + reader = f + } + + decoder := kyaml.NewYAMLOrJSONDecoder(reader, defaults.LookaheadBufSize) + for { + var raw services.UnknownResource + err := decoder.Decode(&raw) + if err != nil { + if err == io.EOF { + return nil + } + return trace.Wrap(err, "Unable to load resource. Make sure the file is in correct format.") + } + + handler, ok := cmd.Handlers[raw.Kind] + if !ok { + return trace.BadParameter("Resources of type %q are not supported. Supported kinds: %v", raw.Kind, cmd.getSupportedKinds()) + } + + requestInfo, err := handler(c, raw.Raw) + if err != nil { + return trace.Wrap(err) + } + + // note: loginErr is processed further down. + loginResponse, loginErr := cmd.runSSOLoginFlow(context.TODO(), raw.Kind, c, requestInfo.Config) + + if requestInfo.RequestCreateErr != nil { + return trace.BadParameter("Failed to create auth request. Check the auth connector definition for errors. Error: %v", requestInfo.RequestCreateErr) + } + + info, infoErr := c.GetSSODiagnosticInfo(context.TODO(), raw.Kind, requestInfo.RequestID) + + err = cmd.reportLoginResult(raw.Kind, info, infoErr, loginResponse, loginErr) + if err != nil { + return trace.Wrap(err) + } + } +} + +// TryRun is executed after the CLI parsing is done. The command must +// determine if selectedCommand belongs to it and return match=true +func (cmd *SSOTestCommand) TryRun(selectedCommand string, c auth.ClientI) (match bool, err error) { + if selectedCommand == cmd.ssoTestCmd.FullCommand() { + return true, cmd.ssoTestCommand(c) + } + return false, nil +} + +// AuthRequestInfo is helper type, useful for tying together test handlers of different auth types. +type AuthRequestInfo struct { + // Config holds *client.RedirectorConfig used for SSO redirect. + Config *client.RedirectorConfig + // RequestID is ID of auth request created for SSO test. + RequestID string + // RequestCreateErr holds an error in case auth request creation failed. + RequestCreateErr error +} + +func (cmd *SSOTestCommand) runSSOLoginFlow(ctx context.Context, protocol string, c auth.ClientI, config *client.RedirectorConfig) (*auth.SSHLoginResponse, error) { + key, err := client.NewKey() + if err != nil { + return nil, trace.Wrap(err) + } + + proxies, err := c.GetProxies() + if err != nil { + return nil, trace.Wrap(err) + } + + if len(proxies) == 0 { + return nil, trace.BadParameter("cluster has no proxies.") + } + + cfg := client.MakeDefaultConfig() + cfg.WebProxyAddr = proxies[0].GetPublicAddr() + + tc, err := client.NewClient(cfg) + if err != nil { + return nil, trace.Wrap(err) + } + + return client.SSHAgentSSOLogin(ctx, client.SSHLoginSSO{ + SSHLogin: client.SSHLogin{ + ProxyAddr: tc.WebProxyAddr, + PubKey: key.Pub, + TTL: tc.KeyTTL, + Insecure: tc.InsecureSkipVerify, + Pool: nil, + Compatibility: tc.CertificateFormat, + RouteToCluster: tc.SiteName, + KubernetesCluster: tc.KubernetesCluster, + }, + ConnectorID: "-sso-test", + Protocol: protocol, + BindAddr: tc.BindAddr, + Browser: tc.Browser, + }, config) +} + +// GetDiagMessage is helper function for preparing message set to be shown to user. +func GetDiagMessage(present bool, show bool, msg string) string { + if present && show { + return msg + } + return "" +} + +func (cmd *SSOTestCommand) reportLoginResult(authKind string, diag *types.SSODiagnosticInfo, infoErr error, loginResponse *auth.SSHLoginResponse, loginErr error) (errResult error) { + success := diag != nil && diag.Success + + // check for errors + if loginErr != nil || infoErr != nil { + success = false + } + + if success { + fmt.Printf("Success! Logged in as: %v\n", loginResponse.Username) + } else { + fmt.Printf("Failure!\n") + errResult = trace.Errorf("SSO flow failed.") + + if infoErr != nil { + fmt.Printf("No diagnostic info found. Most likely cause: the request timed out or callback configuration is incorrect. Ensure that user logs within alloted time and IdP configuration is correct.\n Error details: %v\n", trace.UserMessage(infoErr)) + errResult = trace.Wrap(infoErr, "SSO flow failed.") + } + + if loginErr != nil { + fmt.Printf("Login error: %v\n", trace.UserMessage(loginErr)) + errResult = trace.Wrap(loginErr, "SSO flow failed.") + } + } + + // finish early if there is no diag info to show. + if diag == nil { + return errResult + } + + cmd.printDiagnosticInfo(authKind, diag, loginErr) + + return errResult +} + +func (cmd *SSOTestCommand) printDiagnosticInfo(authKind string, diag *types.SSODiagnosticInfo, loginErr error) { + fields := []string{ + // common fields across auth connector types. + GetDiagMessage( + diag.Error != "", + cmd.config.Debug || loginErr == nil, + FormatString("Original error", diag.Error)), + GetDiagMessage( + diag.CreateUserParams != nil, + true, + formatUserDetails("Authentication details", diag.CreateUserParams)), + } + + // enrich the fields with auth-specific fields + if getFields, ok := cmd.GetDiagInfoFields[authKind]; ok { + fields = append(fields, getFields(diag, cmd.config.Debug)...) + } + + // raw data - debug. we want this field last. + fields = append(fields, GetDiagMessage(true, cmd.config.Debug, FormatJSON("Raw data", diag))) + + const termWidth = 80 + + for _, msg := range fields { + if msg != "" { + fmt.Println(strings.Repeat("-", termWidth)) + fmt.Println(msg) + } + } + + if !cmd.config.Debug { + fmt.Println(strings.Repeat("-", termWidth)) + fmt.Println("For more details repeat the command with --debug flag.") + } + + fmt.Println() +} diff --git a/tool/tctl/sso/tester/format.go b/tool/tctl/sso/tester/format.go new file mode 100644 index 0000000000000..92d532bc125c9 --- /dev/null +++ b/tool/tctl/sso/tester/format.go @@ -0,0 +1,64 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 tester + +import ( + "encoding/json" + "fmt" + + "github.com/gravitational/teleport/api/types" + + "github.com/ghodss/yaml" +) + +func FormatString(description string, msg string) string { + return fmt.Sprintf("%v:\n%v\n", description, msg) +} + +func FormatYAML(description string, object interface{}) string { + output, err := yaml.Marshal(object) + if err != nil { + return formatError(description, err) + } + return fmt.Sprintf("%v:\n%v", description, string(output)) +} + +func FormatJSON(description string, object interface{}) string { + output, err := json.MarshalIndent(object, "", " ") + if err != nil { + return formatError(description, err) + } + return fmt.Sprintf("%v:\n%v\n", description, string(output)) +} + +func formatUserDetails(description string, info *types.CreateUserParams) string { + if info == nil { + return "" + } + + // Skip fields: connector_name, session_ttl + info.ConnectorName = "" + info.SessionTTL = 0 + + output, err := yaml.Marshal(info) + if err != nil { + return formatError(description, err) + } + return fmt.Sprintf("%v:\n%v", description, Indent(string(output), 3)) +} + +func formatError(fieldDesc string, err error) string { + return fmt.Sprintf("%v: error rendering field: %v\n", fieldDesc, err) +} diff --git a/tool/tctl/sso/tester/format_test.go b/tool/tctl/sso/tester/format_test.go new file mode 100644 index 0000000000000..b30d1a5fdf5d9 --- /dev/null +++ b/tool/tctl/sso/tester/format_test.go @@ -0,0 +1,250 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 tester + +import ( + "fmt" + "testing" + + "github.com/gravitational/teleport/api/types" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" +) + +func Test_formatString(t *testing.T) { + tests := []struct { + name string + description string + msg string + want string + }{ + { + name: "empty", + description: "", + msg: "", + want: ":\n\n", + }, + { + name: "something", + description: "a field", + msg: "foo baz bar blah", + want: `a field: +foo baz bar blah +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := FormatString(tt.description, tt.msg) + require.Equal(t, tt.want, got) + }) + } +} + +func Test_formatYAML(t *testing.T) { + tests := []struct { + name string + description string + object interface{} + want string + }{ + { + name: "empty", + description: "", + object: nil, + want: ":\nnull\n", + }, + { + name: "simple object", + description: "my field", + object: types.RoleSpecV5{ + Allow: types.RoleConditions{ + Logins: []string{"username"}, + ClusterLabels: types.Labels{"access": []string{"ops"}}, + }, + }, + want: `my field: +allow: + cluster_labels: + access: ops + logins: + - username +deny: {} +options: + cert_format: "" + desktop_clipboard: null + desktop_directory_sharing: null + forward_agent: false + record_session: null +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := FormatYAML(tt.description, tt.object) + require.Equal(t, tt.want, got) + }) + } +} + +func Test_formatJSON(t *testing.T) { + tests := []struct { + name string + description string + object interface{} + want string + }{ + { + name: "empty", + description: "empty field", + object: struct{}{}, + want: `empty field: +{} +`, + }, + { + name: "simple object", + description: "my field", + object: types.RoleSpecV5{ + Allow: types.RoleConditions{ + Logins: []string{"username"}, + ClusterLabels: types.Labels{"access": []string{"ops"}}, + }, + }, + want: `my field: +{ + "options": { + "forward_agent": false, + "cert_format": "", + "record_session": null, + "desktop_clipboard": null, + "desktop_directory_sharing": null + }, + "allow": { + "logins": [ + "username" + ], + "cluster_labels": { + "access": "ops" + } + }, + "deny": {} +} +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := FormatJSON(tt.description, tt.object) + require.Equal(t, tt.want, got) + }) + } +} + +func Test_formatUserDetails(t *testing.T) { + tests := []struct { + name string + description string + info *types.CreateUserParams + want string + }{ + { + name: "empty", + description: "", + info: nil, + want: "", + }, + { + name: "some details", + description: "user details", + info: &types.CreateUserParams{ + ConnectorName: "foo", + Username: "bar", + Logins: []string{"laa", "lbb", "lcc"}, + KubeGroups: []string{"kgaa", "kgbb", "kgcc"}, + KubeUsers: []string{"kuaa", "kubb", "kucc"}, + Roles: []string{"raa", "rbb", "rcc"}, + Traits: map[string][]string{ + "groups": {"gfoo", "gbar", "gbaz"}, + }, + SessionTTL: 1230, + }, + want: `user details: + kube_groups: + - kgaa + - kgbb + - kgcc + kube_users: + - kuaa + - kubb + - kucc + logins: + - laa + - lbb + - lcc + roles: + - raa + - rbb + - rcc + traits: + groups: + - gfoo + - gbar + - gbaz + username: bar`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := formatUserDetails(tt.description, tt.info) + require.Equal(t, tt.want, got) + }) + } +} + +func Test_formatError(t *testing.T) { + tests := []struct { + name string + fieldDesc string + err error + want string + }{ + { + name: "empty", + fieldDesc: "my field", + err: nil, + want: "my field: error rendering field: \n", + }, + { + name: "plain error", + fieldDesc: "my field", + err: fmt.Errorf("foo: %v", 123), + want: "my field: error rendering field: foo: 123\n", + }, + { + name: "trace error", + fieldDesc: "my field", + err: trace.Errorf("bar: %v", 321), + want: "my field: error rendering field: bar: 321\n", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := formatError(tt.fieldDesc, tt.err) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/tool/tctl/sso/tester/github.go b/tool/tctl/sso/tester/github.go new file mode 100644 index 0000000000000..5709ab8765892 --- /dev/null +++ b/tool/tctl/sso/tester/github.go @@ -0,0 +1,102 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 tester + +import ( + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/services" + + "github.com/gravitational/trace" +) + +func githubTest(c auth.ClientI, connector types.GithubConnector) (*AuthRequestInfo, error) { + // get connector spec + var spec types.GithubConnectorSpecV3 + switch ghConnector := connector.(type) { + case *types.GithubConnectorV3: + spec = ghConnector.Spec + default: + return nil, trace.BadParameter("Unrecognized GitHub connector version: %T. Provide supported connector version.", ghConnector) + } + + requestInfo := &AuthRequestInfo{} + + makeRequest := func(req client.SSOLoginConsoleReq) (*client.SSOLoginConsoleResponse, error) { + ghRequest := services.GithubAuthRequest{ + ConnectorID: req.ConnectorID + "-" + connector.GetName(), + Type: constants.Github, + PublicKey: req.PublicKey, + CertTTL: defaults.GithubAuthRequestTTL, + CreateWebSession: false, + ClientRedirectURL: req.RedirectURL, + RouteToCluster: req.RouteToCluster, + SSOTestFlow: true, + ConnectorSpec: &spec, + } + + request, err := c.CreateGithubAuthRequest(ghRequest) + + if request != nil { + requestInfo.RequestID = request.StateToken + } + requestInfo.RequestCreateErr = err + + if err != nil { + return nil, trace.Wrap(err) + } + + return &client.SSOLoginConsoleResponse{RedirectURL: request.RedirectURL}, nil + } + + requestInfo.Config = &client.RedirectorConfig{SSOLoginConsoleRequestFn: makeRequest} + return requestInfo, nil +} + +func handleGithubConnector(c auth.ClientI, connBytes []byte) (*AuthRequestInfo, error) { + conn, err := services.UnmarshalGithubConnector(connBytes) + if err != nil { + return nil, trace.Wrap(err, "Unable to load GitHub connector. Correct the definition and try again.") + } + requestInfo, err := githubTest(c, conn) + if err != nil { + return nil, trace.Wrap(err) + } + + return requestInfo, nil +} + +func getGithubDiagInfoFields(diag *types.SSODiagnosticInfo, debug bool) []string { + return []string{ + GetDiagMessage( + diag.GithubTokenInfo != nil, + debug, + FormatJSON("[GitHub] OAuth2 token info", diag.GithubTokenInfo), + ), + GetDiagMessage( + diag.GithubClaims != nil, + true, + FormatYAML("[GitHub] Received claims", diag.GithubClaims), + ), + GetDiagMessage( + diag.GithubTeamsToLogins != nil, + true, + FormatYAML("[GitHub] Connector team to logins mapping", diag.GithubTeamsToLogins), + ), + } +} diff --git a/tool/tctl/sso/tester/utils.go b/tool/tctl/sso/tester/utils.go new file mode 100644 index 0000000000000..bcff6b2a406d6 --- /dev/null +++ b/tool/tctl/sso/tester/utils.go @@ -0,0 +1,32 @@ +// Copyright 2022 Gravitational, Inc +// +// 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 tester + +import ( + "bufio" + "fmt" + "strings" +) + +// Indent returns the string where each line is indented by the specified +// number of spaces. +func Indent(s string, padding int) string { + var lines []string + scanner := bufio.NewScanner(strings.NewReader(s)) + for scanner.Scan() { + lines = append(lines, fmt.Sprintf("%v%v", strings.Repeat(" ", padding), scanner.Text())) + } + return strings.Join(lines, "\n") +}