diff --git a/.github/workflows/docs-amplify.yaml b/.github/workflows/docs-amplify.yaml new file mode 100644 index 0000000000000..3ca12933b50c1 --- /dev/null +++ b/.github/workflows/docs-amplify.yaml @@ -0,0 +1,42 @@ +name: Docs Preview +on: + pull_request: + paths: + - 'docs/**' + - .github/workflows/docs-amplify.yaml + workflow_dispatch: + +permissions: + pull-requests: write + id-token: write + +jobs: + amplify-preview: + name: Prepare Amplify preview URL + runs-on: ubuntu-22.04-2core-arm64 + environment: docs-amplify + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4 + with: + aws-region: us-west-2 + role-to-assume: ${{ vars.IAM_ROLE }} + + - name: Create Amplify preview environment + uses: gravitational/shared-workflows/tools/amplify-preview@tools/amplify-preview/v0.0.1 + continue-on-error: true + with: + app_ids: ${{ vars.AMPLIFY_APP_IDS }} + create_branches: "true" + github_token: ${{ secrets.GITHUB_TOKEN }} + wait: "true" + + - name: Print failure message + if: failure() + env: + ERR_TITLE: Teleport Docs preview build failed + ERR_MESSAGE: >- + Please refer to the following documentation for help: https://www.notion.so/goteleport/How-to-Amplify-deployments-162fdd3830be8096ba72efa1a49ee7bc?pvs=4 + run: | + echo ::error title=$ERR_TITLE::$ERR_MESSAGE + exit 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index da90069f11b80..a022e8c261068 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 17.1.2 (12/30/24) + +* Fixed a bug in the WebUI that could cause an access denied error when accessing application. [#50611](https://github.com/gravitational/teleport/pull/50611) +* Improve session playback initial delay caused by an additional events query. [#50592](https://github.com/gravitational/teleport/pull/50592) +* Fix a bug in the `tbot` Helm chart causing invalid configuration when both default and custom outputs were used. [#50526](https://github.com/gravitational/teleport/pull/50526) +* Restore the ability to play session recordings in the web UI without specifying the session duration in the URL. [#50459](https://github.com/gravitational/teleport/pull/50459) +* Fix regression in `tbot` on Linux causing the Kubernetes credential helper to fail. [#50413](https://github.com/gravitational/teleport/pull/50413) + ## 17.1.1 (12/20/24) **Warning**: 17.1.1 fixes a regression in 17.1.0 that causes SSH server heartbeats diff --git a/Makefile b/Makefile index 62034396bdf86..9fafcd1fc6211 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ # Stable releases: "1.0.0" # Pre-releases: "1.0.0-alpha.1", "1.0.0-beta.2", "1.0.0-rc.3" # Master/dev branch: "1.0.0-dev" -VERSION=17.1.1 +VERSION=17.1.2 DOCKER_IMAGE ?= teleport diff --git a/api/client/client.go b/api/client/client.go index 10aa123b166a2..bb06da5e4655d 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -5081,6 +5081,52 @@ func (c *Client) UpsertUserLastSeenNotification(ctx context.Context, req *notifi return rsp, trace.Wrap(err) } +// GetWorkloadIdentity returns a workload identity by name. +func (c *Client) GetWorkloadIdentity(ctx context.Context, name string) (*workloadidentityv1pb.WorkloadIdentity, error) { + resp, err := c.WorkloadIdentityResourceServiceClient().GetWorkloadIdentity(ctx, &workloadidentityv1pb.GetWorkloadIdentityRequest{ + Name: name, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// DeleteWorkloadIdentity deletes a workload identity by name. It will throw an +// error if the workload identity does not exist. +func (c *Client) DeleteWorkloadIdentity(ctx context.Context, name string) error { + _, err := c.WorkloadIdentityResourceServiceClient().DeleteWorkloadIdentity(ctx, &workloadidentityv1pb.DeleteWorkloadIdentityRequest{ + Name: name, + }) + if err != nil { + return trace.Wrap(err) + } + return nil +} + +// CreateWorkloadIdentity creates a new workload identity, it will not overwrite +// an existing workload identity with the same name. +func (c *Client) CreateWorkloadIdentity(ctx context.Context, r *workloadidentityv1pb.WorkloadIdentity) (*workloadidentityv1pb.WorkloadIdentity, error) { + resp, err := c.WorkloadIdentityResourceServiceClient().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.CreateWorkloadIdentityRequest{ + WorkloadIdentity: r, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// UpsertWorkloadIdentity creates or updates a workload identity. +func (c *Client) UpsertWorkloadIdentity(ctx context.Context, r *workloadidentityv1pb.WorkloadIdentity) (*workloadidentityv1pb.WorkloadIdentity, error) { + resp, err := c.WorkloadIdentityResourceServiceClient().UpsertWorkloadIdentity(ctx, &workloadidentityv1pb.UpsertWorkloadIdentityRequest{ + WorkloadIdentity: r, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + // ResourceUsageClient returns an unadorned Resource Usage service client, // using the underlying Auth gRPC connection. // Clients connecting to non-Enterprise clusters, or older Teleport versions, diff --git a/api/proto/teleport/legacy/types/events/events.proto b/api/proto/teleport/legacy/types/events/events.proto index 5b51bc0a1a5c7..483b8272bc240 100644 --- a/api/proto/teleport/legacy/types/events/events.proto +++ b/api/proto/teleport/legacy/types/events/events.proto @@ -4694,6 +4694,8 @@ message OneOf { events.UserTaskUpdate UserTaskUpdate = 189; events.UserTaskDelete UserTaskDelete = 190; events.SFTPSummary SFTPSummary = 191; + events.ContactCreate ContactCreate = 192; + events.ContactDelete ContactDelete = 193; events.WorkloadIdentityCreate WorkloadIdentityCreate = 194; events.WorkloadIdentityUpdate WorkloadIdentityUpdate = 195; events.WorkloadIdentityDelete WorkloadIdentityDelete = 196; @@ -7738,3 +7740,98 @@ message UserLoginAccessListInvalid { (gogoproto.jsontag) = "" ]; } + +// ContactCreate is emitted when a contact is created. +message ContactCreate { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Status indicates whether the creation was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Email is the Email of the contact being deleted + string Email = 6 [(gogoproto.jsontag) = "email"]; + + // ContactType is the type of the contact being deleted ('Business' or 'Security') + ContactType ContactType = 7 [(gogoproto.jsontag) = "contact_type"]; +} + +// ContactDelete is emitted when a contact is deleted. +message ContactDelete { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Status indicates whether the deletion was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Email is the Email of the contact being deleted + string Email = 6 [(gogoproto.jsontag) = "email"]; + + // ContactType is the type of the contact being deleted ('Business' or 'Security') + ContactType ContactType = 7 [(gogoproto.jsontag) = "contact_type"]; +} + +// ContactType is the type of contact being added. +enum ContactType { + CONTACT_TYPE_UNSPECIFIED = 0; + CONTACT_TYPE_BUSINESS = 1; + CONTACT_TYPE_SECURITY = 2; +} diff --git a/api/types/events/events.go b/api/types/events/events.go index 2ed1598a8176b..13a33cb57918a 100644 --- a/api/types/events/events.go +++ b/api/types/events/events.go @@ -2413,3 +2413,11 @@ func (m *WorkloadIdentityUpdate) TrimToMaxSize(maxSize int) AuditEvent { func (m *WorkloadIdentityDelete) TrimToMaxSize(_ int) AuditEvent { return m } + +func (m *ContactCreate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *ContactDelete) TrimToMaxSize(_ int) AuditEvent { + return m +} diff --git a/api/types/events/events.pb.go b/api/types/events/events.pb.go index 2ff3c85bd80e7..b182c9a47e1ba 100644 --- a/api/types/events/events.pb.go +++ b/api/types/events/events.pb.go @@ -350,6 +350,35 @@ func (AdminActionsMFAStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_007ba1c3d6266d56, []int{7} } +// ContactType is the type of contact being added. +type ContactType int32 + +const ( + ContactType_CONTACT_TYPE_UNSPECIFIED ContactType = 0 + ContactType_CONTACT_TYPE_BUSINESS ContactType = 1 + ContactType_CONTACT_TYPE_SECURITY ContactType = 2 +) + +var ContactType_name = map[int32]string{ + 0: "CONTACT_TYPE_UNSPECIFIED", + 1: "CONTACT_TYPE_BUSINESS", + 2: "CONTACT_TYPE_SECURITY", +} + +var ContactType_value = map[string]int32{ + "CONTACT_TYPE_UNSPECIFIED": 0, + "CONTACT_TYPE_BUSINESS": 1, + "CONTACT_TYPE_SECURITY": 2, +} + +func (x ContactType) String() string { + return proto.EnumName(ContactType_name, int32(x)) +} + +func (ContactType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_007ba1c3d6266d56, []int{8} +} + // Operation is the network operation that was performed or attempted type SessionNetwork_NetworkOperation int32 @@ -7842,6 +7871,8 @@ type OneOf struct { // *OneOf_UserTaskUpdate // *OneOf_UserTaskDelete // *OneOf_SFTPSummary + // *OneOf_ContactCreate + // *OneOf_ContactDelete // *OneOf_WorkloadIdentityCreate // *OneOf_WorkloadIdentityUpdate // *OneOf_WorkloadIdentityDelete @@ -8452,6 +8483,12 @@ type OneOf_UserTaskDelete struct { type OneOf_SFTPSummary struct { SFTPSummary *SFTPSummary `protobuf:"bytes,191,opt,name=SFTPSummary,proto3,oneof" json:"SFTPSummary,omitempty"` } +type OneOf_ContactCreate struct { + ContactCreate *ContactCreate `protobuf:"bytes,192,opt,name=ContactCreate,proto3,oneof" json:"ContactCreate,omitempty"` +} +type OneOf_ContactDelete struct { + ContactDelete *ContactDelete `protobuf:"bytes,193,opt,name=ContactDelete,proto3,oneof" json:"ContactDelete,omitempty"` +} type OneOf_WorkloadIdentityCreate struct { WorkloadIdentityCreate *WorkloadIdentityCreate `protobuf:"bytes,194,opt,name=WorkloadIdentityCreate,proto3,oneof" json:"WorkloadIdentityCreate,omitempty"` } @@ -8652,6 +8689,8 @@ func (*OneOf_UserTaskCreate) isOneOf_Event() {} func (*OneOf_UserTaskUpdate) isOneOf_Event() {} func (*OneOf_UserTaskDelete) isOneOf_Event() {} func (*OneOf_SFTPSummary) isOneOf_Event() {} +func (*OneOf_ContactCreate) isOneOf_Event() {} +func (*OneOf_ContactDelete) isOneOf_Event() {} func (*OneOf_WorkloadIdentityCreate) isOneOf_Event() {} func (*OneOf_WorkloadIdentityUpdate) isOneOf_Event() {} func (*OneOf_WorkloadIdentityDelete) isOneOf_Event() {} @@ -9973,6 +10012,20 @@ func (m *OneOf) GetSFTPSummary() *SFTPSummary { return nil } +func (m *OneOf) GetContactCreate() *ContactCreate { + if x, ok := m.GetEvent().(*OneOf_ContactCreate); ok { + return x.ContactCreate + } + return nil +} + +func (m *OneOf) GetContactDelete() *ContactDelete { + if x, ok := m.GetEvent().(*OneOf_ContactDelete); ok { + return x.ContactDelete + } + return nil +} + func (m *OneOf) GetWorkloadIdentityCreate() *WorkloadIdentityCreate { if x, ok := m.GetEvent().(*OneOf_WorkloadIdentityCreate); ok { return x.WorkloadIdentityCreate @@ -10191,6 +10244,8 @@ func (*OneOf) XXX_OneofWrappers() []interface{} { (*OneOf_UserTaskUpdate)(nil), (*OneOf_UserTaskDelete)(nil), (*OneOf_SFTPSummary)(nil), + (*OneOf_ContactCreate)(nil), + (*OneOf_ContactDelete)(nil), (*OneOf_WorkloadIdentityCreate)(nil), (*OneOf_WorkloadIdentityUpdate)(nil), (*OneOf_WorkloadIdentityDelete)(nil), @@ -15186,6 +15241,114 @@ func (m *UserLoginAccessListInvalid) XXX_DiscardUnknown() { var xxx_messageInfo_UserLoginAccessListInvalid proto.InternalMessageInfo +// ContactCreate is emitted when a contact is created. +type ContactCreate struct { + // Metadata is a common event metadata + Metadata `protobuf:"bytes,1,opt,name=Metadata,proto3,embedded=Metadata" json:""` + // ResourceMetadata is a common resource event metadata + ResourceMetadata `protobuf:"bytes,2,opt,name=Resource,proto3,embedded=Resource" json:""` + // User is a common user event metadata + UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` + // ConnectionMetadata holds information about the connection + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the creation was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` + // Email is the Email of the contact being deleted + Email string `protobuf:"bytes,6,opt,name=Email,proto3" json:"email"` + // ContactType is the type of the contact being deleted ('Business' or 'Security') + ContactType ContactType `protobuf:"varint,7,opt,name=ContactType,proto3,enum=events.ContactType" json:"contact_type"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContactCreate) Reset() { *m = ContactCreate{} } +func (m *ContactCreate) String() string { return proto.CompactTextString(m) } +func (*ContactCreate) ProtoMessage() {} +func (*ContactCreate) Descriptor() ([]byte, []int) { + return fileDescriptor_007ba1c3d6266d56, []int{238} +} +func (m *ContactCreate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContactCreate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContactCreate.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 *ContactCreate) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContactCreate.Merge(m, src) +} +func (m *ContactCreate) XXX_Size() int { + return m.Size() +} +func (m *ContactCreate) XXX_DiscardUnknown() { + xxx_messageInfo_ContactCreate.DiscardUnknown(m) +} + +var xxx_messageInfo_ContactCreate proto.InternalMessageInfo + +// ContactDelete is emitted when a contact is deleted. +type ContactDelete struct { + // Metadata is a common event metadata + Metadata `protobuf:"bytes,1,opt,name=Metadata,proto3,embedded=Metadata" json:""` + // ResourceMetadata is a common resource event metadata + ResourceMetadata `protobuf:"bytes,2,opt,name=Resource,proto3,embedded=Resource" json:""` + // User is a common user event metadata + UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` + // ConnectionMetadata holds information about the connection + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the deletion was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` + // Email is the Email of the contact being deleted + Email string `protobuf:"bytes,6,opt,name=Email,proto3" json:"email"` + // ContactType is the type of the contact being deleted ('Business' or 'Security') + ContactType ContactType `protobuf:"varint,7,opt,name=ContactType,proto3,enum=events.ContactType" json:"contact_type"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContactDelete) Reset() { *m = ContactDelete{} } +func (m *ContactDelete) String() string { return proto.CompactTextString(m) } +func (*ContactDelete) ProtoMessage() {} +func (*ContactDelete) Descriptor() ([]byte, []int) { + return fileDescriptor_007ba1c3d6266d56, []int{239} +} +func (m *ContactDelete) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContactDelete) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContactDelete.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 *ContactDelete) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContactDelete.Merge(m, src) +} +func (m *ContactDelete) XXX_Size() int { + return m.Size() +} +func (m *ContactDelete) XXX_DiscardUnknown() { + xxx_messageInfo_ContactDelete.DiscardUnknown(m) +} + +var xxx_messageInfo_ContactDelete proto.InternalMessageInfo + func init() { proto.RegisterEnum("events.UserKind", UserKind_name, UserKind_value) proto.RegisterEnum("events.EventAction", EventAction_name, EventAction_value) @@ -15195,6 +15358,7 @@ func init() { proto.RegisterEnum("events.ElasticsearchCategory", ElasticsearchCategory_name, ElasticsearchCategory_value) proto.RegisterEnum("events.OpenSearchCategory", OpenSearchCategory_name, OpenSearchCategory_value) proto.RegisterEnum("events.AdminActionsMFAStatus", AdminActionsMFAStatus_name, AdminActionsMFAStatus_value) + proto.RegisterEnum("events.ContactType", ContactType_name, ContactType_value) proto.RegisterEnum("events.SessionNetwork_NetworkOperation", SessionNetwork_NetworkOperation_name, SessionNetwork_NetworkOperation_value) proto.RegisterType((*Metadata)(nil), "events.Metadata") proto.RegisterType((*SessionMetadata)(nil), "events.SessionMetadata") @@ -15448,6 +15612,8 @@ func init() { proto.RegisterType((*WorkloadIdentityDelete)(nil), "events.WorkloadIdentityDelete") proto.RegisterType((*AccessListInvalidMetadata)(nil), "events.AccessListInvalidMetadata") proto.RegisterType((*UserLoginAccessListInvalid)(nil), "events.UserLoginAccessListInvalid") + proto.RegisterType((*ContactCreate)(nil), "events.ContactCreate") + proto.RegisterType((*ContactDelete)(nil), "events.ContactDelete") } func init() { @@ -15455,1085 +15621,1094 @@ func init() { } var fileDescriptor_007ba1c3d6266d56 = []byte{ - // 17244 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x78, 0x24, 0x49, - 0x72, 0x18, 0x86, 0x7e, 0xa0, 0x01, 0x04, 0x9e, 0x93, 0xf3, 0xaa, 0x9d, 0x9d, 0x59, 0xec, 0xd6, - 0xee, 0xcd, 0xcd, 0xec, 0xed, 0x62, 0x6e, 0x67, 0x67, 0x77, 0x6f, 0x5f, 0xdc, 0x6b, 0xa0, 0x81, - 0x41, 0xcf, 0xe0, 0xd1, 0x5b, 0x8d, 0x99, 0xb9, 0x3d, 0xf2, 0xae, 0x59, 0xe8, 0xca, 0x01, 0x6a, - 0xa7, 0xbb, 0xaa, 0x59, 0x55, 0x3d, 0x18, 0xac, 0xfc, 0xe0, 0xc9, 0x14, 0x45, 0x8a, 0xc7, 0xd3, - 0xf9, 0x68, 0xf2, 0x28, 0x4a, 0xb6, 0x8f, 0x92, 0x65, 0x53, 0x14, 0xc5, 0x33, 0x65, 0x9a, 0xe4, - 0x91, 0x3c, 0xeb, 0x71, 0xb2, 0x7c, 0x22, 0x3f, 0xf1, 0x23, 0x65, 0x5b, 0xd6, 0x67, 0xcb, 0x38, - 0x99, 0xb6, 0xfe, 0xe0, 0xb3, 0xfd, 0xd1, 0x36, 0x3f, 0xeb, 0x4c, 0x4b, 0xfe, 0xfc, 0x65, 0x64, - 0x56, 0x55, 0xd6, 0xab, 0xf1, 0x5c, 0x61, 0x71, 0x83, 0x3f, 0x33, 0xe8, 0x88, 0xc8, 0xc8, 0xac, - 0xc8, 0xc8, 0xcc, 0xc8, 0xcc, 0xc8, 0x08, 0xb8, 0xea, 0xd1, 0x16, 0xed, 0xd8, 0x8e, 0x77, 0xad, - 0x45, 0xd7, 0xf4, 0xe6, 0xe6, 0x35, 0x6f, 0xb3, 0x43, 0xdd, 0x6b, 0xf4, 0x21, 0xb5, 0x3c, 0xff, - 0xbf, 0xa9, 0x8e, 0x63, 0x7b, 0x36, 0x29, 0xf1, 0x5f, 0x17, 0xce, 0xac, 0xd9, 0x6b, 0x36, 0x82, - 0xae, 0xb1, 0xbf, 0x38, 0xf6, 0xc2, 0xc5, 0x35, 0xdb, 0x5e, 0x6b, 0xd1, 0x6b, 0xf8, 0x6b, 0xb5, - 0x7b, 0xff, 0x9a, 0xeb, 0x39, 0xdd, 0xa6, 0x27, 0xb0, 0x93, 0x71, 0xac, 0x67, 0xb6, 0xa9, 0xeb, - 0xe9, 0xed, 0x8e, 0x20, 0x78, 0x2a, 0x4e, 0xb0, 0xe1, 0xe8, 0x9d, 0x0e, 0x75, 0x44, 0xe5, 0x17, - 0x3e, 0x1e, 0xb4, 0x53, 0x6f, 0x36, 0xa9, 0xeb, 0xb6, 0x4c, 0xd7, 0xbb, 0xf6, 0xf0, 0x25, 0xe9, - 0x97, 0x20, 0x7c, 0x26, 0xfd, 0x83, 0xf0, 0x5f, 0x41, 0xf2, 0x62, 0x3a, 0x89, 0x5f, 0x63, 0xac, - 0x6a, 0xf5, 0x2b, 0x79, 0x18, 0x5c, 0xa4, 0x9e, 0x6e, 0xe8, 0x9e, 0x4e, 0x2e, 0x42, 0x7f, 0xd5, - 0x32, 0xe8, 0x23, 0x25, 0xf7, 0x74, 0xee, 0x4a, 0x61, 0xba, 0xb4, 0xbd, 0x35, 0x99, 0xa7, 0xa6, - 0xc6, 0x81, 0xe4, 0x12, 0x14, 0x57, 0x36, 0x3b, 0x54, 0xc9, 0x3f, 0x9d, 0xbb, 0x32, 0x34, 0x3d, - 0xb4, 0xbd, 0x35, 0xd9, 0x8f, 0x42, 0xd3, 0x10, 0x4c, 0x9e, 0x81, 0x7c, 0xb5, 0xa2, 0x14, 0x10, - 0x79, 0x6a, 0x7b, 0x6b, 0x72, 0xb4, 0x6b, 0x1a, 0x2f, 0xd8, 0x6d, 0xd3, 0xa3, 0xed, 0x8e, 0xb7, - 0xa9, 0xe5, 0xab, 0x15, 0x72, 0x19, 0x8a, 0x33, 0xb6, 0x41, 0x95, 0x22, 0x12, 0x91, 0xed, 0xad, - 0xc9, 0xb1, 0xa6, 0x6d, 0x50, 0x89, 0x0a, 0xf1, 0xe4, 0xd3, 0x50, 0x5c, 0x31, 0xdb, 0x54, 0xe9, - 0x7f, 0x3a, 0x77, 0x65, 0xf8, 0xfa, 0x85, 0x29, 0x2e, 0xbe, 0x29, 0x5f, 0x7c, 0x53, 0x2b, 0xbe, - 0x7c, 0xa7, 0x27, 0xbe, 0xbd, 0x35, 0xd9, 0xb7, 0xbd, 0x35, 0x59, 0x64, 0x22, 0xff, 0xf2, 0x77, - 0x26, 0x73, 0x1a, 0x96, 0x24, 0x6f, 0xc1, 0xf0, 0x4c, 0xab, 0xeb, 0x7a, 0xd4, 0x59, 0xd2, 0xdb, - 0x54, 0x29, 0x61, 0x85, 0x17, 0xb6, 0xb7, 0x26, 0xcf, 0x35, 0x39, 0xb8, 0x61, 0xe9, 0x6d, 0xb9, - 0x62, 0x99, 0x5c, 0xfd, 0x8d, 0x1c, 0x8c, 0xd7, 0xa9, 0xeb, 0x9a, 0xb6, 0x15, 0xc8, 0xe6, 0x63, - 0x30, 0x24, 0x40, 0xd5, 0x0a, 0xca, 0x67, 0x68, 0x7a, 0x60, 0x7b, 0x6b, 0xb2, 0xe0, 0x9a, 0x86, - 0x16, 0x62, 0xc8, 0x27, 0x61, 0xe0, 0x9e, 0xe9, 0xad, 0x2f, 0xce, 0x95, 0x85, 0x9c, 0xce, 0x6d, - 0x6f, 0x4d, 0x92, 0x0d, 0xd3, 0x5b, 0x6f, 0xb4, 0xef, 0xeb, 0x52, 0x85, 0x3e, 0x19, 0x59, 0x80, - 0x89, 0x9a, 0x63, 0x3e, 0xd4, 0x3d, 0x7a, 0x9b, 0x6e, 0xd6, 0xec, 0x96, 0xd9, 0xdc, 0x14, 0x52, - 0x7c, 0x7a, 0x7b, 0x6b, 0xf2, 0x62, 0x87, 0xe3, 0x1a, 0x0f, 0xe8, 0x66, 0xa3, 0x83, 0x58, 0x89, - 0x49, 0xa2, 0xa4, 0xfa, 0x9b, 0x25, 0x18, 0xb9, 0xe3, 0x52, 0x27, 0x68, 0xf7, 0x65, 0x28, 0xb2, - 0xdf, 0xa2, 0xc9, 0x28, 0xf3, 0xae, 0x4b, 0x1d, 0x59, 0xe6, 0x0c, 0x4f, 0xae, 0x42, 0xff, 0x82, - 0xbd, 0x66, 0x5a, 0xa2, 0xd9, 0xa7, 0xb7, 0xb7, 0x26, 0xc7, 0x5b, 0x0c, 0x20, 0x51, 0x72, 0x0a, - 0xf2, 0x7d, 0x30, 0x52, 0x6d, 0x33, 0x1d, 0xb2, 0x2d, 0xdd, 0xb3, 0x1d, 0xd1, 0x5a, 0x94, 0xae, - 0x29, 0xc1, 0xa5, 0x82, 0x11, 0x7a, 0xf2, 0x06, 0x40, 0xf9, 0x5e, 0x5d, 0xb3, 0x5b, 0xb4, 0xac, - 0x2d, 0x09, 0x65, 0xc0, 0xd2, 0xfa, 0x86, 0xdb, 0x70, 0xec, 0x16, 0x6d, 0xe8, 0x8e, 0x5c, 0xad, - 0x44, 0x4d, 0x66, 0x61, 0xac, 0x8c, 0xa3, 0x42, 0xa3, 0x3f, 0xd4, 0xa5, 0xae, 0xe7, 0x2a, 0xfd, - 0x4f, 0x17, 0xae, 0x0c, 0x4d, 0x5f, 0xda, 0xde, 0x9a, 0x7c, 0x82, 0x8f, 0x97, 0x86, 0x23, 0x50, - 0x12, 0x8b, 0x58, 0x21, 0x32, 0x0d, 0xa3, 0xe5, 0x0f, 0xba, 0x0e, 0xad, 0x1a, 0xd4, 0xf2, 0x4c, - 0x6f, 0x53, 0x68, 0xc8, 0xc5, 0xed, 0xad, 0x49, 0x45, 0x67, 0x88, 0x86, 0x29, 0x30, 0x12, 0x93, - 0x68, 0x11, 0xb2, 0x0c, 0xa7, 0x6e, 0xce, 0xd4, 0xea, 0xd4, 0x79, 0x68, 0x36, 0x69, 0xb9, 0xd9, - 0xb4, 0xbb, 0x96, 0xa7, 0x0c, 0x20, 0x9f, 0x67, 0xb6, 0xb7, 0x26, 0x2f, 0xad, 0x35, 0x3b, 0x0d, - 0x97, 0x63, 0x1b, 0x3a, 0x47, 0x4b, 0xcc, 0x92, 0x65, 0xc9, 0x67, 0x61, 0x74, 0xc5, 0x61, 0x5a, - 0x68, 0x54, 0x28, 0x83, 0x2b, 0x83, 0xa8, 0xff, 0xe7, 0xa6, 0xc4, 0x4c, 0xc5, 0xa1, 0x7e, 0xcf, - 0xf2, 0xc6, 0x7a, 0xbc, 0x40, 0xc3, 0x40, 0x9c, 0xdc, 0xd8, 0x08, 0x2b, 0x42, 0x41, 0x61, 0x1f, - 0x6f, 0x3a, 0xd4, 0x48, 0x68, 0xdb, 0x10, 0xb6, 0xf9, 0xea, 0xf6, 0xd6, 0xe4, 0xc7, 0x1c, 0x41, - 0xd3, 0xe8, 0xa9, 0x76, 0x99, 0xac, 0xc8, 0x2c, 0x0c, 0x32, 0x6d, 0xba, 0x6d, 0x5a, 0x86, 0x02, - 0x4f, 0xe7, 0xae, 0x8c, 0x5d, 0x9f, 0xf0, 0x5b, 0xef, 0xc3, 0xa7, 0xcf, 0x6f, 0x6f, 0x4d, 0x9e, - 0x66, 0x3a, 0xd8, 0x78, 0x60, 0x5a, 0xf2, 0x14, 0x11, 0x14, 0x65, 0xa3, 0x68, 0xda, 0xf6, 0x70, - 0xe8, 0x0e, 0x87, 0xa3, 0x68, 0xd5, 0xf6, 0xe2, 0xc3, 0xd6, 0x27, 0x23, 0x33, 0x30, 0x3a, 0x6d, - 0x7b, 0x55, 0xcb, 0xf5, 0x74, 0xab, 0x49, 0xab, 0x15, 0x65, 0x04, 0xcb, 0xa1, 0x5a, 0xb0, 0x72, - 0xa6, 0xc0, 0x34, 0x22, 0x93, 0x52, 0xb4, 0x8c, 0xfa, 0x2f, 0x8a, 0x30, 0xc6, 0xfa, 0x44, 0x1a, - 0x3e, 0x65, 0x36, 0x13, 0x30, 0x08, 0xab, 0xc5, 0xed, 0xe8, 0x4d, 0x2a, 0x46, 0x12, 0x7e, 0x85, - 0xe5, 0x03, 0x25, 0x9e, 0x71, 0x7a, 0x72, 0x15, 0x06, 0x39, 0xa8, 0x5a, 0x11, 0x83, 0x6b, 0x74, - 0x7b, 0x6b, 0x72, 0xc8, 0x45, 0x58, 0xc3, 0x34, 0xb4, 0x00, 0xcd, 0xb4, 0x9b, 0xff, 0x3d, 0x6f, - 0xbb, 0x1e, 0x63, 0x2e, 0xc6, 0x16, 0x7e, 0x86, 0x28, 0xb0, 0x2e, 0x50, 0xb2, 0x76, 0x47, 0x0b, - 0x91, 0xd7, 0x01, 0x38, 0xa4, 0x6c, 0x18, 0x8e, 0x18, 0x60, 0x4f, 0x6c, 0x6f, 0x4d, 0x9e, 0x15, - 0x2c, 0x74, 0xc3, 0x90, 0x47, 0xa7, 0x44, 0x4c, 0xda, 0x30, 0xc2, 0x7f, 0x2d, 0xe8, 0xab, 0xb4, - 0xc5, 0x47, 0xd7, 0xf0, 0xf5, 0x2b, 0x7e, 0x27, 0x46, 0xa5, 0x33, 0x25, 0x93, 0xce, 0x5a, 0x9e, - 0xb3, 0x39, 0x3d, 0x29, 0x26, 0xe4, 0xf3, 0xa2, 0xaa, 0x16, 0xe2, 0xe4, 0xa9, 0x40, 0x2e, 0xc3, - 0xe6, 0xe9, 0x39, 0xdb, 0xd9, 0xd0, 0x1d, 0x83, 0x1a, 0xd3, 0x9b, 0xf2, 0x3c, 0x7d, 0xdf, 0x07, - 0x37, 0x56, 0x65, 0xd5, 0x93, 0xc9, 0x59, 0xa7, 0x73, 0x6e, 0xf5, 0xee, 0x2a, 0xaa, 0xdc, 0x40, - 0x42, 0x5a, 0x6e, 0x77, 0x35, 0xae, 0x66, 0xd1, 0x32, 0x6c, 0x2a, 0xe0, 0x80, 0xbb, 0xd4, 0x61, - 0x93, 0x38, 0x8e, 0x3a, 0x31, 0x15, 0x08, 0x26, 0x0f, 0x39, 0x26, 0xc9, 0x43, 0x14, 0xb9, 0xf0, - 0x0e, 0x9c, 0x4a, 0x88, 0x82, 0x4c, 0x40, 0xe1, 0x01, 0xdd, 0xe4, 0xea, 0xa2, 0xb1, 0x3f, 0xc9, - 0x19, 0xe8, 0x7f, 0xa8, 0xb7, 0xba, 0x62, 0x09, 0xd5, 0xf8, 0x8f, 0x37, 0xf2, 0x9f, 0xca, 0xb1, - 0x15, 0x87, 0xcc, 0xd8, 0x96, 0x45, 0x9b, 0x9e, 0xbc, 0xe8, 0xbc, 0x0a, 0x43, 0x0b, 0x76, 0x53, - 0x6f, 0x61, 0x3f, 0x72, 0xbd, 0x53, 0xb6, 0xb7, 0x26, 0xcf, 0xb0, 0x0e, 0x9c, 0x6a, 0x31, 0x8c, - 0xd4, 0xa6, 0x90, 0x94, 0x29, 0x80, 0x46, 0xdb, 0xb6, 0x47, 0xb1, 0x60, 0x3e, 0x54, 0x00, 0x2c, - 0xe8, 0x20, 0x4a, 0x56, 0x80, 0x90, 0x98, 0x5c, 0x83, 0xc1, 0x1a, 0x5b, 0x67, 0x9b, 0x76, 0x4b, - 0x28, 0x1f, 0x2e, 0x05, 0xb8, 0xf6, 0xca, 0x63, 0xd5, 0x27, 0x52, 0xe7, 0x61, 0x6c, 0xa6, 0x65, - 0x52, 0xcb, 0x93, 0x5b, 0xcd, 0x46, 0x72, 0x79, 0x8d, 0x5a, 0x9e, 0xdc, 0x6a, 0x1c, 0xf3, 0x3a, - 0x83, 0xca, 0xad, 0x0e, 0x48, 0xd5, 0xdf, 0x2b, 0xc0, 0x13, 0xb7, 0xbb, 0xab, 0xd4, 0xb1, 0xa8, - 0x47, 0x5d, 0xb1, 0x20, 0x07, 0x5c, 0x97, 0xe0, 0x54, 0x02, 0x29, 0xb8, 0xe3, 0x42, 0xf9, 0x20, - 0x40, 0x36, 0xc4, 0x1a, 0x2f, 0xcf, 0xb6, 0x89, 0xa2, 0x64, 0x1e, 0xc6, 0x43, 0x20, 0x6b, 0x84, - 0xab, 0xe4, 0x71, 0x29, 0x79, 0x6a, 0x7b, 0x6b, 0xf2, 0x82, 0xc4, 0x8d, 0x35, 0x5b, 0xd6, 0xe0, - 0x78, 0x31, 0x72, 0x1b, 0x26, 0x42, 0xd0, 0x4d, 0xc7, 0xee, 0x76, 0x5c, 0xa5, 0x80, 0xac, 0x26, - 0xb7, 0xb7, 0x26, 0x9f, 0x94, 0x58, 0xad, 0x21, 0x52, 0x5e, 0xc0, 0xe3, 0x05, 0xc9, 0x8f, 0xe4, - 0x64, 0x6e, 0x62, 0x14, 0x16, 0x71, 0x14, 0xbe, 0xe6, 0x8f, 0xc2, 0x4c, 0x21, 0x4d, 0xc5, 0x4b, - 0x8a, 0x41, 0x19, 0x6b, 0x46, 0x62, 0x50, 0x26, 0x6a, 0xbc, 0x30, 0x03, 0x67, 0x53, 0x79, 0xed, - 0x49, 0xab, 0xff, 0x79, 0x41, 0xe6, 0x52, 0xb3, 0x8d, 0xa0, 0x33, 0x97, 0xe5, 0xce, 0xac, 0xd9, - 0x06, 0x4e, 0xf5, 0xb9, 0x70, 0xed, 0x94, 0x1a, 0xdb, 0xb1, 0x8d, 0xf8, 0xac, 0x9f, 0x2c, 0x4b, - 0x3e, 0x0f, 0xe7, 0x12, 0x40, 0x3e, 0x5d, 0x73, 0xed, 0xbf, 0xbc, 0xbd, 0x35, 0xa9, 0xa6, 0x70, - 0x8d, 0xcf, 0xde, 0x19, 0x5c, 0x88, 0x0e, 0xe7, 0x25, 0xa9, 0xdb, 0x96, 0xa7, 0x9b, 0x96, 0x30, - 0x2e, 0xf9, 0x28, 0xf9, 0xf8, 0xf6, 0xd6, 0xe4, 0xb3, 0xb2, 0x0e, 0xfa, 0x34, 0xf1, 0xc6, 0x67, - 0xf1, 0x21, 0x06, 0x28, 0x29, 0xa8, 0x6a, 0x5b, 0x5f, 0xf3, 0x2d, 0xe6, 0x2b, 0xdb, 0x5b, 0x93, - 0xcf, 0xa5, 0xd6, 0x61, 0x32, 0x2a, 0x79, 0x85, 0xce, 0xe2, 0x44, 0x34, 0x20, 0x21, 0x6e, 0xc9, - 0x36, 0x28, 0x7e, 0x43, 0x3f, 0xf2, 0x57, 0xb7, 0xb7, 0x26, 0x9f, 0x92, 0xf8, 0x5b, 0xb6, 0x41, - 0xe3, 0xcd, 0x4f, 0x29, 0xad, 0xfe, 0x46, 0x01, 0x9e, 0xaa, 0x97, 0x17, 0x17, 0xaa, 0x86, 0x6f, - 0xd2, 0xd4, 0x1c, 0xfb, 0xa1, 0x69, 0x48, 0xa3, 0x77, 0x15, 0xce, 0xc7, 0x50, 0xb3, 0x68, 0x45, - 0x05, 0xc6, 0x34, 0x7e, 0x9b, 0x6f, 0x2e, 0x75, 0x04, 0x4d, 0x83, 0x9b, 0x5a, 0xd1, 0x45, 0x3b, - 0x8b, 0x11, 0xeb, 0xa3, 0x18, 0xaa, 0xbe, 0x6e, 0x3b, 0x5e, 0xb3, 0xeb, 0x09, 0x25, 0xc0, 0x3e, - 0x4a, 0xd4, 0xe1, 0x0a, 0xa2, 0x1e, 0x55, 0xf8, 0x7c, 0xc8, 0x8f, 0xe7, 0x60, 0xa2, 0xec, 0x79, - 0x8e, 0xb9, 0xda, 0xf5, 0xe8, 0xa2, 0xde, 0xe9, 0x98, 0xd6, 0x1a, 0x8e, 0xf5, 0xe1, 0xeb, 0x6f, - 0x05, 0x6b, 0x64, 0x4f, 0x49, 0x4c, 0xc5, 0x8b, 0x4b, 0x43, 0x54, 0xf7, 0x51, 0x8d, 0x36, 0xc7, - 0xc9, 0x43, 0x34, 0x5e, 0x8e, 0x0d, 0xd1, 0x54, 0x5e, 0x7b, 0x1a, 0xa2, 0x5f, 0x29, 0xc0, 0xc5, - 0xe5, 0x07, 0x9e, 0xae, 0x51, 0xd7, 0xee, 0x3a, 0x4d, 0xea, 0xde, 0xe9, 0x18, 0xba, 0x47, 0xc3, - 0x91, 0x3a, 0x09, 0xfd, 0x65, 0xc3, 0xa0, 0x06, 0xb2, 0xeb, 0xe7, 0xdb, 0x3e, 0x9d, 0x01, 0x34, - 0x0e, 0x27, 0x1f, 0x83, 0x01, 0x51, 0x06, 0xb9, 0xf7, 0x4f, 0x0f, 0x6f, 0x6f, 0x4d, 0x0e, 0x74, - 0x39, 0x48, 0xf3, 0x71, 0x8c, 0xac, 0x42, 0x5b, 0x94, 0x91, 0x15, 0x42, 0x32, 0x83, 0x83, 0x34, - 0x1f, 0x47, 0xde, 0x85, 0x31, 0x64, 0x1b, 0xb4, 0x47, 0xcc, 0x7d, 0x67, 0x7c, 0xe9, 0xca, 0x8d, - 0xe5, 0x4b, 0x13, 0xb6, 0xa6, 0xe1, 0xf8, 0x05, 0xb4, 0x18, 0x03, 0x72, 0x0f, 0x26, 0x44, 0x23, - 0x42, 0xa6, 0xfd, 0x3d, 0x98, 0x9e, 0xdd, 0xde, 0x9a, 0x3c, 0x25, 0xda, 0x2f, 0xb1, 0x4d, 0x30, - 0x61, 0x8c, 0x45, 0xb3, 0x43, 0xc6, 0xa5, 0x9d, 0x18, 0x8b, 0x2f, 0x96, 0x19, 0xc7, 0x99, 0xa8, - 0xef, 0xc1, 0x88, 0x5c, 0x90, 0x9c, 0xc3, 0xad, 0x35, 0x1f, 0x27, 0xb8, 0x29, 0x37, 0x0d, 0xdc, - 0x4f, 0xbf, 0x04, 0xc3, 0x15, 0xea, 0x36, 0x1d, 0xb3, 0xc3, 0xac, 0x06, 0xa1, 0xe4, 0xe3, 0xdb, - 0x5b, 0x93, 0xc3, 0x46, 0x08, 0xd6, 0x64, 0x1a, 0xf5, 0xff, 0xce, 0xc1, 0x39, 0xc6, 0xbb, 0xec, - 0xba, 0xe6, 0x9a, 0xd5, 0x96, 0x97, 0xed, 0x17, 0xa0, 0x54, 0xc7, 0xfa, 0x44, 0x4d, 0x67, 0xb6, - 0xb7, 0x26, 0x27, 0x78, 0x0b, 0x24, 0x3d, 0x14, 0x34, 0xc1, 0xbe, 0x32, 0xbf, 0xc3, 0xbe, 0x92, - 0x99, 0xb4, 0x9e, 0xee, 0x78, 0xa6, 0xb5, 0x56, 0xf7, 0x74, 0xaf, 0xeb, 0x46, 0x4c, 0x5a, 0x81, - 0x69, 0xb8, 0x88, 0x8a, 0x98, 0xb4, 0x91, 0x42, 0xe4, 0x1d, 0x18, 0x99, 0xb5, 0x8c, 0x90, 0x09, - 0x9f, 0x10, 0x9f, 0x64, 0x96, 0x26, 0x45, 0x78, 0x92, 0x45, 0xa4, 0x80, 0xfa, 0x37, 0x72, 0xa0, - 0xf0, 0x4d, 0xe0, 0x82, 0xe9, 0x7a, 0x8b, 0xb4, 0xbd, 0x2a, 0xcd, 0x4e, 0x73, 0xfe, 0xae, 0x92, - 0xe1, 0xa4, 0xb5, 0x08, 0x4d, 0x01, 0xb1, 0xab, 0x6c, 0x99, 0x6e, 0x62, 0xfb, 0x11, 0x2b, 0x45, - 0xaa, 0x30, 0xc0, 0x39, 0x73, 0x5b, 0x62, 0xf8, 0xba, 0xe2, 0x2b, 0x42, 0xbc, 0x6a, 0xae, 0x0c, - 0x6d, 0x4e, 0x2c, 0x6f, 0x68, 0x44, 0x79, 0xf5, 0x6b, 0x05, 0x98, 0x88, 0x17, 0x22, 0xf7, 0x60, - 0xf0, 0x96, 0x6d, 0x5a, 0xd4, 0x58, 0xb6, 0xb0, 0x85, 0xbd, 0x0f, 0x47, 0x7c, 0x5b, 0xfc, 0xf4, - 0xfb, 0x58, 0xa6, 0x21, 0x5b, 0xb0, 0x78, 0x56, 0x12, 0x30, 0x23, 0x9f, 0x85, 0x21, 0x66, 0x03, - 0x3e, 0x44, 0xce, 0xf9, 0x1d, 0x39, 0x3f, 0x2d, 0x38, 0x9f, 0x71, 0x78, 0xa1, 0x24, 0xeb, 0x90, - 0x1d, 0xd3, 0x2b, 0x8d, 0xea, 0xae, 0x6d, 0x89, 0x9e, 0x47, 0xbd, 0x72, 0x10, 0x22, 0xeb, 0x15, - 0xa7, 0x61, 0xa6, 0x2b, 0xff, 0x58, 0xec, 0x06, 0x69, 0xef, 0xc2, 0x65, 0x15, 0xef, 0x01, 0x89, - 0x98, 0x58, 0x30, 0x2e, 0x04, 0xba, 0x6e, 0x76, 0xd0, 0xea, 0xc7, 0x75, 0x6d, 0xec, 0xfa, 0xe5, - 0x29, 0xff, 0x50, 0x6c, 0x4a, 0x3a, 0x52, 0x7b, 0xf8, 0xd2, 0xd4, 0x62, 0x40, 0x8e, 0x3b, 0x53, - 0xd4, 0xc9, 0x18, 0x0b, 0xb9, 0xb7, 0xdb, 0x11, 0x72, 0xf5, 0x47, 0xf3, 0xf0, 0x62, 0xd8, 0x45, - 0x1a, 0x7d, 0x68, 0xd2, 0x8d, 0x90, 0xa3, 0xd8, 0x23, 0xb3, 0x21, 0xe6, 0xce, 0xac, 0xeb, 0xd6, - 0x1a, 0x35, 0xc8, 0x55, 0xe8, 0xd7, 0xec, 0x16, 0x75, 0x95, 0x1c, 0x9a, 0x87, 0x38, 0x7d, 0x39, - 0x0c, 0x20, 0x1f, 0xb2, 0x20, 0x05, 0xb1, 0xa1, 0xb4, 0xe2, 0xe8, 0xa6, 0xe7, 0x6b, 0x52, 0x39, - 0xa9, 0x49, 0xbb, 0xa8, 0x71, 0x8a, 0xf3, 0xe0, 0x6b, 0x0c, 0x0a, 0xde, 0x43, 0x80, 0x2c, 0x78, - 0x4e, 0x72, 0xe1, 0x75, 0x18, 0x96, 0x88, 0xf7, 0xb4, 0x88, 0x7c, 0xa3, 0x28, 0x8f, 0x2d, 0xbf, - 0x59, 0x62, 0x6c, 0x5d, 0x63, 0x63, 0xc2, 0x75, 0x99, 0x15, 0xc3, 0x07, 0x95, 0xd0, 0x7c, 0x04, - 0x45, 0x35, 0x1f, 0x41, 0xe4, 0x65, 0x18, 0xe4, 0x2c, 0x82, 0xfd, 0x32, 0xee, 0xb5, 0x1d, 0x84, - 0x45, 0x4d, 0x81, 0x80, 0x90, 0xfc, 0x62, 0x0e, 0x2e, 0xf5, 0x94, 0x04, 0x2a, 0xdf, 0xf0, 0xf5, - 0x57, 0xf6, 0x25, 0xc6, 0xe9, 0x17, 0xb7, 0xb7, 0x26, 0xaf, 0x4a, 0x9a, 0xe1, 0x48, 0x34, 0x8d, - 0x26, 0x27, 0x92, 0xda, 0xd5, 0xbb, 0x29, 0xcc, 0x58, 0xe5, 0x95, 0xce, 0xe1, 0x51, 0x95, 0xd5, - 0xdc, 0xf4, 0x1b, 0x59, 0x0c, 0x8d, 0x55, 0xf1, 0xbd, 0xf7, 0x7d, 0x92, 0x94, 0x6a, 0x32, 0xb8, - 0x90, 0x26, 0x9c, 0xe7, 0x98, 0x8a, 0xbe, 0xb9, 0x7c, 0x7f, 0xd1, 0xb6, 0xbc, 0x75, 0xbf, 0x82, - 0x7e, 0xf9, 0xac, 0x07, 0x2b, 0x30, 0xf4, 0xcd, 0x86, 0x7d, 0xbf, 0xd1, 0x66, 0x54, 0x29, 0x75, - 0x64, 0x71, 0x62, 0x13, 0xbb, 0x18, 0xe3, 0xfe, 0x94, 0x57, 0x0a, 0x4f, 0xe2, 0xfc, 0x79, 0x21, - 0x39, 0xc1, 0xc5, 0x0a, 0xa9, 0x55, 0x18, 0x59, 0xb0, 0x9b, 0x0f, 0x02, 0x75, 0x79, 0x1d, 0x4a, - 0x2b, 0xba, 0xb3, 0x46, 0x3d, 0x94, 0xc5, 0xf0, 0xf5, 0x53, 0x53, 0xfc, 0x74, 0x9b, 0x11, 0x71, - 0xc4, 0xf4, 0x98, 0x98, 0x7d, 0x4a, 0x1e, 0xfe, 0xd6, 0x44, 0x01, 0xf5, 0x3b, 0xfd, 0x30, 0x22, - 0x4e, 0x62, 0x71, 0xf5, 0x20, 0x6f, 0x84, 0x67, 0xdb, 0x62, 0xba, 0x0c, 0x4e, 0xa3, 0x82, 0x53, - 0xb4, 0x11, 0xc6, 0xec, 0xf7, 0xb7, 0x26, 0x73, 0xdb, 0x5b, 0x93, 0x7d, 0xda, 0xa0, 0xb4, 0x89, - 0x0d, 0xd7, 0x37, 0x69, 0x41, 0x97, 0xcf, 0x56, 0x63, 0x65, 0xf9, 0x7a, 0xf7, 0x0e, 0x0c, 0x88, - 0x36, 0x08, 0x8d, 0x3b, 0x1f, 0x9e, 0x9d, 0x44, 0x4e, 0x94, 0x63, 0xa5, 0xfd, 0x52, 0xe4, 0x2d, - 0x28, 0xf1, 0xb3, 0x04, 0x21, 0x80, 0x73, 0xe9, 0x67, 0x2f, 0xb1, 0xe2, 0xa2, 0x0c, 0x99, 0x07, - 0x08, 0xcf, 0x11, 0x82, 0x03, 0x74, 0xc1, 0x21, 0x79, 0xc2, 0x10, 0xe3, 0x22, 0x95, 0x25, 0xaf, - 0xc2, 0xc8, 0x0a, 0x75, 0xda, 0xa6, 0xa5, 0xb7, 0xea, 0xe6, 0x07, 0xfe, 0x19, 0x3a, 0x2e, 0xf4, - 0xae, 0xf9, 0x81, 0x3c, 0x72, 0x23, 0x74, 0xe4, 0x73, 0x69, 0xfb, 0xf4, 0x01, 0x6c, 0xc8, 0x33, - 0x3b, 0x6e, 0x60, 0x63, 0xed, 0x49, 0xd9, 0xb6, 0xbf, 0x0b, 0xa3, 0x91, 0x2d, 0x9a, 0x38, 0x24, - 0xbd, 0x94, 0x64, 0x2d, 0xed, 0x37, 0x63, 0x6c, 0xa3, 0x1c, 0x98, 0x26, 0x57, 0x2d, 0xd3, 0x33, - 0xf5, 0xd6, 0x8c, 0xdd, 0x6e, 0xeb, 0x96, 0xa1, 0x0c, 0x85, 0x9a, 0x6c, 0x72, 0x4c, 0xa3, 0xc9, - 0x51, 0xb2, 0x26, 0x47, 0x0b, 0x91, 0xdb, 0x30, 0x21, 0xfa, 0x50, 0xa3, 0x4d, 0xdb, 0x61, 0xb6, - 0x07, 0x9e, 0x81, 0x8a, 0x63, 0x00, 0x97, 0xe3, 0x1a, 0x8e, 0x8f, 0x94, 0x8d, 0xfb, 0x78, 0xc1, - 0x5b, 0xc5, 0xc1, 0xe1, 0x89, 0x91, 0xf8, 0xb1, 0xb5, 0xfa, 0xd7, 0x0a, 0x30, 0x2c, 0x48, 0xd9, - 0xd2, 0x7d, 0xa2, 0xe0, 0x07, 0x51, 0xf0, 0x54, 0x45, 0x2d, 0x1d, 0x96, 0xa2, 0xaa, 0x5f, 0xcc, - 0x07, 0xb3, 0x51, 0xcd, 0x31, 0xad, 0x83, 0xcd, 0x46, 0x97, 0x01, 0x66, 0xd6, 0xbb, 0xd6, 0x03, - 0x7e, 0x3d, 0x97, 0x0f, 0xaf, 0xe7, 0x9a, 0xa6, 0x26, 0x61, 0xc8, 0x25, 0x28, 0x56, 0x18, 0x7f, - 0xd6, 0x33, 0x23, 0xd3, 0x43, 0xdf, 0xe6, 0x9c, 0x72, 0x2f, 0x6a, 0x08, 0x66, 0x9b, 0xb9, 0xe9, - 0x4d, 0x8f, 0x72, 0xf3, 0xb9, 0xc0, 0x37, 0x73, 0xab, 0x0c, 0xa0, 0x71, 0x38, 0xb9, 0x01, 0xa7, - 0x2a, 0xb4, 0xa5, 0x6f, 0x2e, 0x9a, 0xad, 0x96, 0xe9, 0xd2, 0xa6, 0x6d, 0x19, 0x2e, 0x0a, 0x59, - 0x54, 0xd7, 0x76, 0xb5, 0x24, 0x01, 0x51, 0xa1, 0xb4, 0x7c, 0xff, 0xbe, 0x4b, 0x3d, 0x14, 0x5f, - 0x61, 0x1a, 0xd8, 0xe4, 0x6c, 0x23, 0x44, 0x13, 0x18, 0xf5, 0xeb, 0x39, 0xb6, 0x5b, 0x72, 0x1f, - 0x78, 0x76, 0x27, 0xd0, 0xf2, 0x03, 0x89, 0xe4, 0x6a, 0x68, 0x57, 0xe4, 0xf1, 0x6b, 0xc7, 0xc5, - 0xd7, 0x0e, 0x08, 0xdb, 0x22, 0xb4, 0x28, 0x52, 0xbf, 0xaa, 0xb0, 0xc3, 0x57, 0xa9, 0x7f, 0x94, - 0x87, 0xf3, 0xa2, 0xc5, 0x33, 0x2d, 0xb3, 0xb3, 0x6a, 0xeb, 0x8e, 0xa1, 0xd1, 0x26, 0x35, 0x1f, - 0xd2, 0xe3, 0x39, 0xf0, 0xa2, 0x43, 0xa7, 0x78, 0x80, 0xa1, 0x73, 0x1d, 0x37, 0x9e, 0x4c, 0x32, - 0x78, 0xc0, 0xcc, 0x8d, 0x8a, 0x89, 0xed, 0xad, 0xc9, 0x11, 0x83, 0x83, 0xf1, 0x8a, 0x41, 0x93, - 0x89, 0x98, 0x92, 0x2c, 0x50, 0x6b, 0xcd, 0x5b, 0x47, 0x25, 0xe9, 0xe7, 0x4a, 0xd2, 0x42, 0x88, - 0x26, 0x30, 0xea, 0xff, 0x96, 0x87, 0x33, 0x71, 0x91, 0xd7, 0xa9, 0x65, 0x9c, 0xc8, 0xfb, 0xc3, - 0x91, 0xf7, 0x1f, 0x17, 0xe0, 0x49, 0x51, 0xa6, 0xbe, 0xae, 0x3b, 0xd4, 0xa8, 0x98, 0x0e, 0x6d, - 0x7a, 0xb6, 0xb3, 0x79, 0x8c, 0x0d, 0xa8, 0xc3, 0x13, 0xfb, 0x0d, 0x28, 0x89, 0xe3, 0x06, 0xbe, - 0xce, 0x8c, 0x05, 0x2d, 0x41, 0x68, 0x62, 0x85, 0xe2, 0x47, 0x15, 0xb1, 0xce, 0x2a, 0xed, 0xa6, - 0xb3, 0x3e, 0x05, 0xa3, 0x81, 0xe8, 0x71, 0xe3, 0x3b, 0x10, 0x5a, 0x5b, 0x86, 0x8f, 0xc0, 0xbd, - 0xaf, 0x16, 0x25, 0xc4, 0xda, 0x7c, 0x40, 0xb5, 0x82, 0xd6, 0xd0, 0xa8, 0xa8, 0x2d, 0x28, 0x67, - 0x1a, 0x9a, 0x4c, 0xa4, 0x6e, 0x15, 0xe1, 0x42, 0x7a, 0xb7, 0x6b, 0x54, 0x37, 0x4e, 0x7a, 0xfd, - 0x7b, 0xb2, 0xd7, 0xc9, 0x33, 0x50, 0xac, 0xe9, 0xde, 0xba, 0xb8, 0xee, 0xc7, 0x3b, 0xe8, 0xfb, - 0x66, 0x8b, 0x36, 0x3a, 0xba, 0xb7, 0xae, 0x21, 0x4a, 0x9a, 0x33, 0x00, 0x39, 0xa6, 0xcc, 0x19, - 0xd2, 0x62, 0x3f, 0xfc, 0x74, 0xee, 0x4a, 0x31, 0x75, 0xb1, 0xff, 0x4e, 0x31, 0x6b, 0x5e, 0xb9, - 0xe7, 0x98, 0x1e, 0x3d, 0xd1, 0xb0, 0x13, 0x0d, 0x3b, 0xa0, 0x86, 0xfd, 0xa3, 0x3c, 0x8c, 0x06, - 0x9b, 0xa6, 0xf7, 0x69, 0xf3, 0x68, 0xd6, 0xaa, 0x70, 0x2b, 0x53, 0x38, 0xf0, 0x56, 0xe6, 0x20, - 0x0a, 0xa5, 0x06, 0x47, 0xac, 0xdc, 0x34, 0x40, 0x89, 0xf1, 0x23, 0xd6, 0xe0, 0x60, 0xf5, 0x19, - 0x18, 0x58, 0xd4, 0x1f, 0x99, 0xed, 0x6e, 0x5b, 0x58, 0xe9, 0xe8, 0xbe, 0xd6, 0xd6, 0x1f, 0x69, - 0x3e, 0x5c, 0xfd, 0x6f, 0x72, 0x30, 0x26, 0x84, 0x2a, 0x98, 0x1f, 0x48, 0xaa, 0xa1, 0x74, 0xf2, - 0x07, 0x96, 0x4e, 0x61, 0xff, 0xd2, 0x51, 0xff, 0x52, 0x01, 0x94, 0x39, 0xb3, 0x45, 0x57, 0x1c, - 0xdd, 0x72, 0xef, 0x53, 0x47, 0x6c, 0xa7, 0x67, 0x19, 0xab, 0x03, 0x7d, 0xa0, 0x34, 0xa5, 0xe4, - 0xf7, 0x35, 0xa5, 0x7c, 0x02, 0x86, 0x44, 0x63, 0x02, 0xd7, 0x49, 0x1c, 0x35, 0x8e, 0x0f, 0xd4, - 0x42, 0x3c, 0x23, 0x2e, 0x77, 0x3a, 0x8e, 0xfd, 0x90, 0x3a, 0xfc, 0x56, 0x4c, 0x10, 0xeb, 0x3e, - 0x50, 0x0b, 0xf1, 0x12, 0x67, 0xea, 0xdb, 0x8b, 0x32, 0x67, 0xea, 0x68, 0x21, 0x9e, 0x5c, 0x81, - 0xc1, 0x05, 0xbb, 0xa9, 0xa3, 0xa0, 0xf9, 0xb4, 0x32, 0xb2, 0xbd, 0x35, 0x39, 0xd8, 0x12, 0x30, - 0x2d, 0xc0, 0x32, 0xca, 0x8a, 0xbd, 0x61, 0xb5, 0x6c, 0x9d, 0x3b, 0xdb, 0x0c, 0x72, 0x4a, 0x43, - 0xc0, 0xb4, 0x00, 0xcb, 0x28, 0x99, 0xcc, 0xd1, 0x89, 0x69, 0x30, 0xe4, 0x79, 0x5f, 0xc0, 0xb4, - 0x00, 0xab, 0x7e, 0xbd, 0xc8, 0xb4, 0xd7, 0x35, 0x3f, 0x78, 0xec, 0xd7, 0x85, 0x70, 0xc0, 0xf4, - 0xef, 0x63, 0xc0, 0x3c, 0x36, 0x07, 0x76, 0xea, 0xbf, 0x18, 0x00, 0x10, 0xd2, 0x9f, 0x3d, 0xd9, - 0x1c, 0x1e, 0x4c, 0x6b, 0x2a, 0x70, 0x6a, 0xd6, 0x5a, 0xd7, 0xad, 0x26, 0x35, 0xc2, 0x63, 0xcb, - 0x12, 0x0e, 0x6d, 0x74, 0xba, 0xa4, 0x02, 0x19, 0x9e, 0x5b, 0x6a, 0xc9, 0x02, 0xe4, 0x25, 0x18, - 0xae, 0x5a, 0x1e, 0x75, 0xf4, 0xa6, 0x67, 0x3e, 0xa4, 0x62, 0x6a, 0xc0, 0x9b, 0x68, 0x33, 0x04, - 0x6b, 0x32, 0x0d, 0xb9, 0x01, 0x23, 0x35, 0xdd, 0xf1, 0xcc, 0xa6, 0xd9, 0xd1, 0x2d, 0xcf, 0x55, - 0x06, 0x71, 0x46, 0x43, 0x0b, 0xa3, 0x23, 0xc1, 0xb5, 0x08, 0x15, 0xf9, 0x1c, 0x0c, 0xe1, 0xd6, - 0x14, 0xfd, 0xc3, 0x87, 0x76, 0xbc, 0xa8, 0x7c, 0x36, 0x74, 0x47, 0xe4, 0xa7, 0xaf, 0x78, 0xe3, - 0x1c, 0xbf, 0xab, 0x0c, 0x38, 0x92, 0xcf, 0xc0, 0xc0, 0xac, 0x65, 0x20, 0x73, 0xd8, 0x91, 0xb9, - 0x2a, 0x98, 0x9f, 0x0b, 0x99, 0xdb, 0x9d, 0x18, 0x6f, 0x9f, 0x5d, 0xfa, 0x28, 0x1b, 0xfe, 0xf0, - 0x46, 0xd9, 0xc8, 0x87, 0x70, 0x2c, 0x3e, 0x7a, 0x58, 0xc7, 0xe2, 0x63, 0xfb, 0x3c, 0x16, 0x57, - 0x3f, 0x80, 0xe1, 0xe9, 0xda, 0x5c, 0x30, 0x7a, 0x9f, 0x80, 0x42, 0x4d, 0x78, 0x46, 0x14, 0xb9, - 0x3d, 0xd3, 0x31, 0x0d, 0x8d, 0xc1, 0xc8, 0x55, 0x18, 0x9c, 0x41, 0x77, 0x3b, 0x71, 0x8b, 0x58, - 0xe4, 0xeb, 0x5f, 0x13, 0x61, 0xe8, 0x75, 0xeb, 0xa3, 0xc9, 0xc7, 0x60, 0xa0, 0xe6, 0xd8, 0x6b, - 0x8e, 0xde, 0x16, 0x6b, 0x30, 0xba, 0xa6, 0x74, 0x38, 0x48, 0xf3, 0x71, 0xea, 0x4f, 0xe5, 0x7c, - 0xb3, 0x9d, 0x95, 0xa8, 0x77, 0xf1, 0x68, 0x1e, 0xeb, 0x1e, 0xe4, 0x25, 0x5c, 0x0e, 0xd2, 0x7c, - 0x1c, 0xb9, 0x0a, 0xfd, 0xb3, 0x8e, 0x63, 0x3b, 0xb2, 0x4f, 0x3d, 0x65, 0x00, 0xf9, 0xba, 0x17, - 0x29, 0xc8, 0x6b, 0x30, 0xcc, 0xe7, 0x1c, 0x7e, 0xa2, 0x59, 0xe8, 0x75, 0x53, 0x2a, 0x53, 0xaa, - 0xdf, 0x2a, 0x48, 0x36, 0x1b, 0x97, 0xf8, 0x63, 0x78, 0x2b, 0xf0, 0x32, 0x14, 0xa6, 0x6b, 0x73, - 0x62, 0x02, 0x3c, 0xed, 0x17, 0x95, 0x54, 0x25, 0x56, 0x8e, 0x51, 0x93, 0x8b, 0x50, 0xac, 0x31, - 0xf5, 0x29, 0xa1, 0x7a, 0x0c, 0x6e, 0x6f, 0x4d, 0x16, 0x3b, 0x4c, 0x7f, 0x10, 0x8a, 0x58, 0xb6, - 0x99, 0xe1, 0x3b, 0x26, 0x8e, 0x0d, 0xf7, 0x31, 0x17, 0xa1, 0x58, 0x76, 0xd6, 0x1e, 0x8a, 0x59, - 0x0b, 0xb1, 0xba, 0xb3, 0xf6, 0x50, 0x43, 0x28, 0xb9, 0x06, 0xa0, 0x51, 0xaf, 0xeb, 0x58, 0xf8, - 0xdc, 0x65, 0x08, 0xcf, 0xdf, 0x70, 0x36, 0x74, 0x10, 0xda, 0x68, 0xda, 0x06, 0xd5, 0x24, 0x12, - 0xf5, 0xaf, 0x86, 0x17, 0x3b, 0x15, 0xd3, 0x7d, 0x70, 0xd2, 0x85, 0x7b, 0xe8, 0x42, 0x5d, 0x1c, - 0x71, 0x26, 0x3b, 0x69, 0x12, 0xfa, 0xe7, 0x5a, 0xfa, 0x9a, 0x8b, 0x7d, 0x28, 0x7c, 0xd7, 0xee, - 0x33, 0x80, 0xc6, 0xe1, 0xb1, 0x7e, 0x1a, 0xdc, 0xb9, 0x9f, 0xbe, 0xda, 0x1f, 0x8c, 0xb6, 0x25, - 0xea, 0x6d, 0xd8, 0xce, 0x49, 0x57, 0xed, 0xb6, 0xab, 0x2e, 0xc3, 0x40, 0xdd, 0x69, 0x4a, 0x47, - 0x17, 0xb8, 0x1f, 0x70, 0x9d, 0x26, 0x3f, 0xb6, 0xf0, 0x91, 0x8c, 0xae, 0xe2, 0x7a, 0x48, 0x37, - 0x10, 0xd2, 0x19, 0xae, 0x27, 0xe8, 0x04, 0x52, 0xd0, 0xd5, 0x6c, 0xc7, 0x13, 0x1d, 0x17, 0xd0, - 0x75, 0x6c, 0xc7, 0xd3, 0x7c, 0x24, 0xf9, 0x04, 0xc0, 0xca, 0x4c, 0xcd, 0x77, 0xee, 0x1f, 0x0a, - 0x7d, 0x0f, 0x85, 0x57, 0xbf, 0x26, 0xa1, 0xc9, 0x0a, 0x0c, 0x2d, 0x77, 0xa8, 0xc3, 0xb7, 0x42, - 0xfc, 0x01, 0xcb, 0xc7, 0x63, 0xa2, 0x15, 0xfd, 0x3e, 0x25, 0xfe, 0x0f, 0xc8, 0xf9, 0xfa, 0x62, - 0xfb, 0x3f, 0xb5, 0x90, 0x11, 0x79, 0x0d, 0x4a, 0x65, 0x6e, 0xe7, 0x0d, 0x23, 0xcb, 0x40, 0x64, - 0xb8, 0x05, 0xe5, 0x28, 0xbe, 0x67, 0xd7, 0xf1, 0x6f, 0x4d, 0x90, 0xab, 0x57, 0x61, 0x22, 0x5e, - 0x0d, 0x19, 0x86, 0x81, 0x99, 0xe5, 0xa5, 0xa5, 0xd9, 0x99, 0x95, 0x89, 0x3e, 0x32, 0x08, 0xc5, - 0xfa, 0xec, 0x52, 0x65, 0x22, 0xa7, 0xfe, 0x92, 0x34, 0x83, 0x30, 0xd5, 0x3a, 0xb9, 0x1a, 0x3e, - 0xd0, 0x7d, 0xcb, 0x04, 0xde, 0x87, 0xe2, 0x89, 0x41, 0xdb, 0xf4, 0x3c, 0x6a, 0x88, 0x55, 0x02, - 0xef, 0x0b, 0xbd, 0x47, 0x5a, 0x02, 0x4f, 0x5e, 0x80, 0x51, 0x84, 0x89, 0x2b, 0x42, 0xbe, 0x3f, - 0x16, 0x05, 0x9c, 0x47, 0x5a, 0x14, 0xa9, 0xfe, 0x6e, 0x78, 0x3b, 0xbc, 0x40, 0xf5, 0xe3, 0x7a, - 0xa3, 0xf8, 0x11, 0xe9, 0x2f, 0xf5, 0x5f, 0x16, 0xf9, 0x93, 0x13, 0xfe, 0x3e, 0xf1, 0x28, 0x44, - 0x19, 0x1e, 0xe9, 0x16, 0xf6, 0x70, 0xa4, 0xfb, 0x02, 0x94, 0x16, 0xa9, 0xb7, 0x6e, 0xfb, 0x8e, - 0x5f, 0xe8, 0xa1, 0xd7, 0x46, 0x88, 0xec, 0xa1, 0xc7, 0x69, 0xc8, 0x03, 0x20, 0xfe, 0xe3, 0xc3, - 0xc0, 0xf1, 0xdb, 0x3f, 0x42, 0x3e, 0x9f, 0xd8, 0xa7, 0xd4, 0xf1, 0x89, 0x32, 0xfa, 0xf4, 0x9f, - 0x09, 0x1c, 0xcb, 0x25, 0x4f, 0xac, 0x3f, 0xd9, 0x9a, 0x2c, 0x71, 0x1a, 0x2d, 0x85, 0x2d, 0x79, - 0x17, 0x86, 0x16, 0xe7, 0xca, 0xe2, 0x21, 0x22, 0xf7, 0x8a, 0x78, 0x22, 0x90, 0xa2, 0x8f, 0x08, - 0x44, 0x82, 0xef, 0x7b, 0xda, 0xf7, 0xf5, 0xe4, 0x3b, 0xc4, 0x90, 0x0b, 0xd3, 0x16, 0xfe, 0x52, - 0x48, 0x9c, 0x2e, 0x04, 0xda, 0x12, 0x7d, 0x3f, 0x14, 0x97, 0x15, 0xc7, 0xc6, 0xb4, 0x65, 0xf0, - 0x00, 0xa3, 0x7b, 0x19, 0x4e, 0x95, 0x3b, 0x9d, 0x96, 0x49, 0x0d, 0xd4, 0x17, 0xad, 0xdb, 0xa2, - 0xae, 0x70, 0xf9, 0xc1, 0xc7, 0x27, 0x3a, 0x47, 0x36, 0xf0, 0xf9, 0x6b, 0xc3, 0xe9, 0x46, 0xfd, - 0x33, 0x93, 0x65, 0xd5, 0x9f, 0xc9, 0xc3, 0xb9, 0x19, 0x87, 0xea, 0x1e, 0x5d, 0x9c, 0x2b, 0x97, - 0xbb, 0xe8, 0x23, 0xd7, 0x6a, 0x51, 0x6b, 0xed, 0x68, 0x86, 0xf5, 0x9b, 0x30, 0x16, 0x34, 0xa0, - 0xde, 0xb4, 0x3b, 0x54, 0x7e, 0xc8, 0xd5, 0xf4, 0x31, 0x0d, 0x97, 0xa1, 0xb4, 0x18, 0x29, 0xb9, - 0x0d, 0xa7, 0x03, 0x48, 0xb9, 0xd5, 0xb2, 0x37, 0x34, 0xda, 0x75, 0xb9, 0x23, 0xee, 0x20, 0x77, - 0xc4, 0x0d, 0x39, 0xe8, 0x0c, 0xdf, 0x70, 0x18, 0x81, 0x96, 0x56, 0x4a, 0xfd, 0x5a, 0x01, 0xce, - 0xdf, 0xd5, 0x5b, 0xa6, 0x11, 0x8a, 0x46, 0xa3, 0x6e, 0xc7, 0xb6, 0x5c, 0x7a, 0x8c, 0x46, 0x69, - 0x64, 0x28, 0x14, 0x0f, 0x65, 0x28, 0x24, 0xbb, 0xa8, 0xff, 0xc0, 0x5d, 0x54, 0xda, 0x57, 0x17, - 0xfd, 0xaf, 0x39, 0x98, 0xf0, 0x1f, 0x1a, 0xc8, 0x8f, 0xc6, 0x25, 0x2f, 0x78, 0x3c, 0x42, 0x8c, - 0xf9, 0x5d, 0x23, 0x9e, 0xd4, 0x61, 0x60, 0xf6, 0x51, 0xc7, 0x74, 0xa8, 0xbb, 0x0b, 0xa7, 0xf1, - 0x4b, 0xe2, 0xb8, 0xe4, 0x14, 0xe5, 0x45, 0x12, 0x27, 0x25, 0x1c, 0x8c, 0xcf, 0x07, 0xf9, 0x53, - 0x8b, 0x69, 0xff, 0x25, 0x3c, 0x7f, 0x3e, 0x28, 0x9e, 0x64, 0x44, 0xde, 0x83, 0x86, 0xa4, 0xe4, - 0x59, 0x28, 0xac, 0xac, 0x2c, 0x88, 0x99, 0x14, 0x23, 0x10, 0x78, 0x9e, 0xfc, 0x3e, 0x92, 0x61, - 0xd5, 0x7f, 0x9a, 0x07, 0x60, 0xaa, 0xc0, 0x87, 0xeb, 0x91, 0x28, 0xe1, 0x34, 0x0c, 0xfa, 0x02, - 0x17, 0x6a, 0x18, 0xbc, 0x12, 0x88, 0x77, 0x44, 0xbc, 0xee, 0xe0, 0x45, 0xc8, 0xa4, 0xef, 0x48, - 0xce, 0xef, 0x01, 0x70, 0x67, 0x83, 0x8e, 0xe4, 0xbe, 0xfb, 0xf8, 0x27, 0x60, 0x48, 0xcc, 0x78, - 0x76, 0xe4, 0xfc, 0xbf, 0xe9, 0x03, 0xb5, 0x10, 0x1f, 0x9b, 0x5a, 0x4b, 0x07, 0x58, 0x88, 0x7d, - 0xf1, 0xf2, 0x5e, 0x39, 0x11, 0xef, 0x21, 0x8b, 0xf7, 0x4b, 0x42, 0xbc, 0xfc, 0xc5, 0xd0, 0xb1, - 0x15, 0xef, 0xa1, 0x9d, 0x7d, 0xab, 0xff, 0x28, 0x07, 0x84, 0x35, 0xab, 0xa6, 0xbb, 0xee, 0x86, - 0xed, 0x18, 0xdc, 0x39, 0xfd, 0x48, 0x04, 0x73, 0x78, 0xf7, 0x95, 0xdf, 0x1a, 0x84, 0xd3, 0x11, - 0xc7, 0xdf, 0x63, 0x3e, 0x59, 0x5d, 0x8d, 0x8e, 0xa6, 0x5e, 0xaf, 0x5e, 0x9e, 0x93, 0x2f, 0x44, - 0xfb, 0x23, 0x0f, 0xde, 0xa4, 0x9b, 0xd0, 0x17, 0x61, 0x44, 0xfc, 0x60, 0x2b, 0xb4, 0x7f, 0xd3, - 0x85, 0xa3, 0xd4, 0x65, 0x00, 0x2d, 0x82, 0x26, 0xaf, 0xc0, 0x10, 0x1b, 0x30, 0x6b, 0x18, 0xac, - 0x64, 0x20, 0x7c, 0x51, 0x62, 0xf8, 0x40, 0x79, 0x3d, 0x09, 0x28, 0xa5, 0x77, 0x4b, 0x83, 0xbb, - 0x78, 0xb7, 0xf4, 0x79, 0x18, 0x2e, 0x5b, 0x96, 0xed, 0xe1, 0x26, 0xdd, 0x15, 0x57, 0x13, 0x99, - 0x56, 0xf9, 0xb3, 0xf8, 0x18, 0x3f, 0xa4, 0x4f, 0x35, 0xcb, 0x65, 0x86, 0xe4, 0xba, 0xff, 0x2a, - 0x86, 0x3a, 0xc2, 0xab, 0x1c, 0xaf, 0x67, 0x1c, 0x01, 0x4b, 0x3e, 0x8a, 0xc1, 0xce, 0x1b, 0xad, - 0x39, 0x76, 0xc7, 0x76, 0xa9, 0xc1, 0x05, 0x35, 0x1c, 0x86, 0x36, 0xe8, 0x08, 0x04, 0xbe, 0x9b, - 0x8b, 0x04, 0x0e, 0x89, 0x14, 0x21, 0xf7, 0xe1, 0x8c, 0x7f, 0x51, 0x1c, 0xbc, 0x50, 0xac, 0x56, - 0x5c, 0x65, 0x04, 0x5f, 0x25, 0x91, 0xb8, 0x32, 0x54, 0x2b, 0xd3, 0x4f, 0xf9, 0xd7, 0x22, 0xfe, - 0x13, 0xc7, 0x86, 0x69, 0xc8, 0x5d, 0x9d, 0xca, 0x8f, 0xfc, 0x20, 0x0c, 0x2f, 0xea, 0x8f, 0x2a, - 0x5d, 0x71, 0xf6, 0x32, 0xba, 0xfb, 0xdb, 0x97, 0xb6, 0xfe, 0xa8, 0x61, 0x88, 0x72, 0x31, 0x9b, - 0x42, 0x66, 0x49, 0x1a, 0x70, 0xae, 0xe6, 0xd8, 0x6d, 0xdb, 0xa3, 0x46, 0xec, 0xb1, 0xdf, 0x78, - 0xf8, 0x3a, 0xb8, 0x23, 0x28, 0x1a, 0x3d, 0x5e, 0xfd, 0x65, 0xb0, 0x21, 0x6d, 0x18, 0x2f, 0xbb, - 0x6e, 0xb7, 0x4d, 0xc3, 0x1b, 0xaa, 0x89, 0x1d, 0x3f, 0xe3, 0xe3, 0xc2, 0x6b, 0xf9, 0x49, 0x1d, - 0x8b, 0xf2, 0x0b, 0xaa, 0x86, 0x67, 0xca, 0x35, 0xe2, 0xb7, 0xc4, 0x79, 0xdf, 0x2a, 0x0e, 0x8e, - 0x4d, 0x8c, 0x6b, 0xe7, 0x93, 0x8d, 0x59, 0x31, 0xbd, 0x16, 0x55, 0xbf, 0x99, 0x03, 0x08, 0x05, - 0x4c, 0x5e, 0x8c, 0x46, 0x44, 0xca, 0x85, 0x17, 0x1d, 0x22, 0x5a, 0x42, 0x24, 0x04, 0x12, 0xb9, - 0x08, 0x45, 0x8c, 0xa8, 0x91, 0x0f, 0x0f, 0x56, 0x1f, 0x98, 0x96, 0xa1, 0x21, 0x94, 0x61, 0xa5, - 0xa7, 0xef, 0x88, 0xc5, 0x4b, 0x7d, 0x6e, 0x15, 0x56, 0x60, 0xbc, 0xde, 0x5d, 0xf5, 0xeb, 0x96, - 0xde, 0xf1, 0x61, 0x60, 0x0f, 0xb7, 0xbb, 0x1a, 0x3c, 0x7e, 0x8d, 0x84, 0x4d, 0x89, 0x16, 0x51, - 0xbf, 0x9e, 0x8b, 0xcd, 0x82, 0x47, 0xb8, 0xe8, 0x3d, 0x97, 0xf4, 0xd3, 0x48, 0x4e, 0x4b, 0xea, - 0x1f, 0x14, 0x60, 0xb8, 0x66, 0x3b, 0x9e, 0x08, 0x51, 0x72, 0xbc, 0x57, 0x21, 0x69, 0xaf, 0x54, - 0xdc, 0xc3, 0x5e, 0xe9, 0x22, 0x14, 0x25, 0x17, 0x65, 0x7e, 0x2f, 0x62, 0x18, 0x8e, 0x86, 0xd0, - 0x0f, 0xf9, 0xc9, 0x45, 0xf2, 0x12, 0x74, 0xe0, 0xc0, 0xae, 0x06, 0x3f, 0x9c, 0x07, 0xf8, 0xcc, - 0x4b, 0x2f, 0x3d, 0xc6, 0x5d, 0xaa, 0xfe, 0xc5, 0x1c, 0x8c, 0x8b, 0xab, 0x45, 0x29, 0x1a, 0xda, - 0x80, 0x7f, 0x29, 0x2c, 0xcf, 0x24, 0x1c, 0xa4, 0xf9, 0x38, 0xb6, 0x68, 0xcd, 0x3e, 0x32, 0x3d, - 0xbc, 0x5d, 0x91, 0xc2, 0xa1, 0x51, 0x01, 0x93, 0x17, 0x2d, 0x9f, 0x8e, 0xbc, 0xe8, 0x5f, 0x9a, - 0x16, 0xc2, 0x95, 0x9a, 0x15, 0x98, 0x4d, 0xbd, 0x38, 0x55, 0x7f, 0xad, 0x08, 0xc5, 0xd9, 0x47, - 0xb4, 0x79, 0xcc, 0xbb, 0x46, 0x3a, 0x8a, 0x2d, 0x1e, 0xf0, 0x28, 0x76, 0x3f, 0x5e, 0x20, 0xef, - 0x84, 0xfd, 0x59, 0x8a, 0x56, 0x1f, 0xeb, 0xf9, 0x78, 0xf5, 0x7e, 0x4f, 0x1f, 0x3f, 0x27, 0xa2, - 0xff, 0xb2, 0x00, 0x85, 0xfa, 0x4c, 0xed, 0x44, 0x6f, 0x8e, 0x54, 0x6f, 0x7a, 0xdf, 0xb2, 0xab, - 0xc1, 0xc5, 0xd9, 0x60, 0xe8, 0xd7, 0x1a, 0xbb, 0x23, 0xfb, 0xe3, 0x02, 0x8c, 0xd5, 0xe7, 0x56, - 0x6a, 0xd2, 0xd9, 0xf5, 0x6d, 0xee, 0x7b, 0x88, 0x5e, 0x70, 0xbc, 0x4b, 0x2f, 0x26, 0x2c, 0xb0, - 0x3b, 0x55, 0xcb, 0x7b, 0xf5, 0xc6, 0x5d, 0xbd, 0xd5, 0xa5, 0x78, 0x58, 0xc4, 0x3d, 0x95, 0x5d, - 0xf3, 0x03, 0xfa, 0x35, 0x0c, 0x8d, 0xe0, 0x33, 0x20, 0x6f, 0x42, 0xe1, 0x8e, 0xf0, 0x21, 0xc9, - 0xe2, 0xf3, 0xf2, 0x75, 0xce, 0x87, 0x4d, 0x82, 0x85, 0xae, 0x69, 0x20, 0x07, 0x56, 0x8a, 0x15, - 0xbe, 0x29, 0x4c, 0x86, 0x5d, 0x15, 0x5e, 0xf3, 0x0b, 0xdf, 0xac, 0x56, 0x48, 0x1d, 0x86, 0x6b, - 0xd4, 0x69, 0x9b, 0xd8, 0x51, 0xfe, 0x9c, 0xdd, 0x9b, 0x09, 0xdb, 0x5b, 0x0d, 0x77, 0xc2, 0x42, - 0xc8, 0x4c, 0xe6, 0x42, 0xde, 0x03, 0xe0, 0x56, 0xd5, 0x2e, 0x23, 0x6c, 0x5e, 0xc2, 0x9d, 0x0a, - 0x37, 0x86, 0x53, 0xac, 0x52, 0x89, 0x19, 0x79, 0x00, 0x13, 0x8b, 0xb6, 0x61, 0xde, 0x37, 0xb9, - 0xb3, 0x28, 0x56, 0x50, 0xda, 0xd9, 0x45, 0x8b, 0x19, 0xbf, 0x6d, 0xa9, 0x5c, 0x5a, 0x35, 0x09, - 0xc6, 0xea, 0xdf, 0xea, 0x87, 0x22, 0xeb, 0xf6, 0x93, 0xf1, 0x7b, 0x90, 0xf1, 0x5b, 0x86, 0x89, - 0x7b, 0xb6, 0xf3, 0xc0, 0xb4, 0xd6, 0x02, 0x3f, 0x7e, 0xb1, 0x9b, 0x46, 0xdf, 0xa3, 0x0d, 0x8e, - 0x6b, 0x04, 0x2e, 0xff, 0x5a, 0x82, 0x7c, 0x87, 0x11, 0xfc, 0x3a, 0x00, 0x7f, 0x9d, 0x8f, 0x34, - 0x83, 0x61, 0x38, 0x0f, 0xfe, 0x76, 0x1f, 0x9f, 0x06, 0xc8, 0xe1, 0x3c, 0x42, 0x62, 0x72, 0xd5, - 0xf7, 0xde, 0x18, 0xc2, 0x97, 0x02, 0x78, 0x6c, 0x80, 0xde, 0x1b, 0xb2, 0x11, 0xc0, 0xfd, 0x38, - 0x6a, 0x00, 0xd2, 0x8d, 0x18, 0xc4, 0x04, 0x11, 0x99, 0x1c, 0x44, 0x00, 0xbd, 0x94, 0x0b, 0x31, - 0x4d, 0xe2, 0x41, 0x5e, 0x8d, 0x5d, 0xd9, 0x93, 0x08, 0xb7, 0xcc, 0x1b, 0xfb, 0xd0, 0xe5, 0x6b, - 0x64, 0x27, 0x97, 0x2f, 0xf5, 0xaf, 0x17, 0x60, 0x98, 0x71, 0xab, 0x77, 0xdb, 0x6d, 0xdd, 0xd9, - 0x3c, 0x51, 0xe4, 0x83, 0x28, 0x72, 0x03, 0x4e, 0xc9, 0x2e, 0xfe, 0xcc, 0x74, 0xf5, 0x83, 0x31, - 0x05, 0x07, 0x56, 0x71, 0x02, 0x6e, 0x5b, 0xe2, 0xbc, 0xef, 0x09, 0x30, 0x9e, 0x86, 0xb8, 0x5a, - 0x92, 0x97, 0xfa, 0xd3, 0x39, 0x98, 0x88, 0x43, 0x03, 0xdd, 0xcf, 0xa5, 0xea, 0xfe, 0x0b, 0x30, - 0x24, 0x2e, 0xfd, 0x75, 0x43, 0xf8, 0x20, 0x8e, 0x6d, 0x6f, 0x4d, 0x02, 0xbe, 0xb8, 0x6e, 0x38, - 0x54, 0x37, 0xb4, 0x90, 0x80, 0xbc, 0x02, 0x23, 0xf8, 0xe3, 0x9e, 0x63, 0x7a, 0x1e, 0xe5, 0x9d, - 0x51, 0xe4, 0xf7, 0x18, 0xbc, 0xc0, 0x06, 0x47, 0x68, 0x11, 0x32, 0xf5, 0x77, 0xf2, 0x30, 0x54, - 0xef, 0xae, 0xba, 0x9b, 0xae, 0x47, 0xdb, 0xc7, 0x5c, 0x87, 0xfc, 0x63, 0x85, 0x62, 0xea, 0xb1, - 0xc2, 0xb3, 0xfe, 0xd0, 0x92, 0xce, 0xdb, 0x83, 0x8d, 0x81, 0xef, 0x47, 0x19, 0x6a, 0x51, 0x69, - 0xef, 0x5a, 0xa4, 0xfe, 0xcd, 0x3c, 0x4c, 0xf0, 0xeb, 0xe6, 0x8a, 0xe9, 0x36, 0x0f, 0xe1, 0x09, - 0xcc, 0xd1, 0xcb, 0xf4, 0x60, 0x2e, 0x1a, 0xbb, 0x78, 0x58, 0xa4, 0x7e, 0x21, 0x0f, 0xc3, 0xe5, - 0xae, 0xb7, 0x5e, 0xf6, 0x70, 0x7e, 0x7b, 0x2c, 0xf7, 0xc8, 0xff, 0x20, 0x07, 0xe3, 0xac, 0x21, - 0x2b, 0xf6, 0x03, 0x6a, 0x1d, 0xc2, 0x71, 0xbd, 0x7c, 0xec, 0x9e, 0xdf, 0xe7, 0xb1, 0xbb, 0x2f, - 0xcb, 0xc2, 0xde, 0x64, 0x89, 0x97, 0x4c, 0x9a, 0xdd, 0xa2, 0xc7, 0xfb, 0x33, 0x0e, 0xf1, 0x92, - 0xc9, 0x17, 0xc8, 0x21, 0x5c, 0x6a, 0x7e, 0x6f, 0x09, 0xe4, 0x10, 0x4e, 0x64, 0xbf, 0x37, 0x04, - 0xf2, 0xad, 0x1c, 0x0c, 0x4d, 0xdb, 0xde, 0x31, 0x1f, 0xf8, 0xe2, 0x2b, 0x8e, 0xb7, 0x9a, 0xfb, - 0x5f, 0x71, 0xbc, 0x75, 0x53, 0xfd, 0xd9, 0x3c, 0x9c, 0x11, 0x11, 0xfc, 0xc5, 0x19, 0xd8, 0xc9, - 0x74, 0x2c, 0x06, 0x5b, 0x52, 0x34, 0x27, 0xf3, 0x90, 0x10, 0xcd, 0x2f, 0x14, 0xe0, 0x0c, 0x06, - 0x1c, 0x66, 0x3b, 0xaa, 0xef, 0x01, 0x5b, 0x84, 0x34, 0xa3, 0xae, 0x03, 0x8b, 0x29, 0xae, 0x03, - 0x7f, 0xb2, 0x35, 0xf9, 0xea, 0x9a, 0xe9, 0xad, 0x77, 0x57, 0xa7, 0x9a, 0x76, 0xfb, 0xda, 0x9a, - 0xa3, 0x3f, 0x34, 0xf9, 0xa5, 0xb9, 0xde, 0xba, 0x16, 0x26, 0xd6, 0xe9, 0x98, 0x22, 0x4d, 0x4e, - 0x1d, 0x77, 0x4a, 0x8c, 0xab, 0xef, 0x74, 0xe0, 0x02, 0xdc, 0xb2, 0x4d, 0x4b, 0x78, 0xe2, 0x72, - 0x43, 0xb7, 0xbe, 0xbd, 0x35, 0x79, 0xf6, 0x7d, 0xdb, 0xb4, 0x1a, 0x71, 0x77, 0xdc, 0xbd, 0xd6, - 0x17, 0xb2, 0xd6, 0xa4, 0x6a, 0xd4, 0xff, 0x3a, 0x07, 0x4f, 0x44, 0xb5, 0xf8, 0x7b, 0xc1, 0x76, - 0xfc, 0x0b, 0x79, 0x38, 0x7b, 0x13, 0x85, 0x13, 0xb8, 0x3f, 0x9d, 0xcc, 0x5b, 0x62, 0x70, 0xa6, - 0xc8, 0xe6, 0xc4, 0xa2, 0xcc, 0x96, 0xcd, 0xc9, 0xa4, 0x2e, 0x64, 0xf3, 0x0f, 0x73, 0x70, 0x7a, - 0xb9, 0x5a, 0x99, 0xf9, 0x1e, 0x19, 0x51, 0xc9, 0xef, 0x39, 0xe6, 0x06, 0x67, 0xe2, 0x7b, 0x8e, - 0xb9, 0xe9, 0xf9, 0x95, 0x3c, 0x9c, 0xae, 0x97, 0x17, 0x17, 0xbe, 0x57, 0x66, 0xf0, 0x19, 0xd9, - 0x57, 0xd7, 0x3f, 0x04, 0x13, 0xb6, 0x80, 0xfc, 0x99, 0x77, 0xaf, 0x67, 0xfb, 0xf0, 0x26, 0x85, - 0x72, 0xcc, 0xa7, 0xee, 0x43, 0x11, 0x0a, 0xd3, 0xfc, 0x08, 0xf5, 0x31, 0xd7, 0xfc, 0xbf, 0x57, - 0x82, 0xe1, 0xdb, 0xdd, 0x55, 0x2a, 0x5c, 0xba, 0x1e, 0xeb, 0x93, 0xdf, 0xeb, 0x30, 0x2c, 0xc4, - 0x80, 0x37, 0x1c, 0x52, 0xc8, 0x49, 0x11, 0x42, 0x88, 0x47, 0xf5, 0x92, 0x89, 0xc8, 0x45, 0x28, - 0xde, 0xa5, 0xce, 0xaa, 0xfc, 0x1a, 0xfb, 0x21, 0x75, 0x56, 0x35, 0x84, 0x92, 0x85, 0xf0, 0xa1, - 0x49, 0xb9, 0x56, 0xc5, 0x74, 0x47, 0xe2, 0xd2, 0x10, 0xf3, 0x37, 0x05, 0xde, 0xa2, 0x7a, 0xc7, - 0xe4, 0x89, 0x92, 0xe4, 0x48, 0x10, 0xf1, 0x92, 0x64, 0x09, 0x4e, 0xc9, 0xee, 0x82, 0x3c, 0xd7, - 0xcf, 0x60, 0x0a, 0xbb, 0xb4, 0x2c, 0x3f, 0xc9, 0xa2, 0xe4, 0x1d, 0x18, 0xf1, 0x81, 0xe8, 0xf8, - 0x38, 0x14, 0x26, 0x98, 0x08, 0x58, 0xc5, 0xf2, 0x01, 0x44, 0x0a, 0xc8, 0x0c, 0xf0, 0x12, 0x03, - 0x52, 0x18, 0xc4, 0x1c, 0x49, 0x23, 0x05, 0xc8, 0x2b, 0xc8, 0x00, 0x1f, 0x47, 0xa1, 0xc3, 0xd4, - 0x30, 0x3e, 0x55, 0xc6, 0x0b, 0x20, 0x47, 0xc0, 0xf9, 0x83, 0xf4, 0x08, 0x19, 0x59, 0x06, 0x08, - 0x1d, 0x5b, 0x44, 0xd8, 0x8f, 0x3d, 0xbb, 0xdc, 0x48, 0x2c, 0xe4, 0x9b, 0xbc, 0xd1, 0xfd, 0xdc, - 0xe4, 0xa9, 0x3f, 0x53, 0x80, 0xe1, 0x72, 0xa7, 0x13, 0x0c, 0x85, 0x17, 0xa1, 0x54, 0xee, 0x74, - 0xee, 0x68, 0x55, 0x39, 0x01, 0x80, 0xde, 0xe9, 0x34, 0xba, 0x8e, 0x29, 0x7b, 0x52, 0x73, 0x22, - 0x32, 0x03, 0xa3, 0xe5, 0x4e, 0xa7, 0xd6, 0x5d, 0x6d, 0x99, 0x4d, 0x29, 0x7f, 0x19, 0xcf, 0xf0, - 0xd8, 0xe9, 0x34, 0x3a, 0x88, 0x89, 0x27, 0xb1, 0x8b, 0x96, 0x21, 0x9f, 0xc7, 0x60, 0x59, 0x22, - 0x7d, 0x16, 0x4f, 0xd0, 0xa3, 0x06, 0xa1, 0xff, 0xc3, 0xb6, 0x4d, 0x05, 0x44, 0x3c, 0x45, 0xc2, - 0x45, 0x3f, 0xb1, 0x05, 0xab, 0x28, 0x91, 0x26, 0x2b, 0x64, 0x49, 0x3e, 0x09, 0x03, 0xe5, 0x4e, - 0x47, 0xba, 0xad, 0x42, 0xc7, 0x36, 0x56, 0x2a, 0x9e, 0xa1, 0x50, 0x90, 0x89, 0xcf, 0x12, 0xf7, - 0xdb, 0xb6, 0xe3, 0xe1, 0x90, 0x1a, 0x0d, 0x3f, 0xcb, 0xbf, 0x10, 0xb7, 0xe5, 0xf8, 0x34, 0x5a, - 0xb4, 0xcc, 0x85, 0xb7, 0x60, 0x2c, 0xda, 0xe2, 0x3d, 0xe5, 0x69, 0xf8, 0x6e, 0x0e, 0xa5, 0x72, - 0xcc, 0x9f, 0x13, 0xbc, 0x0c, 0x85, 0x72, 0xa7, 0x23, 0x26, 0xb5, 0xd3, 0x29, 0x9d, 0x1a, 0x8f, - 0x3e, 0x50, 0xee, 0x74, 0xfc, 0x4f, 0x3f, 0xe6, 0xef, 0x92, 0xf6, 0xf5, 0xe9, 0xdf, 0xe2, 0x9f, - 0x7e, 0xbc, 0xdf, 0x0c, 0xa9, 0xbf, 0x56, 0x80, 0xf1, 0x72, 0xa7, 0x73, 0x92, 0xdf, 0xe1, 0xb0, - 0x62, 0x1c, 0xbc, 0x04, 0x20, 0xcd, 0xb1, 0x03, 0xc1, 0xab, 0xc9, 0x61, 0x69, 0x7e, 0x55, 0x72, - 0x9a, 0x44, 0xe4, 0xab, 0xdf, 0xe0, 0x9e, 0xd4, 0xef, 0x0b, 0x05, 0x9c, 0xf8, 0x8e, 0x7b, 0xbc, - 0xb6, 0x8f, 0x4a, 0xb7, 0x89, 0x3e, 0x28, 0xed, 0xa9, 0x0f, 0xfe, 0x4e, 0x64, 0xf0, 0x60, 0xbe, - 0x80, 0x93, 0x5e, 0xe8, 0x3f, 0x90, 0x6d, 0x3d, 0x26, 0x0b, 0x53, 0x04, 0x91, 0xf2, 0x73, 0xa6, - 0x89, 0x90, 0x66, 0x4d, 0x86, 0x6a, 0x98, 0x86, 0x16, 0xa3, 0xf5, 0xfb, 0x70, 0x60, 0x4f, 0x7d, - 0xb8, 0x95, 0xc7, 0xb0, 0x05, 0x41, 0x48, 0xb4, 0x83, 0x6f, 0x51, 0xae, 0x01, 0x70, 0xf7, 0x85, - 0xc0, 0x3f, 0x7f, 0x94, 0x47, 0x3f, 0xe2, 0xa9, 0xd4, 0x44, 0xf4, 0xa3, 0x90, 0x24, 0x70, 0x77, - 0x2a, 0xa4, 0xba, 0x3b, 0x5d, 0x85, 0x41, 0x4d, 0xdf, 0x78, 0xb7, 0x4b, 0x9d, 0x4d, 0x61, 0x13, - 0xf1, 0x88, 0xa3, 0xfa, 0x46, 0xe3, 0x87, 0x18, 0x50, 0x0b, 0xd0, 0x44, 0x0d, 0xe2, 0x5e, 0x48, - 0x6e, 0x25, 0xfc, 0xa0, 0x3d, 0x88, 0x76, 0xb1, 0x1f, 0x45, 0x27, 0x6f, 0x40, 0xa1, 0x7c, 0xaf, - 0x2e, 0x24, 0x1b, 0x74, 0x6d, 0xf9, 0x5e, 0x5d, 0xc8, 0x2b, 0xb3, 0xec, 0xbd, 0xba, 0xfa, 0x85, - 0x3c, 0x90, 0x24, 0x25, 0x79, 0x15, 0x86, 0x10, 0xba, 0xc6, 0x74, 0x46, 0xce, 0xc1, 0xbb, 0xe1, - 0x36, 0x1c, 0x84, 0x46, 0x2c, 0x44, 0x9f, 0x94, 0xbc, 0x8e, 0x59, 0xce, 0x45, 0x16, 0xc8, 0x48, - 0x0e, 0xde, 0x0d, 0xd7, 0xcf, 0x0b, 0x1e, 0x4b, 0x72, 0x2e, 0x88, 0xd1, 0xb8, 0xbc, 0x57, 0x9f, - 0xb7, 0x5d, 0x4f, 0x88, 0x9a, 0x1b, 0x97, 0x1b, 0x2e, 0x26, 0x7f, 0x8e, 0x18, 0x97, 0x9c, 0x0c, - 0x13, 0xd8, 0xdd, 0xab, 0xf3, 0x17, 0x62, 0x86, 0x66, 0xb7, 0x7c, 0xab, 0x94, 0x27, 0xb0, 0xdb, - 0x70, 0x1b, 0xfc, 0x75, 0x99, 0x81, 0xe9, 0xd5, 0x23, 0x09, 0xec, 0x22, 0xa5, 0xd4, 0x9f, 0x1c, - 0x84, 0x89, 0x8a, 0xee, 0xe9, 0xab, 0xba, 0x4b, 0xa5, 0x2d, 0xf9, 0xb8, 0x0f, 0xf3, 0x3f, 0x47, - 0x92, 0x83, 0xb1, 0x9a, 0xf2, 0x35, 0xf1, 0x02, 0xe4, 0xcd, 0x90, 0x6f, 0x90, 0x5e, 0x58, 0xce, - 0x57, 0xb8, 0xda, 0xe8, 0x08, 0xb0, 0x96, 0x20, 0x24, 0x2f, 0xc0, 0xb0, 0x0f, 0x63, 0xbb, 0x88, - 0x42, 0xa8, 0x33, 0xc6, 0x2a, 0xdb, 0x44, 0x68, 0x32, 0x9a, 0xbc, 0x0e, 0x23, 0xfe, 0x4f, 0xc9, - 0x3e, 0xe7, 0xc9, 0x17, 0x57, 0x13, 0x5b, 0x30, 0x99, 0x54, 0x2e, 0x8a, 0xf3, 0x5b, 0x7f, 0xa4, - 0x68, 0x2c, 0xbf, 0x61, 0x84, 0x94, 0xfc, 0x10, 0x8c, 0xf9, 0xbf, 0xc5, 0xae, 0x83, 0x7b, 0x1f, - 0xbe, 0x10, 0x64, 0x6f, 0x8f, 0x89, 0x75, 0x2a, 0x4a, 0xce, 0xf7, 0x1f, 0x4f, 0xfa, 0x29, 0xfb, - 0x8c, 0xd5, 0xe4, 0xf6, 0x23, 0x56, 0x01, 0xa9, 0xc2, 0x29, 0x1f, 0x12, 0x6a, 0xe8, 0x40, 0xb8, - 0xed, 0x34, 0x56, 0x1b, 0xa9, 0x4a, 0x9a, 0x2c, 0x45, 0x5a, 0x70, 0x31, 0x02, 0x34, 0xdc, 0x75, - 0xf3, 0xbe, 0x27, 0xf6, 0x8c, 0x22, 0xfc, 0xb7, 0xc8, 0xd1, 0x1a, 0x70, 0xe5, 0x34, 0x7e, 0xb2, - 0xe5, 0x68, 0x62, 0xb6, 0x9e, 0xdc, 0x48, 0x1d, 0xce, 0xf8, 0xf8, 0x9b, 0x33, 0xb5, 0x9a, 0x63, - 0xbf, 0x4f, 0x9b, 0x5e, 0xb5, 0x22, 0xf6, 0xdc, 0x18, 0x16, 0xd2, 0x58, 0x6d, 0xac, 0x35, 0x3b, - 0x4c, 0x29, 0x18, 0x2e, 0xca, 0x3c, 0xb5, 0x30, 0xb9, 0x0b, 0x67, 0x25, 0xb8, 0x94, 0x09, 0x1e, - 0xc2, 0x43, 0x01, 0xc1, 0x35, 0x3d, 0x19, 0x7c, 0x7a, 0x71, 0xf2, 0x16, 0x8c, 0xfa, 0x08, 0x7e, - 0x15, 0x39, 0x8c, 0x57, 0x91, 0x38, 0x24, 0x8d, 0xd5, 0x46, 0xfc, 0x21, 0x73, 0x94, 0x58, 0xd6, - 0xa8, 0x95, 0xcd, 0x0e, 0x15, 0x6e, 0xc1, 0xbe, 0x46, 0x79, 0x9b, 0x9d, 0x54, 0x65, 0x64, 0xa4, - 0xe4, 0x9d, 0x50, 0xa3, 0x96, 0x1d, 0x73, 0xcd, 0xe4, 0xdb, 0x71, 0xff, 0xed, 0xf2, 0x6a, 0xc3, - 0x46, 0x60, 0x9a, 0x7e, 0x70, 0xf2, 0x0b, 0x65, 0x38, 0x9d, 0xa2, 0x63, 0x7b, 0xda, 0x31, 0x7e, - 0x31, 0x1f, 0x36, 0xe2, 0x98, 0x6f, 0x1b, 0xa7, 0x61, 0xd0, 0xff, 0x12, 0x61, 0x3c, 0x28, 0x59, - 0x43, 0x33, 0xce, 0xc3, 0xc7, 0x47, 0xc4, 0x71, 0xcc, 0xb7, 0x92, 0x87, 0x21, 0x8e, 0x6f, 0xe7, - 0x42, 0x71, 0x1c, 0xf3, 0xed, 0xe5, 0x4f, 0x14, 0xc3, 0x39, 0xe9, 0x64, 0x8f, 0x79, 0x58, 0x66, - 0x72, 0xe8, 0x4c, 0x5b, 0xda, 0xc3, 0x1b, 0x62, 0x59, 0x35, 0x07, 0xf6, 0xa7, 0x9a, 0xe4, 0x2d, - 0x18, 0xae, 0xd9, 0xae, 0xb7, 0xe6, 0x50, 0xb7, 0x16, 0xa4, 0xaf, 0xc0, 0xf7, 0xe7, 0x1d, 0x01, - 0x6e, 0x74, 0x22, 0xb3, 0xbf, 0x4c, 0xae, 0xfe, 0xe3, 0x42, 0x42, 0x1b, 0xb8, 0xe1, 0x7a, 0x2c, - 0xb5, 0xe1, 0x10, 0x86, 0x3a, 0xb9, 0x1e, 0xae, 0x82, 0xdc, 0xc2, 0xef, 0x97, 0x62, 0x73, 0xae, - 0x0a, 0x03, 0x3f, 0x4a, 0x42, 0xbe, 0x1f, 0xce, 0x47, 0x00, 0x35, 0xdd, 0xd1, 0xdb, 0xd4, 0x0b, - 0x53, 0x85, 0x62, 0xb4, 0x35, 0xbf, 0x74, 0xa3, 0x13, 0xa0, 0xe5, 0xf4, 0xa3, 0x19, 0x1c, 0x24, - 0xd5, 0x1a, 0xd8, 0x83, 0x9f, 0xf6, 0x57, 0x0b, 0xa1, 0xa1, 0x13, 0x8d, 0x9a, 0xac, 0x51, 0xb7, - 0xdb, 0xf2, 0x1e, 0xdf, 0x0e, 0xde, 0x5f, 0x4e, 0x9a, 0x79, 0x18, 0x2f, 0xdf, 0xbf, 0x4f, 0x9b, - 0x9e, 0x1f, 0x0c, 0xde, 0x15, 0x71, 0x32, 0xf9, 0xc6, 0x43, 0xa0, 0x44, 0x70, 0x6f, 0xb9, 0x5f, - 0xe3, 0xc5, 0xd4, 0x7f, 0x52, 0x04, 0x25, 0x30, 0xfc, 0x83, 0xf7, 0x8a, 0x47, 0xb8, 0xc8, 0x7e, - 0x24, 0x7a, 0xc5, 0x84, 0x53, 0xa1, 0x30, 0xc4, 0x43, 0x31, 0x91, 0xbc, 0x7e, 0x32, 0xce, 0x2c, - 0x24, 0xe4, 0x7b, 0x89, 0x0b, 0x62, 0x2f, 0x41, 0xc2, 0xf7, 0xa0, 0x0d, 0x97, 0xb3, 0xd0, 0x92, - 0x5c, 0xc9, 0x97, 0x72, 0x70, 0xc6, 0xef, 0x94, 0xe5, 0x55, 0x66, 0x54, 0xcf, 0xd8, 0x5d, 0x2b, - 0x78, 0x45, 0xf5, 0x46, 0x76, 0x75, 0xbc, 0x93, 0xa6, 0xd2, 0x0a, 0xf3, 0x96, 0x04, 0x11, 0x61, - 0x02, 0x85, 0xb0, 0x91, 0xa6, 0xd1, 0x44, 0x22, 0x2d, 0xb5, 0xde, 0x0b, 0x37, 0xe1, 0x89, 0x4c, - 0x96, 0x3b, 0x19, 0xb1, 0xfd, 0xb2, 0x11, 0xfb, 0xdf, 0xe6, 0xc2, 0x89, 0x28, 0x26, 0x24, 0x32, - 0x05, 0x10, 0x82, 0xc4, 0xb6, 0x16, 0x1f, 0x69, 0x85, 0x42, 0xd3, 0x24, 0x0a, 0xb2, 0x0c, 0x25, - 0x21, 0x16, 0x9e, 0x96, 0xfb, 0x13, 0x3b, 0xf4, 0xc2, 0x94, 0x2c, 0x07, 0xdc, 0xb2, 0x8a, 0x6f, - 0x16, 0x6c, 0x2e, 0xbc, 0x0e, 0xc3, 0xfb, 0xfd, 0xae, 0x2f, 0x15, 0x80, 0xc8, 0x7b, 0xd0, 0x23, - 0x34, 0xd0, 0x8f, 0xf1, 0x14, 0x76, 0x05, 0x06, 0xd9, 0x27, 0x60, 0xa2, 0x1a, 0x29, 0x30, 0x75, - 0x57, 0xc0, 0xb4, 0x00, 0x1b, 0x46, 0x85, 0x1b, 0x48, 0x8f, 0x0a, 0xa7, 0xfe, 0x74, 0x01, 0xce, - 0xc9, 0x1d, 0x52, 0xa1, 0x98, 0xeb, 0xe2, 0xa4, 0x53, 0x3e, 0xc4, 0x4e, 0x51, 0xa1, 0xc4, 0xb7, - 0x1e, 0x22, 0xe9, 0x08, 0x3f, 0x16, 0x42, 0x88, 0x26, 0x30, 0xea, 0xff, 0x9c, 0x87, 0xd1, 0xc0, - 0xbc, 0xd3, 0x1d, 0xf7, 0x31, 0xee, 0x8e, 0x4f, 0xc1, 0x28, 0xc6, 0xf5, 0x6a, 0x53, 0x8b, 0xc7, - 0xbe, 0xea, 0x97, 0xb2, 0x04, 0xf9, 0x08, 0x91, 0x10, 0x2e, 0x42, 0xc8, 0xb4, 0x9f, 0x5b, 0x7e, - 0x52, 0xb4, 0x35, 0x6e, 0xf6, 0x71, 0xb8, 0xfa, 0x97, 0x0b, 0x30, 0xe2, 0x4b, 0x79, 0xda, 0x3c, - 0xae, 0xf7, 0x3c, 0x47, 0x2b, 0xe4, 0x6b, 0x00, 0x35, 0xdb, 0xf1, 0xf4, 0xd6, 0x52, 0xa8, 0xf9, - 0x78, 0x40, 0xda, 0x41, 0x28, 0x2f, 0x23, 0x91, 0xe0, 0xfa, 0x15, 0x9a, 0xd5, 0x7c, 0x62, 0xe2, - 0xeb, 0x57, 0x00, 0xd5, 0x24, 0x0a, 0xf5, 0xb7, 0xf2, 0x30, 0xee, 0x77, 0xd2, 0xec, 0x23, 0xda, - 0xec, 0x3e, 0xce, 0x73, 0x53, 0x54, 0xda, 0xfd, 0x3b, 0x4a, 0x5b, 0xfd, 0xbf, 0xa4, 0x89, 0x64, - 0xa6, 0x65, 0x9f, 0x4c, 0x24, 0xff, 0x3a, 0x74, 0x5c, 0xfd, 0x91, 0x02, 0x9c, 0xf1, 0xa5, 0x3e, - 0xd7, 0xb5, 0xf0, 0x68, 0x61, 0x46, 0x6f, 0xb5, 0x1e, 0xe7, 0xdd, 0xf8, 0xb0, 0x2f, 0x88, 0x65, - 0x11, 0x28, 0x53, 0x24, 0xe7, 0xbc, 0x2f, 0xc0, 0x0d, 0xdb, 0x34, 0x34, 0x99, 0x88, 0xbc, 0x03, - 0x23, 0xfe, 0xcf, 0xb2, 0xb3, 0xe6, 0x6f, 0xc1, 0xf1, 0xa2, 0x20, 0x28, 0xa4, 0x3b, 0x91, 0xe8, - 0x1a, 0x91, 0x02, 0xea, 0x17, 0x06, 0xe0, 0xc2, 0x3d, 0xd3, 0x32, 0xec, 0x0d, 0xd7, 0xcf, 0xed, - 0x7a, 0xec, 0x0f, 0xca, 0x8e, 0x3a, 0xa7, 0xeb, 0xbb, 0x70, 0x36, 0x2e, 0x52, 0x27, 0x88, 0xb8, - 0x2f, 0x7a, 0x67, 0x83, 0x13, 0x34, 0xfc, 0x2c, 0xaf, 0xe2, 0xb6, 0x4d, 0x4b, 0x2f, 0x19, 0x4f, - 0x13, 0x3b, 0xb0, 0x9b, 0x34, 0xb1, 0xcf, 0x43, 0xa9, 0x62, 0xb7, 0x75, 0xd3, 0x8f, 0xb3, 0x84, - 0xa3, 0x38, 0xa8, 0x17, 0x31, 0x9a, 0xa0, 0x60, 0xfc, 0x45, 0xc5, 0xd8, 0x65, 0x43, 0x21, 0x7f, - 0xbf, 0x00, 0xb3, 0xd2, 0x34, 0x99, 0x88, 0xd8, 0x30, 0x2a, 0xaa, 0x13, 0x77, 0x63, 0x80, 0x9b, - 0xa7, 0x57, 0x7c, 0x19, 0x65, 0xab, 0xd5, 0x54, 0xa4, 0x1c, 0xdf, 0x46, 0xf1, 0xec, 0xb5, 0xe2, - 0x63, 0xf8, 0x2d, 0x99, 0x16, 0xe5, 0x2f, 0x09, 0x01, 0x27, 0x99, 0xe1, 0xa4, 0x10, 0x70, 0x96, - 0x91, 0x89, 0xc8, 0x2c, 0x9c, 0xc2, 0xb8, 0xe8, 0xc1, 0x56, 0x8a, 0xa9, 0xc4, 0x08, 0x1a, 0x95, - 0x78, 0xe5, 0xc2, 0x43, 0xa9, 0xb3, 0x8f, 0x6b, 0x34, 0x05, 0x5a, 0x4b, 0x96, 0x20, 0x4f, 0x40, - 0x61, 0x69, 0xa1, 0x8c, 0x77, 0x35, 0x83, 0x3c, 0x27, 0x99, 0xd5, 0xd2, 0x35, 0x06, 0xbb, 0xf0, - 0x69, 0x20, 0xc9, 0xcf, 0xd9, 0xd3, 0x7d, 0xcc, 0xdf, 0x97, 0xb6, 0x7c, 0xc7, 0xdd, 0xa3, 0xe6, - 0x30, 0x26, 0xc2, 0x48, 0x3a, 0xc0, 0xfe, 0x0f, 0x33, 0x1d, 0x60, 0xe9, 0x50, 0xd3, 0x01, 0xaa, - 0xbf, 0x9c, 0x83, 0x53, 0x89, 0xdc, 0x01, 0xe4, 0x65, 0x00, 0x0e, 0x91, 0x62, 0xb4, 0x62, 0x08, - 0xa1, 0x30, 0x9f, 0x80, 0x58, 0x1e, 0x43, 0x32, 0x72, 0x0d, 0x06, 0xf9, 0x2f, 0x11, 0xa5, 0x2c, - 0x59, 0xa4, 0xdb, 0x35, 0x0d, 0x2d, 0x20, 0x0a, 0x6b, 0xc1, 0x1b, 0xc9, 0x42, 0x6a, 0x11, 0x6f, - 0xb3, 0x13, 0xd4, 0xc2, 0xc8, 0xd4, 0x9f, 0xcc, 0xc3, 0x48, 0xd0, 0xe0, 0xb2, 0x71, 0x54, 0x3a, - 0x57, 0x12, 0x69, 0x18, 0x0a, 0x3b, 0xa5, 0x61, 0x88, 0xcd, 0xb7, 0x22, 0xef, 0xc2, 0xe1, 0xbd, - 0xca, 0xfa, 0x72, 0x1e, 0xc6, 0x83, 0x5a, 0x8f, 0xf0, 0xf2, 0xeb, 0x23, 0x24, 0x92, 0x2f, 0xe5, - 0x40, 0x99, 0x36, 0x5b, 0x2d, 0xd3, 0x5a, 0xab, 0x5a, 0xf7, 0x6d, 0xa7, 0x8d, 0x13, 0xe2, 0xd1, - 0x1d, 0xe1, 0xaa, 0x7f, 0x36, 0x07, 0xa7, 0x44, 0x83, 0x66, 0x74, 0xc7, 0x38, 0xba, 0xf3, 0xb1, - 0x78, 0x4b, 0x8e, 0x4e, 0x5f, 0xd4, 0x6f, 0xe4, 0x01, 0x16, 0xec, 0xe6, 0x83, 0x63, 0xfe, 0xa8, - 0xeb, 0x4d, 0x28, 0x71, 0xb7, 0x78, 0xa1, 0xb1, 0xa7, 0xc4, 0xe3, 0x25, 0xf6, 0x69, 0x1c, 0x31, - 0x3d, 0x21, 0xe6, 0xe3, 0x12, 0xf7, 0xac, 0x57, 0x72, 0x9a, 0x28, 0xc2, 0x2a, 0x65, 0x74, 0x62, - 0xc1, 0x08, 0x2a, 0x65, 0xb0, 0x68, 0xa5, 0xdb, 0x5b, 0x93, 0xc5, 0x96, 0xdd, 0x7c, 0xa0, 0x21, - 0xbd, 0xfa, 0x2f, 0x73, 0x5c, 0x76, 0xc7, 0xfc, 0x69, 0xaa, 0xff, 0xf9, 0xc5, 0x3d, 0x7e, 0xfe, - 0x9f, 0xcb, 0xc1, 0x19, 0x8d, 0x36, 0xed, 0x87, 0xd4, 0xd9, 0x9c, 0xb1, 0x0d, 0x7a, 0x93, 0x5a, - 0xd4, 0x39, 0xaa, 0x11, 0xf5, 0xdb, 0x98, 0xb7, 0x26, 0x6c, 0xcc, 0x1d, 0x97, 0x1a, 0xc7, 0x27, - 0xa7, 0x90, 0xfa, 0xab, 0x03, 0xa0, 0xa4, 0x5a, 0xbd, 0xc7, 0xd6, 0x9c, 0xcb, 0xdc, 0xca, 0x14, - 0x0f, 0x6b, 0x2b, 0xd3, 0xbf, 0xb7, 0xad, 0x4c, 0x69, 0xaf, 0x5b, 0x99, 0x81, 0xdd, 0x6c, 0x65, - 0xda, 0xf1, 0xad, 0xcc, 0x20, 0x6e, 0x65, 0x5e, 0xee, 0xb9, 0x95, 0x99, 0xb5, 0x8c, 0x7d, 0x6e, - 0x64, 0x8e, 0x6d, 0xbe, 0xeb, 0xfd, 0xec, 0xc0, 0xae, 0xb0, 0x49, 0xb1, 0x69, 0x3b, 0x06, 0x35, - 0xc4, 0xc6, 0x0b, 0x4f, 0xfd, 0x1d, 0x01, 0xd3, 0x02, 0x6c, 0x22, 0x79, 0xf8, 0xe8, 0x6e, 0x92, - 0x87, 0x1f, 0xc2, 0xfe, 0xeb, 0x8b, 0x79, 0x38, 0x35, 0x43, 0x1d, 0x8f, 0xc7, 0xa2, 0x3d, 0x0c, - 0x97, 0xb8, 0x32, 0x8c, 0x4b, 0x0c, 0xd1, 0x22, 0xcf, 0x87, 0x6e, 0x7e, 0x4d, 0xea, 0x78, 0x71, - 0x2f, 0xc1, 0x38, 0x3d, 0xab, 0xde, 0x4f, 0xe0, 0x27, 0xc6, 0x6e, 0x50, 0xbd, 0x0f, 0xe7, 0x82, - 0x34, 0xc5, 0x2f, 0x2d, 0xa0, 0x97, 0x72, 0xf2, 0x15, 0xf7, 0x9e, 0x93, 0x4f, 0xfd, 0xa5, 0x1c, - 0x5c, 0xd6, 0xa8, 0x45, 0x37, 0xf4, 0xd5, 0x16, 0x95, 0x9a, 0x25, 0x56, 0x06, 0x36, 0x6b, 0x98, - 0x6e, 0x5b, 0xf7, 0x9a, 0xeb, 0x07, 0x92, 0xd1, 0x1c, 0x8c, 0xc8, 0xf3, 0xd7, 0x1e, 0xe6, 0xb6, - 0x48, 0x39, 0xf5, 0x57, 0x8b, 0x30, 0x30, 0x6d, 0x7b, 0xb7, 0xec, 0x03, 0x26, 0x89, 0x0c, 0xa7, - 0xfc, 0xfc, 0x1e, 0xce, 0x7a, 0x3e, 0x89, 0x95, 0x4b, 0x79, 0x33, 0xd0, 0x85, 0x74, 0xd5, 0x4e, - 0xe4, 0x17, 0xf1, 0xc9, 0xf6, 0x98, 0x1e, 0xf2, 0x55, 0x18, 0xc2, 0x10, 0x32, 0xd2, 0x69, 0x2c, - 0x3a, 0x68, 0x7b, 0x0c, 0x18, 0xaf, 0x23, 0x24, 0x25, 0xdf, 0x1f, 0x09, 0x9e, 0x5b, 0x3a, 0x78, - 0x3a, 0x49, 0x39, 0x8e, 0xee, 0xcb, 0xfc, 0x22, 0x0f, 0xdb, 0x24, 0xa5, 0xde, 0xc1, 0x53, 0x94, - 0x58, 0x93, 0x02, 0xc2, 0x43, 0x4c, 0xf5, 0x38, 0x03, 0xa3, 0xd3, 0xb6, 0x27, 0x39, 0x03, 0x0f, - 0x85, 0x6f, 0x49, 0x99, 0xe4, 0xd3, 0x3d, 0x81, 0xa3, 0x65, 0xd4, 0x3f, 0x2e, 0xc2, 0x88, 0xff, - 0xf3, 0x88, 0x74, 0xe7, 0x45, 0x28, 0xcd, 0xdb, 0x52, 0xf6, 0x11, 0x74, 0x20, 0x5e, 0xb7, 0xdd, - 0x98, 0x67, 0xb4, 0x20, 0x62, 0x52, 0x5f, 0xb2, 0x0d, 0xd9, 0xfd, 0x1d, 0xa5, 0x6e, 0xd9, 0x46, - 0xe2, 0x0d, 0x72, 0x40, 0x48, 0x2e, 0x43, 0x11, 0x5f, 0x0e, 0x48, 0x07, 0xf9, 0xb1, 0xd7, 0x02, - 0x88, 0x97, 0xb4, 0xb2, 0xb4, 0x57, 0xad, 0x1c, 0xd8, 0xaf, 0x56, 0x0e, 0x1e, 0xae, 0x56, 0xbe, - 0x07, 0x23, 0x58, 0x93, 0x9f, 0xbc, 0x70, 0xe7, 0x85, 0xf5, 0x09, 0xb1, 0xf6, 0x8d, 0xf2, 0x76, - 0x8b, 0x14, 0x86, 0xb8, 0xe4, 0x45, 0x58, 0xc5, 0x74, 0x17, 0x0e, 0xb0, 0x9d, 0xfe, 0xc7, 0x39, - 0x18, 0xb8, 0x63, 0x3d, 0xb0, 0xec, 0x8d, 0x83, 0x69, 0xdc, 0xcb, 0x30, 0x2c, 0xd8, 0x48, 0xab, - 0x0b, 0x3e, 0x2b, 0xef, 0x72, 0x70, 0x03, 0x39, 0x69, 0x32, 0x15, 0x79, 0x2b, 0x28, 0x84, 0x8f, - 0x83, 0x0a, 0x61, 0xfe, 0x1e, 0xbf, 0x50, 0x33, 0x9a, 0xc0, 0x43, 0x26, 0x27, 0x17, 0xa1, 0x58, - 0x61, 0x4d, 0x95, 0x02, 0xf9, 0xb2, 0xa6, 0x68, 0x08, 0x55, 0xbf, 0x58, 0x84, 0xb1, 0xd8, 0xc1, - 0xd7, 0xf3, 0x30, 0x24, 0x0e, 0x9e, 0x4c, 0x3f, 0xa3, 0x08, 0x3e, 0x1e, 0x0a, 0x80, 0xda, 0x20, - 0xff, 0xb3, 0x6a, 0x90, 0xef, 0x83, 0x01, 0xdb, 0xc5, 0x45, 0x11, 0xbf, 0x65, 0x2c, 0x1c, 0x42, - 0xcb, 0x75, 0xd6, 0x76, 0x3e, 0x38, 0x04, 0x89, 0xac, 0x91, 0xb6, 0x8b, 0x9f, 0x76, 0x03, 0x86, - 0x74, 0xd7, 0xa5, 0x5e, 0xc3, 0xd3, 0xd7, 0xe4, 0x24, 0x23, 0x01, 0x50, 0x1e, 0x1d, 0x08, 0x5c, - 0xd1, 0xd7, 0xc8, 0xa7, 0x61, 0xb4, 0xe9, 0x50, 0x5c, 0x36, 0xf5, 0x16, 0x6b, 0xa5, 0x64, 0xd6, - 0x46, 0x10, 0xf2, 0xfd, 0x49, 0x88, 0xa8, 0x1a, 0xe4, 0x2e, 0x8c, 0x8a, 0xcf, 0xe1, 0x9e, 0xfb, - 0x38, 0xd0, 0xc6, 0xc2, 0x65, 0x8c, 0x8b, 0x84, 0xfb, 0xee, 0x8b, 0x07, 0x1c, 0x32, 0xb9, 0xcc, - 0xd7, 0x90, 0x48, 0xc9, 0x32, 0x90, 0x0d, 0xba, 0xda, 0xd0, 0xbb, 0xde, 0x3a, 0xab, 0x8b, 0xc7, - 0xc8, 0x17, 0xd9, 0x40, 0xf1, 0xd5, 0x43, 0x12, 0x2b, 0x3f, 0x06, 0xd9, 0xa0, 0xab, 0xe5, 0x08, - 0x92, 0xdc, 0x83, 0xb3, 0xc9, 0x22, 0xec, 0x93, 0xf9, 0xe5, 0xc0, 0xb3, 0xdb, 0x5b, 0x93, 0x93, - 0xa9, 0x04, 0x12, 0xdb, 0xd3, 0x09, 0xb6, 0x55, 0xe3, 0x56, 0x71, 0x70, 0x60, 0x62, 0x50, 0x1b, - 0x63, 0x65, 0x7d, 0x13, 0xd2, 0x34, 0xd4, 0xdf, 0xcd, 0x31, 0x53, 0x91, 0x7d, 0x10, 0xa6, 0x43, - 0x67, 0xba, 0xde, 0xde, 0xa3, 0xae, 0xb7, 0xc3, 0xc4, 0xa5, 0x25, 0xb7, 0xc7, 0xec, 0xaa, 0x09, - 0x2c, 0x99, 0x82, 0x92, 0x21, 0x9f, 0x9a, 0x9d, 0x8b, 0x76, 0x82, 0x5f, 0x8f, 0x26, 0xa8, 0xc8, - 0x15, 0x28, 0xb2, 0x25, 0x2b, 0xbe, 0x65, 0x96, 0xad, 0x0b, 0x0d, 0x29, 0xd4, 0x1f, 0xce, 0xc3, - 0x88, 0xf4, 0x35, 0xd7, 0x0f, 0xf4, 0x39, 0x6f, 0xec, 0xae, 0x99, 0xbe, 0xd3, 0x0b, 0xee, 0xa5, - 0xfc, 0x26, 0xdf, 0x08, 0x44, 0xb1, 0xab, 0x0b, 0x29, 0x21, 0x98, 0x57, 0xc5, 0x87, 0x96, 0x76, - 0xbf, 0x7d, 0x64, 0xf4, 0xb7, 0x8a, 0x83, 0xf9, 0x89, 0xc2, 0xad, 0xe2, 0x60, 0x71, 0xa2, 0x1f, - 0x83, 0x79, 0x61, 0xfc, 0x6c, 0xbe, 0x37, 0xb7, 0xee, 0x9b, 0x6b, 0xc7, 0xfc, 0xed, 0xc8, 0xe1, - 0x06, 0x3a, 0x8b, 0xc9, 0xe6, 0x98, 0x3f, 0x24, 0xf9, 0x50, 0x65, 0x73, 0x92, 0xe8, 0x54, 0xc8, - 0xe6, 0x9f, 0xe4, 0x40, 0x49, 0x95, 0x4d, 0xf9, 0x88, 0xfc, 0x20, 0x0e, 0x2f, 0xdd, 0xe9, 0x1f, - 0xe6, 0xe1, 0x54, 0xd5, 0xf2, 0xe8, 0x1a, 0xdf, 0x31, 0x1e, 0xf3, 0xa9, 0xe2, 0x36, 0x0c, 0x4b, - 0x1f, 0x23, 0xfa, 0xfc, 0xc9, 0x60, 0x3f, 0x1e, 0xa2, 0x32, 0x38, 0xc9, 0xa5, 0x0f, 0xef, 0x25, - 0x4e, 0x5c, 0xc8, 0xc7, 0x7c, 0xce, 0x39, 0x1e, 0x42, 0x3e, 0xe6, 0x93, 0xd7, 0x47, 0x54, 0xc8, - 0xff, 0x47, 0x0e, 0x4e, 0xa7, 0x54, 0x4e, 0x2e, 0xc3, 0x40, 0xbd, 0xbb, 0x8a, 0xb1, 0xbb, 0x72, - 0xa1, 0xc7, 0xb0, 0xdb, 0x5d, 0xc5, 0xb0, 0x5d, 0x9a, 0x8f, 0x24, 0x2b, 0xf8, 0xb8, 0x7e, 0xb9, - 0x5a, 0x99, 0x11, 0x52, 0x55, 0xa5, 0x30, 0x01, 0x0c, 0x9c, 0xf6, 0x65, 0xc1, 0x03, 0x7c, 0xdb, - 0x34, 0x9a, 0xb1, 0x07, 0xf8, 0xac, 0x0c, 0xf9, 0x01, 0x18, 0x2a, 0x7f, 0xd0, 0x75, 0x28, 0xf2, - 0xe5, 0x12, 0x7f, 0x2e, 0xe0, 0xeb, 0x23, 0xd2, 0x38, 0xf3, 0x58, 0x02, 0x8c, 0x22, 0xce, 0x3b, - 0x64, 0xa8, 0xfe, 0x64, 0x0e, 0x2e, 0x64, 0xb7, 0x8e, 0x7c, 0x12, 0x06, 0xd8, 0xce, 0xbc, 0xac, - 0x2d, 0x89, 0x4f, 0xe7, 0xa9, 0x81, 0xed, 0x16, 0x6d, 0xe8, 0x8e, 0x6c, 0xec, 0xfb, 0x64, 0xe4, - 0x6d, 0x18, 0xae, 0xba, 0x6e, 0x97, 0x3a, 0xf5, 0x97, 0xef, 0x68, 0x55, 0xb1, 0x27, 0xc4, 0x3d, - 0x87, 0x89, 0xe0, 0x86, 0xfb, 0x72, 0x2c, 0x3a, 0x97, 0x4c, 0xaf, 0xfe, 0x58, 0x0e, 0x2e, 0xf6, - 0xfa, 0x2a, 0xf2, 0x32, 0x0c, 0xae, 0x50, 0x4b, 0xb7, 0xbc, 0x6a, 0x45, 0x34, 0x09, 0xb7, 0x58, - 0x1e, 0xc2, 0xa2, 0x3b, 0x85, 0x80, 0x90, 0x15, 0xe2, 0xe7, 0x8a, 0x81, 0x23, 0x03, 0x3f, 0x03, - 0x45, 0x58, 0xac, 0x90, 0x4f, 0xa8, 0xfe, 0x5e, 0x1e, 0x46, 0x6a, 0xad, 0xee, 0x9a, 0x29, 0x2d, - 0x1c, 0xfb, 0xb6, 0xb7, 0x7d, 0xeb, 0x37, 0xbf, 0x37, 0xeb, 0x97, 0x0d, 0x37, 0x67, 0x9f, 0xc3, - 0xcd, 0x2f, 0x47, 0xde, 0x82, 0x52, 0x07, 0xbf, 0x23, 0x7e, 0x12, 0xcb, 0xbf, 0x2e, 0xeb, 0x24, - 0x96, 0x97, 0x61, 0xe3, 0xab, 0x79, 0x80, 0xf1, 0x15, 0x96, 0x95, 0x04, 0x1a, 0x2e, 0x12, 0x27, - 0x02, 0x3d, 0x14, 0x81, 0x86, 0x0b, 0xc2, 0x89, 0x40, 0x0f, 0x20, 0xd0, 0x5f, 0xcd, 0xc3, 0x58, - 0xb4, 0x4a, 0xf2, 0x49, 0x18, 0xe6, 0xd5, 0xf0, 0x73, 0xa1, 0x9c, 0xe4, 0x54, 0x1c, 0x82, 0x35, - 0xe0, 0x3f, 0xc4, 0x01, 0xd7, 0xf8, 0xba, 0xee, 0x36, 0xc2, 0x13, 0x1a, 0x7e, 0x7f, 0x3b, 0xc8, - 0x3d, 0xa1, 0x62, 0x28, 0x6d, 0x6c, 0x5d, 0x77, 0x67, 0xc2, 0xdf, 0x64, 0x16, 0x88, 0x43, 0xbb, - 0x2e, 0x8d, 0x32, 0x28, 0x22, 0x03, 0x91, 0x97, 0x3d, 0x8e, 0xd5, 0x4e, 0x71, 0x98, 0xcc, 0xe6, - 0x73, 0x41, 0xb3, 0x51, 0x19, 0xfa, 0x77, 0x91, 0x34, 0x5e, 0xa2, 0x4f, 0x3f, 0xe6, 0xe4, 0x04, - 0x15, 0xdd, 0xd3, 0xf9, 0xa6, 0xdc, 0xef, 0x00, 0xf5, 0x4f, 0x1c, 0xe8, 0x5f, 0xb6, 0xe8, 0xf2, - 0x7d, 0xf2, 0x12, 0x0c, 0x31, 0x85, 0x59, 0xb0, 0x59, 0x5f, 0xe6, 0x84, 0xff, 0x84, 0xa4, 0x49, - 0x88, 0x98, 0xef, 0xd3, 0x42, 0x2a, 0x72, 0x03, 0x20, 0x7c, 0x62, 0x26, 0xb4, 0x8f, 0xc8, 0x65, - 0x38, 0x66, 0xbe, 0x4f, 0x93, 0xe8, 0xfc, 0x52, 0xe2, 0x81, 0x4e, 0x21, 0x59, 0x8a, 0x63, 0xfc, - 0x52, 0x62, 0x7c, 0x2c, 0x00, 0x61, 0xbf, 0x6a, 0xba, 0xeb, 0x6e, 0xd8, 0x8e, 0x31, 0xb3, 0xae, - 0x5b, 0x6b, 0x34, 0xbe, 0x7b, 0x4a, 0x52, 0xcc, 0xf7, 0x69, 0x29, 0xe5, 0xc8, 0x1b, 0x30, 0x22, - 0x3b, 0x94, 0xc6, 0x9d, 0x3e, 0x64, 0xdc, 0x7c, 0x9f, 0x16, 0xa1, 0x25, 0xaf, 0xc1, 0xb0, 0xf8, - 0x7d, 0xcb, 0x16, 0x37, 0xca, 0x52, 0x2c, 0x22, 0x09, 0x35, 0xdf, 0xa7, 0xc9, 0x94, 0x52, 0xa5, - 0x35, 0xc7, 0xb4, 0x3c, 0xf1, 0x46, 0x39, 0x5e, 0x29, 0xe2, 0xa4, 0x4a, 0xf1, 0x37, 0x79, 0x1b, - 0x46, 0x83, 0x20, 0x4f, 0xef, 0xd3, 0xa6, 0x27, 0x0e, 0xbf, 0xcf, 0xc6, 0x0a, 0x73, 0xe4, 0x7c, - 0x9f, 0x16, 0xa5, 0x26, 0x57, 0xa0, 0xa4, 0x51, 0xd7, 0xfc, 0xc0, 0xbf, 0x2e, 0x1e, 0x93, 0xc6, - 0xb9, 0xf9, 0x01, 0x93, 0x92, 0xc0, 0xb3, 0xde, 0x09, 0xef, 0xa7, 0xc5, 0x51, 0x35, 0x89, 0xd5, - 0x32, 0x6b, 0x19, 0xac, 0x77, 0x24, 0xe7, 0x84, 0x4f, 0x87, 0xa1, 0xaf, 0x44, 0xe6, 0xd7, 0xe1, - 0x78, 0x8c, 0x01, 0x19, 0x3b, 0xdf, 0xa7, 0xc5, 0xe8, 0x25, 0xa9, 0x56, 0x4c, 0xf7, 0x81, 0x08, - 0x59, 0x1a, 0x97, 0x2a, 0x43, 0x49, 0x52, 0x65, 0x3f, 0xa5, 0xaa, 0x97, 0xa8, 0xb7, 0x61, 0x3b, - 0x0f, 0x44, 0x80, 0xd2, 0x78, 0xd5, 0x02, 0x2b, 0x55, 0x2d, 0x20, 0x72, 0xd5, 0x6c, 0xc0, 0x8d, - 0xa5, 0x57, 0xad, 0x7b, 0xba, 0x5c, 0x35, 0x3f, 0x89, 0xf3, 0x3b, 0x69, 0x81, 0xea, 0x0f, 0xa9, - 0x32, 0x9e, 0xda, 0xa1, 0x88, 0x93, 0x3a, 0x14, 0x7f, 0xb3, 0x4a, 0xa5, 0xb4, 0xf0, 0xca, 0x44, - 0xb4, 0x52, 0x09, 0xc5, 0x2a, 0x95, 0x13, 0xc8, 0xdf, 0x90, 0x73, 0x8f, 0x2b, 0xa7, 0xa2, 0x1d, - 0x14, 0x62, 0x58, 0x07, 0x49, 0x39, 0xca, 0x27, 0x31, 0xaf, 0xb1, 0x42, 0x90, 0x7c, 0x38, 0x68, - 0xe1, 0x4c, 0x6d, 0xbe, 0x4f, 0xc3, 0x8c, 0xc7, 0x2a, 0xcf, 0x98, 0xad, 0x9c, 0x46, 0x8a, 0x11, - 0x9f, 0x82, 0xc1, 0xe6, 0xfb, 0x34, 0x9e, 0x4d, 0xfb, 0x25, 0x29, 0xab, 0xa0, 0x72, 0x26, 0x3a, - 0x45, 0x04, 0x08, 0x36, 0x45, 0x84, 0xb9, 0x07, 0xe7, 0x92, 0xb9, 0xf3, 0x94, 0xb3, 0xd1, 0xa5, - 0x26, 0x8e, 0x9f, 0xef, 0xd3, 0x92, 0xf9, 0xf6, 0x5e, 0x8b, 0xa4, 0x93, 0x53, 0xce, 0xc5, 0x02, - 0x80, 0x85, 0x28, 0x26, 0x2e, 0x39, 0xf1, 0xdc, 0x32, 0x9c, 0xe6, 0xd9, 0x68, 0x45, 0x08, 0x2f, - 0x31, 0x59, 0x9d, 0x8f, 0x6e, 0x5c, 0x52, 0x48, 0xe6, 0xfb, 0xb4, 0xb4, 0x92, 0x64, 0x26, 0x91, - 0xd4, 0x4d, 0x51, 0xa2, 0xbe, 0x31, 0x31, 0xf4, 0x7c, 0x9f, 0x96, 0x48, 0x03, 0x77, 0x43, 0xce, - 0xa6, 0xa6, 0x3c, 0x11, 0xed, 0xc4, 0x10, 0xc3, 0x3a, 0x51, 0xca, 0xba, 0x76, 0x43, 0xce, 0xb0, - 0xa5, 0x5c, 0x48, 0x96, 0x0a, 0x67, 0x4e, 0x29, 0x13, 0x97, 0x96, 0x9e, 0x34, 0x48, 0x79, 0x52, - 0xa4, 0x0e, 0x16, 0xe5, 0xd3, 0x68, 0xe6, 0xfb, 0xb4, 0xf4, 0x84, 0x43, 0x5a, 0x7a, 0xb6, 0x1d, - 0xe5, 0x62, 0x2f, 0x9e, 0x41, 0xeb, 0xd2, 0x33, 0xf5, 0xe8, 0x3d, 0x72, 0x9f, 0x28, 0x97, 0xa2, - 0x21, 0x8c, 0x33, 0x09, 0xe7, 0xfb, 0xb4, 0x1e, 0x19, 0x54, 0xee, 0x64, 0x24, 0x22, 0x51, 0x9e, - 0x8a, 0x66, 0x0e, 0x4f, 0x25, 0x9a, 0xef, 0xd3, 0x32, 0xd2, 0x98, 0xdc, 0xc9, 0xc8, 0x53, 0xa1, - 0x4c, 0xf6, 0x64, 0x1b, 0xc8, 0x23, 0x23, 0xcb, 0xc5, 0x72, 0x6a, 0x8a, 0x07, 0xe5, 0xe9, 0xa8, - 0xea, 0xa6, 0x90, 0x30, 0xd5, 0x4d, 0x4b, 0x0e, 0xb1, 0x9c, 0x9a, 0x93, 0x40, 0x79, 0xa6, 0x07, - 0xc3, 0xa0, 0x8d, 0xa9, 0xd9, 0x0c, 0x96, 0x53, 0x93, 0x02, 0x28, 0x6a, 0x94, 0x61, 0x0a, 0x09, - 0x63, 0x98, 0x96, 0x4e, 0x60, 0x39, 0x35, 0x76, 0xbc, 0xf2, 0x6c, 0x0f, 0x86, 0x61, 0x0b, 0xd3, - 0xa2, 0xce, 0xbf, 0x16, 0x09, 0xde, 0xae, 0x3c, 0x17, 0x9d, 0x37, 0x24, 0x14, 0x9b, 0x37, 0xe4, - 0x30, 0xef, 0x33, 0x89, 0xc8, 0xb2, 0xca, 0xc7, 0xa2, 0xc3, 0x3c, 0x86, 0x66, 0xc3, 0x3c, 0x1e, - 0x8b, 0x76, 0x26, 0x11, 0x61, 0x53, 0xb9, 0x9c, 0xc5, 0x04, 0xd1, 0x51, 0x26, 0x3c, 0x26, 0x67, - 0x35, 0x25, 0xc4, 0xa3, 0xf2, 0xf1, 0xa8, 0x5f, 0x77, 0x82, 0x60, 0xbe, 0x4f, 0x4b, 0x09, 0x0c, - 0xa9, 0xa5, 0xc7, 0x33, 0x52, 0xae, 0x44, 0x87, 0x6d, 0x1a, 0x0d, 0x1b, 0xb6, 0xa9, 0xb1, 0x90, - 0x16, 0xd2, 0x1e, 0x9f, 0x28, 0x57, 0xa3, 0x86, 0x59, 0x92, 0x82, 0x19, 0x66, 0x29, 0x8f, 0x56, - 0xb4, 0xf4, 0x18, 0x3b, 0xca, 0xf3, 0x3d, 0x5b, 0x88, 0x34, 0x29, 0x2d, 0xe4, 0x21, 0x67, 0x42, - 0xdb, 0xe9, 0x4e, 0xa7, 0x65, 0xeb, 0x86, 0xf2, 0x89, 0x54, 0xdb, 0x89, 0x23, 0x25, 0xdb, 0x89, - 0x03, 0xd8, 0x2a, 0x2f, 0xbf, 0x71, 0x50, 0x5e, 0x88, 0xae, 0xf2, 0x32, 0x8e, 0xad, 0xf2, 0x91, - 0xf7, 0x10, 0x33, 0x89, 0xf7, 0x00, 0xca, 0x8b, 0x51, 0x05, 0x88, 0xa1, 0x99, 0x02, 0xc4, 0x5f, - 0x10, 0x7c, 0x3e, 0xdb, 0x83, 0x5e, 0x99, 0x42, 0x6e, 0x4f, 0xfb, 0xdc, 0xb2, 0xe8, 0xe6, 0xfb, - 0xb4, 0x6c, 0x2f, 0xfc, 0x6a, 0x8a, 0x43, 0xbc, 0x72, 0x2d, 0xaa, 0x60, 0x09, 0x02, 0xa6, 0x60, - 0x49, 0x37, 0xfa, 0x6a, 0x8a, 0x47, 0xbb, 0xf2, 0xc9, 0x4c, 0x56, 0xc1, 0x37, 0xa7, 0xf8, 0xc1, - 0xdf, 0x90, 0x5d, 0xd2, 0x95, 0x97, 0xa2, 0x8b, 0x5d, 0x88, 0x61, 0x8b, 0x9d, 0xe4, 0xba, 0x7e, - 0x43, 0x76, 0xc6, 0x56, 0xae, 0x27, 0x4b, 0x85, 0x4b, 0xa4, 0xe4, 0xb4, 0xad, 0xa5, 0xfb, 0x30, - 0x2b, 0x2f, 0x47, 0xb5, 0x2e, 0x8d, 0x86, 0x69, 0x5d, 0xaa, 0xff, 0xf3, 0x5c, 0xd2, 0x15, 0x59, - 0xb9, 0x11, 0xdf, 0x64, 0x47, 0xf1, 0xcc, 0xf2, 0x49, 0xb8, 0x2f, 0x7f, 0x3a, 0x1e, 0x6c, 0x4f, - 0x79, 0x25, 0x76, 0xed, 0x1b, 0xc1, 0x32, 0xfb, 0x36, 0x16, 0x9c, 0xef, 0xd3, 0xf1, 0xf8, 0x74, - 0xca, 0xab, 0xe9, 0x1c, 0x02, 0x5d, 0x89, 0xc7, 0xb3, 0xfb, 0x74, 0x3c, 0xa4, 0x9b, 0xf2, 0x5a, - 0x3a, 0x87, 0x40, 0xba, 0xf1, 0x10, 0x70, 0x2f, 0x49, 0x41, 0xe6, 0x95, 0x4f, 0x45, 0x4d, 0xc7, - 0x00, 0xc1, 0x4c, 0xc7, 0x30, 0x14, 0xfd, 0x4b, 0x52, 0x70, 0x76, 0xe5, 0xf5, 0x44, 0x91, 0xa0, - 0xb1, 0x52, 0x08, 0xf7, 0x97, 0xa4, 0xa0, 0xe6, 0xca, 0x1b, 0x89, 0x22, 0x41, 0xeb, 0xa4, 0xd0, - 0xe7, 0x46, 0xaf, 0xf7, 0xab, 0xca, 0x9b, 0xd1, 0xc3, 0xe0, 0x6c, 0xca, 0xf9, 0x3e, 0xad, 0xd7, - 0x3b, 0xd8, 0xcf, 0x67, 0x3b, 0x76, 0x2b, 0x6f, 0x45, 0x87, 0x70, 0x16, 0x1d, 0x1b, 0xc2, 0x99, - 0xce, 0xe1, 0x6f, 0xc7, 0x62, 0x59, 0x28, 0x6f, 0x47, 0xa7, 0xb8, 0x08, 0x92, 0x4d, 0x71, 0xf1, - 0xc8, 0x17, 0x91, 0x20, 0x0d, 0xca, 0xf7, 0x45, 0xa7, 0x38, 0x19, 0xc7, 0xa6, 0xb8, 0x48, 0x40, - 0x87, 0x99, 0x44, 0xec, 0x00, 0xe5, 0x9d, 0xe8, 0x14, 0x17, 0x43, 0xb3, 0x29, 0x2e, 0x1e, 0x6d, - 0xe0, 0xed, 0xd8, 0x13, 0x7a, 0xe5, 0xd3, 0xe9, 0xed, 0x47, 0xa4, 0xdc, 0x7e, 0xfe, 0xe0, 0x5e, - 0x4b, 0x7f, 0x0b, 0xae, 0x94, 0xa3, 0xe3, 0x37, 0x8d, 0x86, 0x8d, 0xdf, 0xd4, 0x77, 0xe4, 0xf1, - 0x8d, 0x83, 0xd0, 0xaa, 0xe9, 0x1e, 0x1b, 0x87, 0xd0, 0x14, 0x49, 0x01, 0x47, 0xf6, 0xc8, 0x7c, - 0x23, 0x34, 0x93, 0xb1, 0x47, 0xf6, 0xb7, 0x41, 0x31, 0x7a, 0x36, 0xbb, 0x26, 0xfc, 0x8c, 0x95, - 0x4a, 0x74, 0x76, 0x4d, 0x10, 0xb0, 0xd9, 0x35, 0xe9, 0x9d, 0x3c, 0x07, 0x13, 0x42, 0x8b, 0xb8, - 0xfb, 0xb4, 0x69, 0xad, 0x29, 0xb3, 0xb1, 0xf7, 0x96, 0x31, 0x3c, 0x9b, 0x9d, 0xe2, 0x30, 0x5c, - 0xaf, 0x39, 0x6c, 0xa6, 0x65, 0x76, 0x56, 0x6d, 0xdd, 0x31, 0xea, 0xd4, 0x32, 0x94, 0xb9, 0xd8, - 0x7a, 0x9d, 0x42, 0x83, 0xeb, 0x75, 0x0a, 0x1c, 0x43, 0xc4, 0xc5, 0xe0, 0x1a, 0x6d, 0x52, 0xf3, - 0x21, 0x55, 0x6e, 0x22, 0xdb, 0xc9, 0x2c, 0xb6, 0x82, 0x6c, 0xbe, 0x4f, 0xcb, 0xe2, 0xc0, 0x6c, - 0xf5, 0xc5, 0xcd, 0xfa, 0xbb, 0x0b, 0x41, 0xf8, 0x81, 0x9a, 0x43, 0x3b, 0xba, 0x43, 0x95, 0xf9, - 0xa8, 0xad, 0x9e, 0x4a, 0xc4, 0x6c, 0xf5, 0x54, 0x44, 0x92, 0xad, 0x3f, 0x16, 0xaa, 0xbd, 0xd8, - 0x86, 0x23, 0x22, 0xbd, 0x34, 0x9b, 0x9d, 0xa2, 0x08, 0x26, 0xa0, 0x05, 0xdb, 0x5a, 0xc3, 0x93, - 0x8a, 0x5b, 0xd1, 0xd9, 0x29, 0x9b, 0x92, 0xcd, 0x4e, 0xd9, 0x58, 0xa6, 0xea, 0x51, 0x2c, 0x1f, - 0x83, 0xb7, 0xa3, 0xaa, 0x9e, 0x42, 0xc2, 0x54, 0x3d, 0x05, 0x9c, 0x64, 0xa8, 0x51, 0x97, 0x7a, - 0xca, 0x42, 0x2f, 0x86, 0x48, 0x92, 0x64, 0x88, 0xe0, 0x24, 0xc3, 0x39, 0xea, 0x35, 0xd7, 0x95, - 0xc5, 0x5e, 0x0c, 0x91, 0x24, 0xc9, 0x10, 0xc1, 0x6c, 0xb3, 0x19, 0x05, 0x4f, 0x77, 0x5b, 0x0f, - 0xfc, 0x3e, 0x5b, 0x8a, 0x6e, 0x36, 0x33, 0x09, 0xd9, 0x66, 0x33, 0x13, 0x49, 0x7e, 0x6c, 0xd7, - 0x7e, 0xf0, 0xca, 0x32, 0x56, 0x38, 0x15, 0xda, 0x05, 0xbb, 0x29, 0x35, 0xdf, 0xa7, 0xed, 0xd6, - 0xcf, 0xfe, 0x13, 0x81, 0xd3, 0xa8, 0x52, 0xc3, 0xaa, 0xc6, 0x83, 0xb3, 0x0a, 0x0e, 0x9e, 0xef, - 0xd3, 0x02, 0xb7, 0xd2, 0xd7, 0x60, 0x18, 0x3f, 0xaa, 0x6a, 0x99, 0x5e, 0x65, 0x5a, 0x79, 0x37, - 0xba, 0x65, 0x92, 0x50, 0x6c, 0xcb, 0x24, 0xfd, 0x64, 0x93, 0x38, 0xfe, 0xe4, 0x53, 0x4c, 0x65, - 0x5a, 0xd1, 0xa2, 0x93, 0x78, 0x04, 0xc9, 0x26, 0xf1, 0x08, 0x20, 0xa8, 0xb7, 0xe2, 0xd8, 0x9d, - 0xca, 0xb4, 0x52, 0x4f, 0xa9, 0x97, 0xa3, 0x82, 0x7a, 0xf9, 0xcf, 0xa0, 0xde, 0xfa, 0x7a, 0xd7, - 0xab, 0xb0, 0x6f, 0x5c, 0x49, 0xa9, 0xd7, 0x47, 0x06, 0xf5, 0xfa, 0x00, 0x36, 0x15, 0x22, 0xa0, - 0xe6, 0xd8, 0x6c, 0xd2, 0xbe, 0x6d, 0xb6, 0x5a, 0xca, 0x9d, 0xe8, 0x54, 0x18, 0xc7, 0xb3, 0xa9, - 0x30, 0x0e, 0x63, 0xa6, 0x27, 0x6f, 0x15, 0x5d, 0xed, 0xae, 0x29, 0x77, 0xa3, 0xa6, 0x67, 0x88, - 0x61, 0xa6, 0x67, 0xf8, 0x0b, 0x77, 0x17, 0xec, 0x97, 0x46, 0xef, 0x3b, 0xd4, 0x5d, 0x57, 0xee, - 0xc5, 0x76, 0x17, 0x12, 0x0e, 0x77, 0x17, 0xd2, 0x6f, 0xb2, 0x06, 0x4f, 0x46, 0x16, 0x1a, 0xff, - 0xd2, 0xa6, 0x4e, 0x75, 0xa7, 0xb9, 0xae, 0x7c, 0x06, 0x59, 0x3d, 0x9b, 0xba, 0x54, 0x45, 0x49, - 0xe7, 0xfb, 0xb4, 0x5e, 0x9c, 0x70, 0x5b, 0xfe, 0xee, 0x02, 0x8f, 0x04, 0xab, 0xd5, 0x66, 0xfc, - 0x4d, 0xe8, 0x7b, 0xb1, 0x6d, 0x79, 0x92, 0x04, 0xb7, 0xe5, 0x49, 0x30, 0xe9, 0xc0, 0x53, 0xb1, - 0xad, 0xda, 0xa2, 0xde, 0x62, 0xfb, 0x12, 0x6a, 0xd4, 0xf4, 0xe6, 0x03, 0xea, 0x29, 0x9f, 0x45, - 0xde, 0x97, 0x33, 0x36, 0x7c, 0x31, 0xea, 0xf9, 0x3e, 0x6d, 0x07, 0x7e, 0x44, 0x85, 0x62, 0x7d, - 0x6e, 0xa5, 0xa6, 0x7c, 0x7f, 0xf4, 0x7c, 0x93, 0xc1, 0xe6, 0xfb, 0x34, 0xc4, 0x31, 0x2b, 0xed, - 0x4e, 0x67, 0xcd, 0xd1, 0x0d, 0xca, 0x0d, 0x2d, 0xb4, 0xdd, 0x84, 0x01, 0xfa, 0x03, 0x51, 0x2b, - 0x2d, 0x8b, 0x8e, 0x59, 0x69, 0x59, 0x38, 0xa6, 0xa8, 0x91, 0xa4, 0x27, 0xca, 0xe7, 0xa2, 0x8a, - 0x1a, 0x41, 0x32, 0x45, 0x8d, 0xa6, 0x48, 0xf9, 0x0c, 0x9c, 0x0b, 0xf6, 0xf3, 0x62, 0xfd, 0xe5, - 0x9d, 0xa6, 0x7c, 0x1e, 0xf9, 0x3c, 0x95, 0xb8, 0x0c, 0x88, 0x50, 0xcd, 0xf7, 0x69, 0x19, 0xe5, - 0xd9, 0x8a, 0x9b, 0x48, 0x0a, 0x26, 0xcc, 0x8b, 0x1f, 0x8c, 0xae, 0xb8, 0x19, 0x64, 0x6c, 0xc5, - 0xcd, 0x40, 0xa5, 0x32, 0x17, 0x42, 0xd5, 0x77, 0x60, 0x1e, 0xc8, 0x34, 0x8b, 0x43, 0x2a, 0x73, - 0x61, 0xa9, 0xad, 0xee, 0xc0, 0x3c, 0xb0, 0xd6, 0xb2, 0x38, 0x90, 0x2b, 0x50, 0xaa, 0xd7, 0x17, - 0xb5, 0xae, 0xa5, 0x34, 0x63, 0xde, 0xb2, 0x08, 0x9d, 0xef, 0xd3, 0x04, 0x9e, 0x99, 0x41, 0xb3, - 0x2d, 0xdd, 0xf5, 0xcc, 0xa6, 0x8b, 0x23, 0xc6, 0x1f, 0x21, 0x46, 0xd4, 0x0c, 0x4a, 0xa3, 0x61, - 0x66, 0x50, 0x1a, 0x9c, 0xd9, 0x8b, 0x33, 0xba, 0xeb, 0xea, 0x96, 0xe1, 0xe8, 0xd3, 0xb8, 0x4c, - 0xd0, 0xd8, 0x6b, 0xac, 0x08, 0x96, 0xd9, 0x8b, 0x51, 0x08, 0x1e, 0xbe, 0xfb, 0x10, 0xdf, 0xcc, - 0xb9, 0x1f, 0x3b, 0x7c, 0x8f, 0xe1, 0xf1, 0xf0, 0x3d, 0x06, 0x43, 0xbb, 0xd3, 0x87, 0x69, 0x74, - 0xcd, 0x64, 0x22, 0x52, 0xd6, 0x62, 0x76, 0x67, 0x9c, 0x00, 0xed, 0xce, 0x38, 0x30, 0xd2, 0x24, - 0x7f, 0xb9, 0x5d, 0xcf, 0x68, 0x52, 0xb8, 0xca, 0x26, 0xca, 0xb0, 0xf5, 0x3b, 0x1c, 0x1c, 0x95, - 0x4d, 0x4b, 0x6f, 0xdb, 0x95, 0x69, 0x5f, 0xea, 0x66, 0x74, 0xfd, 0xce, 0x24, 0x64, 0xeb, 0x77, - 0x26, 0x92, 0xcd, 0xae, 0xfe, 0x46, 0x6b, 0x5d, 0x77, 0xa8, 0x51, 0x31, 0x1d, 0x3c, 0x59, 0xdc, - 0xe4, 0x5b, 0xc3, 0xf7, 0xa3, 0xb3, 0x6b, 0x0f, 0x52, 0x36, 0xbb, 0xf6, 0x40, 0x33, 0x23, 0x2f, - 0x1d, 0xad, 0x51, 0xdd, 0x50, 0x1e, 0x44, 0x8d, 0xbc, 0x6c, 0x4a, 0x66, 0xe4, 0x65, 0x63, 0xb3, - 0x3f, 0xe7, 0x9e, 0x63, 0x7a, 0x54, 0x69, 0xed, 0xe6, 0x73, 0x90, 0x34, 0xfb, 0x73, 0x10, 0xcd, - 0x36, 0x84, 0xf1, 0x0e, 0x69, 0x47, 0x37, 0x84, 0xc9, 0x6e, 0x88, 0x97, 0x60, 0x16, 0x8b, 0x78, - 0x94, 0xa7, 0x58, 0x51, 0x8b, 0x45, 0x80, 0x99, 0xc5, 0x12, 0x3e, 0xdb, 0x8b, 0x3c, 0xc5, 0x52, - 0xec, 0xe8, 0x1a, 0x2a, 0xe3, 0xd8, 0x1a, 0x1a, 0x79, 0xb6, 0xf5, 0x5a, 0xe4, 0x9d, 0x81, 0xd2, - 0x89, 0x5a, 0x1d, 0x12, 0x8a, 0x59, 0x1d, 0xf2, 0x8b, 0x84, 0x19, 0x18, 0xc7, 0x5b, 0x70, 0xad, - 0x1b, 0xdc, 0xe3, 0xfc, 0x50, 0xf4, 0x33, 0x63, 0x68, 0xf6, 0x99, 0x31, 0x50, 0x84, 0x89, 0x98, - 0xb6, 0x9c, 0x0c, 0x26, 0xe1, 0xf9, 0x60, 0x0c, 0x44, 0x16, 0x80, 0xd4, 0xcb, 0x8b, 0x0b, 0x55, - 0xa3, 0x26, 0x5f, 0x91, 0xb9, 0xd1, 0x13, 0xd8, 0x24, 0xc5, 0x7c, 0x9f, 0x96, 0x52, 0x8e, 0xbc, - 0x0f, 0x17, 0x05, 0x54, 0xbc, 0xb8, 0xae, 0x39, 0xf6, 0x43, 0xd3, 0x08, 0x16, 0x04, 0x2f, 0xea, - 0xc7, 0xd6, 0x8b, 0x76, 0xbe, 0x4f, 0xeb, 0xc9, 0x2b, 0xbb, 0x2e, 0xb1, 0x3e, 0x74, 0x77, 0x53, - 0x57, 0xb0, 0x48, 0xf4, 0xe4, 0x95, 0x5d, 0x97, 0x90, 0xfb, 0xc3, 0xdd, 0xd4, 0x15, 0x74, 0x42, - 0x4f, 0x5e, 0xc4, 0x85, 0xc9, 0x5e, 0xf8, 0x72, 0xab, 0xa5, 0x6c, 0x60, 0x75, 0x1f, 0xdf, 0x4d, - 0x75, 0x65, 0x34, 0x38, 0x77, 0xe2, 0xc8, 0x66, 0xe9, 0xe5, 0x0e, 0xb5, 0xea, 0x91, 0x05, 0xe8, - 0x51, 0x74, 0x96, 0x4e, 0x10, 0xb0, 0x59, 0x3a, 0x01, 0x64, 0x03, 0x4a, 0x7e, 0xae, 0xa2, 0x6c, - 0x46, 0x07, 0x94, 0x8c, 0x63, 0x03, 0x2a, 0xf2, 0xb4, 0x65, 0x19, 0x4e, 0x2f, 0x3f, 0xf0, 0x74, - 0xdf, 0x82, 0x74, 0x45, 0x57, 0x7e, 0x10, 0xbb, 0x64, 0x4a, 0x92, 0xe0, 0x25, 0x53, 0x12, 0xcc, - 0xc6, 0x08, 0x03, 0xd7, 0x37, 0xad, 0xe6, 0x9c, 0x6e, 0xb6, 0xba, 0x0e, 0x55, 0xfe, 0x54, 0x74, - 0x8c, 0xc4, 0xd0, 0x6c, 0x8c, 0xc4, 0x40, 0x6c, 0x81, 0x66, 0xa0, 0xb2, 0xeb, 0x9a, 0x6b, 0x96, - 0xd8, 0x57, 0x76, 0x5b, 0x9e, 0xf2, 0x6f, 0x44, 0x17, 0xe8, 0x34, 0x1a, 0xb6, 0x40, 0xa7, 0xc1, - 0xf1, 0xd4, 0x89, 0xf5, 0x02, 0x5b, 0x3c, 0xe4, 0xbb, 0xca, 0x7f, 0x33, 0x76, 0xea, 0x94, 0x42, - 0x83, 0xa7, 0x4e, 0x29, 0x70, 0xb6, 0x3e, 0x72, 0x9b, 0x6c, 0xc1, 0x0c, 0xee, 0xaa, 0xff, 0xad, - 0xe8, 0xfa, 0x18, 0xc7, 0xb3, 0xf5, 0x31, 0x0e, 0x8b, 0xf2, 0x11, 0x5d, 0xf0, 0x6f, 0x67, 0xf1, - 0x09, 0xe4, 0x9f, 0x28, 0x43, 0x6e, 0xca, 0x7c, 0xc4, 0x48, 0xf9, 0xe1, 0x5c, 0x16, 0xa3, 0x60, - 0x78, 0x24, 0x0a, 0x45, 0x19, 0x69, 0xf4, 0xa1, 0x49, 0x37, 0x94, 0x2f, 0x64, 0x32, 0xe2, 0x04, - 0x51, 0x46, 0x1c, 0x46, 0xde, 0x83, 0x73, 0x21, 0x6c, 0x91, 0xb6, 0x57, 0x83, 0x99, 0xe9, 0x4f, - 0xe7, 0xa2, 0x66, 0x70, 0x3a, 0x19, 0x33, 0x83, 0xd3, 0x31, 0x69, 0xac, 0x85, 0xe8, 0xfe, 0x9d, - 0x1d, 0x58, 0x07, 0x12, 0xcc, 0x60, 0x90, 0xc6, 0x5a, 0x48, 0xf3, 0x47, 0x76, 0x60, 0x1d, 0xc8, - 0x34, 0x83, 0x01, 0xf9, 0xf1, 0x1c, 0x5c, 0x4e, 0x47, 0x95, 0x5b, 0xad, 0x39, 0xdb, 0x09, 0x71, - 0xca, 0x9f, 0xc9, 0x45, 0x0f, 0x1a, 0x76, 0x57, 0x6c, 0xbe, 0x4f, 0xdb, 0x65, 0x05, 0xe4, 0xfb, - 0x60, 0xb4, 0xdc, 0x35, 0x4c, 0x0f, 0x2f, 0xde, 0x98, 0xe1, 0xfc, 0xa3, 0xb9, 0xd8, 0x16, 0x47, - 0xc6, 0xe2, 0x16, 0x47, 0x06, 0x90, 0x5b, 0x70, 0xaa, 0x4e, 0x9b, 0x5d, 0xc7, 0xf4, 0x36, 0x35, - 0xda, 0xb1, 0x1d, 0x8f, 0xf1, 0xf8, 0xb3, 0xb9, 0xe8, 0x24, 0x96, 0xa0, 0x60, 0x93, 0x58, 0x02, - 0x48, 0xee, 0x26, 0x6e, 0xe5, 0x45, 0x67, 0xfe, 0x58, 0xae, 0xe7, 0xb5, 0x7c, 0xd0, 0x97, 0xe9, - 0xc5, 0x49, 0x2d, 0x76, 0x8b, 0x2e, 0xb8, 0xfe, 0x78, 0xae, 0xc7, 0x35, 0xba, 0x34, 0xc3, 0x25, - 0xc1, 0x8c, 0x63, 0x4a, 0x1a, 0x79, 0xe5, 0xcf, 0xe5, 0x7a, 0x5c, 0x7b, 0x87, 0x1c, 0xd3, 0x32, - 0xd0, 0xbf, 0xc2, 0x3d, 0x45, 0x04, 0xa3, 0x9f, 0xc8, 0x25, 0x5d, 0x45, 0x82, 0xf2, 0x12, 0x21, - 0x2b, 0x76, 0xc7, 0x0d, 0x94, 0xfe, 0x8b, 0xb9, 0xa4, 0x6f, 0x5e, 0x58, 0x2c, 0xfc, 0x45, 0x28, - 0x5c, 0x98, 0x7d, 0xe4, 0x51, 0xc7, 0xd2, 0x5b, 0xd8, 0x9d, 0x75, 0xcf, 0x76, 0xf4, 0x35, 0x3a, - 0x6b, 0xe9, 0xab, 0x2d, 0xaa, 0xfc, 0x64, 0x2e, 0x6a, 0xc1, 0x66, 0x93, 0x32, 0x0b, 0x36, 0x1b, - 0x4b, 0xd6, 0xe1, 0xc9, 0x34, 0x6c, 0xc5, 0x74, 0xb1, 0x9e, 0x2f, 0xe5, 0xa2, 0x26, 0x6c, 0x0f, - 0x5a, 0x66, 0xc2, 0xf6, 0x40, 0x93, 0xeb, 0x30, 0x34, 0x6d, 0xfb, 0xd3, 0xef, 0x9f, 0x8f, 0x39, - 0x43, 0x06, 0x98, 0xf9, 0x3e, 0x2d, 0x24, 0x13, 0x65, 0xc4, 0xa0, 0xfe, 0x72, 0xb2, 0x4c, 0x78, - 0xf9, 0x14, 0xfc, 0x10, 0x65, 0x84, 0xb8, 0xff, 0xdd, 0x64, 0x99, 0xf0, 0x8e, 0x2b, 0xf8, 0xc1, - 0x66, 0x12, 0x5e, 0xe3, 0xe2, 0x5c, 0x99, 0xd9, 0x6d, 0x33, 0xeb, 0x7a, 0xab, 0x45, 0xad, 0x35, - 0xaa, 0x7c, 0x25, 0x36, 0x93, 0xa4, 0x93, 0xb1, 0x99, 0x24, 0x1d, 0x43, 0x7e, 0x00, 0xce, 0xdf, - 0xd5, 0x5b, 0xa6, 0x11, 0xe2, 0xfc, 0xa4, 0xe2, 0xca, 0x4f, 0xe5, 0xa2, 0xbb, 0xe9, 0x0c, 0x3a, - 0xb6, 0x9b, 0xce, 0x40, 0x91, 0x45, 0x20, 0xb8, 0x8c, 0x06, 0xb3, 0x05, 0x5b, 0x9f, 0x95, 0x7f, - 0x2f, 0x17, 0xb5, 0x53, 0x93, 0x24, 0xcc, 0x4e, 0x4d, 0x42, 0x49, 0x23, 0x3b, 0x35, 0x88, 0xf2, - 0xd3, 0xb9, 0xe8, 0x69, 0x4d, 0x16, 0xe1, 0x7c, 0x9f, 0x96, 0x9d, 0x5f, 0xe4, 0x26, 0x4c, 0xd4, - 0x6b, 0xd5, 0xb9, 0xb9, 0xd9, 0xfa, 0xdd, 0x6a, 0x05, 0x1f, 0x3a, 0x18, 0xca, 0xcf, 0xc4, 0x56, - 0xac, 0x38, 0x01, 0x5b, 0xb1, 0xe2, 0x30, 0xf2, 0x26, 0x8c, 0xb0, 0xf6, 0xb3, 0x01, 0x83, 0x9f, - 0xfc, 0xd5, 0x5c, 0xd4, 0x9c, 0x92, 0x91, 0xcc, 0x9c, 0x92, 0x7f, 0x93, 0x3a, 0x9c, 0x61, 0x52, - 0xac, 0x39, 0xf4, 0x3e, 0x75, 0xa8, 0xd5, 0xf4, 0xc7, 0xf4, 0xcf, 0xe6, 0xa2, 0x56, 0x46, 0x1a, - 0x11, 0xb3, 0x32, 0xd2, 0xe0, 0xe4, 0x01, 0x5c, 0x8c, 0x9f, 0x04, 0xc9, 0xcf, 0x4e, 0x95, 0xbf, - 0x90, 0x8b, 0x19, 0xc3, 0x3d, 0x88, 0xd1, 0x18, 0xee, 0x81, 0x27, 0x16, 0x5c, 0x12, 0xc7, 0x2a, - 0xc2, 0xe1, 0x32, 0x5e, 0xdb, 0xcf, 0xf1, 0xda, 0x3e, 0x16, 0x3a, 0x04, 0xf6, 0xa0, 0x9e, 0xef, - 0xd3, 0x7a, 0xb3, 0x63, 0x7a, 0x96, 0x4c, 0x80, 0xa1, 0xfc, 0xc5, 0x5c, 0xba, 0x47, 0x4a, 0xc4, - 0x4d, 0x39, 0x2d, 0x73, 0xc6, 0x7b, 0x59, 0xe9, 0x1b, 0x94, 0xbf, 0x14, 0x1b, 0x6f, 0xe9, 0x64, - 0x6c, 0xbc, 0x65, 0xe4, 0x7f, 0xb8, 0x05, 0xa7, 0xb8, 0x52, 0xd7, 0x74, 0x1c, 0x86, 0xd6, 0x1a, - 0x35, 0x94, 0x7f, 0x3f, 0xb6, 0xda, 0x25, 0x28, 0xd0, 0xb5, 0x27, 0x0e, 0x64, 0x53, 0x77, 0xbd, - 0xa3, 0x5b, 0x16, 0x1e, 0xb3, 0x2a, 0xff, 0x41, 0x6c, 0xea, 0x0e, 0x51, 0xe8, 0xb8, 0x1b, 0xfc, - 0x62, 0x9a, 0xd0, 0x2b, 0xf5, 0x91, 0xf2, 0x1f, 0xc6, 0x34, 0xa1, 0x17, 0x31, 0xd3, 0x84, 0x9e, - 0x79, 0x94, 0xee, 0x66, 0x3c, 0x01, 0x57, 0xbe, 0x16, 0x5b, 0x91, 0x53, 0xa9, 0xd8, 0x8a, 0x9c, - 0xfe, 0x82, 0xfc, 0x6e, 0xc6, 0xf3, 0x69, 0xe5, 0xe7, 0x7b, 0xf3, 0x0d, 0x57, 0xfa, 0xf4, 0xd7, - 0xd7, 0x77, 0x33, 0x9e, 0x1e, 0x2b, 0x7f, 0xb9, 0x37, 0xdf, 0xd0, 0xb1, 0x2f, 0xfd, 0xe5, 0x72, - 0x23, 0xfb, 0xd9, 0xae, 0xf2, 0x57, 0xe2, 0x53, 0x57, 0x06, 0x21, 0x4e, 0x5d, 0x59, 0x6f, 0x7f, - 0x57, 0xe1, 0x09, 0xae, 0x21, 0x37, 0x1d, 0xbd, 0xb3, 0x5e, 0xa7, 0x9e, 0x67, 0x5a, 0x6b, 0xfe, - 0x4e, 0xec, 0x3f, 0xca, 0xc5, 0x8e, 0xc7, 0xb2, 0x28, 0xf1, 0x78, 0x2c, 0x0b, 0xc9, 0x94, 0x37, - 0xf1, 0x40, 0x57, 0xf9, 0xab, 0x31, 0xe5, 0x4d, 0x50, 0x30, 0xe5, 0x4d, 0xbe, 0xeb, 0xbd, 0x95, - 0xf2, 0x0e, 0x55, 0xf9, 0x8f, 0xb3, 0x79, 0x05, 0xed, 0x4b, 0x79, 0xbe, 0x7a, 0x2b, 0xe5, 0xb9, - 0xa5, 0xf2, 0x9f, 0x64, 0xf3, 0x0a, 0x7d, 0x90, 0x92, 0xaf, 0x34, 0xdf, 0x83, 0x73, 0x7c, 0x36, - 0x9f, 0xa3, 0x06, 0x8d, 0x7c, 0xe8, 0x2f, 0xc4, 0xc6, 0x7e, 0x3a, 0x19, 0x1e, 0xb9, 0xa7, 0x62, - 0xd2, 0x58, 0x8b, 0xb6, 0xfe, 0xb5, 0x1d, 0x58, 0x87, 0x1b, 0x82, 0x74, 0x0c, 0x5b, 0x6f, 0xe4, - 0xc7, 0x6f, 0xca, 0x2f, 0xc6, 0xd6, 0x1b, 0x19, 0x89, 0xee, 0x1c, 0xf2, 0x4b, 0xb9, 0x37, 0xa3, - 0x0f, 0xbd, 0x94, 0xbf, 0x9e, 0x5a, 0x38, 0xe8, 0x80, 0xe8, 0xab, 0xb0, 0x37, 0xa3, 0x8f, 0x9a, - 0x94, 0x5f, 0x4a, 0x2d, 0x1c, 0x7c, 0x40, 0xf4, 0x05, 0x14, 0xdb, 0x22, 0x75, 0x3d, 0x9b, 0xb3, - 0x8a, 0x4c, 0x0f, 0x7f, 0x23, 0xbe, 0x45, 0x4a, 0x25, 0xc3, 0x2d, 0x52, 0x2a, 0x26, 0x8d, 0xb5, - 0xf8, 0xbc, 0x5f, 0xde, 0x81, 0xb5, 0xb4, 0xb1, 0x4b, 0xc5, 0xa4, 0xb1, 0x16, 0x1f, 0xff, 0xf5, - 0x1d, 0x58, 0x4b, 0x1b, 0xbb, 0x54, 0x0c, 0x33, 0xc7, 0x42, 0xcc, 0x5d, 0xea, 0xb8, 0xa1, 0xfa, - 0xfd, 0xa7, 0x31, 0x73, 0x2c, 0x83, 0x8e, 0x99, 0x63, 0x19, 0xa8, 0x54, 0xee, 0x42, 0x28, 0xbf, - 0xb2, 0x13, 0xf7, 0xf0, 0x5e, 0x26, 0x03, 0x95, 0xca, 0x5d, 0xc8, 0xe5, 0x6f, 0xee, 0xc4, 0x3d, - 0xbc, 0x98, 0xc9, 0x40, 0x31, 0xa3, 0xa8, 0xee, 0xe9, 0x9e, 0xd9, 0x9c, 0xb7, 0x5d, 0x4f, 0x5a, - 0xe4, 0xff, 0xb3, 0x98, 0x51, 0x94, 0x46, 0xc4, 0x8c, 0xa2, 0x34, 0x78, 0x92, 0xa9, 0x90, 0xc6, - 0xaf, 0xf6, 0x64, 0x1a, 0x5a, 0x5a, 0x69, 0xf0, 0x24, 0x53, 0x21, 0x84, 0xff, 0xbc, 0x27, 0xd3, - 0xd0, 0x53, 0x3e, 0x0d, 0xce, 0x2c, 0xd3, 0x19, 0xc7, 0xde, 0xb0, 0x6e, 0xd1, 0x0d, 0xda, 0x12, - 0x9f, 0xfe, 0x6b, 0x31, 0xcb, 0x34, 0x4e, 0x80, 0xb7, 0x28, 0x31, 0x58, 0x94, 0x91, 0xf8, 0xdc, - 0x5f, 0xcf, 0x64, 0x14, 0x1e, 0x13, 0xc5, 0x61, 0x51, 0x46, 0xe2, 0x13, 0x7f, 0x23, 0x93, 0x51, - 0x78, 0x4c, 0x14, 0x87, 0x91, 0x32, 0x8c, 0xe1, 0x5b, 0x09, 0xdd, 0xf5, 0x3d, 0x3f, 0x7f, 0x3b, - 0x17, 0xbd, 0xf5, 0x8a, 0xa2, 0xe7, 0xfb, 0xb4, 0x58, 0x01, 0x99, 0x85, 0xf8, 0xa4, 0x6f, 0x66, - 0xb0, 0x08, 0xfd, 0x1d, 0xa3, 0x10, 0x99, 0x85, 0xf8, 0x98, 0xff, 0x22, 0x83, 0x45, 0xe8, 0xf0, - 0x18, 0x85, 0x90, 0x4f, 0xc1, 0x70, 0x7d, 0x6e, 0xa5, 0xe6, 0xa7, 0xe7, 0xfb, 0x5b, 0xb9, 0xd8, - 0xab, 0xa2, 0x10, 0x87, 0xaf, 0x8a, 0xc2, 0x9f, 0x6c, 0x46, 0xb9, 0x67, 0x3b, 0x0f, 0x5a, 0xb6, - 0x6e, 0xf8, 0x11, 0x19, 0x85, 0x28, 0xfe, 0x6e, 0x6c, 0x46, 0x49, 0x27, 0x63, 0x33, 0x4a, 0x3a, - 0x26, 0x8d, 0xb5, 0x10, 0xd1, 0xb7, 0x76, 0x60, 0x1d, 0xce, 0x83, 0xe9, 0x98, 0x34, 0xd6, 0x42, - 0x74, 0x7f, 0x6f, 0x07, 0xd6, 0xe1, 0x3c, 0x98, 0x8e, 0x21, 0x14, 0x2e, 0x04, 0x6f, 0x0e, 0xc3, - 0x4d, 0x60, 0xd5, 0x7a, 0xc8, 0x36, 0x9a, 0xca, 0xdf, 0x8f, 0x1d, 0x2f, 0x64, 0x93, 0xce, 0xf7, - 0x69, 0x3d, 0x18, 0x4d, 0x0f, 0x40, 0x3f, 0x1e, 0x61, 0xdf, 0x2a, 0x0d, 0x7e, 0x23, 0x37, 0xf1, - 0x9b, 0xb9, 0x5b, 0xa5, 0xc1, 0xdf, 0xcc, 0x4d, 0xfc, 0x16, 0xfb, 0xff, 0xb7, 0x72, 0x13, 0xbf, - 0x9d, 0xd3, 0x9e, 0x08, 0xa7, 0xa3, 0xf2, 0x1a, 0xb5, 0xbc, 0x5a, 0x4b, 0x17, 0x93, 0x69, 0x2a, - 0x8a, 0xff, 0x4c, 0x45, 0x89, 0x14, 0x64, 0x5f, 0xcb, 0xc1, 0x48, 0xdd, 0x73, 0xa8, 0xde, 0x16, - 0x11, 0xfd, 0x2e, 0xc0, 0x20, 0x77, 0x3a, 0xf7, 0x5f, 0xc8, 0x6b, 0xc1, 0x6f, 0x72, 0x19, 0xc6, - 0x16, 0x74, 0xd7, 0xc3, 0x26, 0x56, 0x2d, 0x83, 0x3e, 0xc2, 0x07, 0x97, 0x05, 0x2d, 0x06, 0x25, - 0x0b, 0x9c, 0x8e, 0x97, 0xc3, 0x20, 0xae, 0x85, 0x1d, 0x03, 0xd9, 0x0d, 0x7e, 0x7b, 0x6b, 0xb2, - 0x0f, 0xe3, 0xd6, 0xc5, 0xca, 0xaa, 0xbf, 0x9b, 0x83, 0x84, 0x3b, 0xfc, 0xfe, 0x23, 0x57, 0x2c, - 0xc3, 0x78, 0x2c, 0x70, 0xb0, 0x78, 0x35, 0xba, 0xcb, 0xb8, 0xc2, 0xf1, 0xd2, 0xe4, 0xe3, 0xc1, - 0x6b, 0xc5, 0x3b, 0xda, 0x82, 0x08, 0x52, 0x88, 0xe9, 0x35, 0xba, 0x4e, 0x4b, 0x93, 0x50, 0x22, - 0x08, 0xd5, 0x77, 0x27, 0xc2, 0xa8, 0xa8, 0xe4, 0xb2, 0x08, 0xa3, 0x91, 0x0b, 0x43, 0x1b, 0xc6, - 0x92, 0xf9, 0xf3, 0xb0, 0x19, 0xdf, 0x07, 0x23, 0xd5, 0x76, 0x87, 0x3a, 0xae, 0x6d, 0xe9, 0x9e, - 0xed, 0x88, 0x28, 0x04, 0x18, 0xf6, 0xce, 0x94, 0xe0, 0x72, 0x28, 0x36, 0x99, 0x9e, 0x5c, 0xf5, - 0x33, 0x04, 0x16, 0x30, 0x1e, 0x2d, 0x3e, 0x25, 0x8e, 0x27, 0x88, 0xe7, 0x14, 0x8c, 0xf4, 0x8e, - 0xab, 0xe3, 0xbb, 0xd6, 0x80, 0xb4, 0xcb, 0x00, 0x32, 0x29, 0x52, 0x90, 0x17, 0xa0, 0x84, 0x7a, - 0xec, 0x62, 0xe6, 0x4f, 0x11, 0x70, 0xb1, 0x85, 0x10, 0x39, 0xbc, 0x1d, 0xa7, 0x21, 0xb7, 0x61, - 0x22, 0x74, 0x72, 0xb8, 0xe9, 0xd8, 0xdd, 0x8e, 0x9f, 0xeb, 0x07, 0x13, 0xeb, 0x3f, 0x08, 0x70, - 0x8d, 0x35, 0x44, 0x4a, 0x2c, 0x12, 0x05, 0xc9, 0x3c, 0x8c, 0x87, 0x30, 0x26, 0x22, 0x3f, 0xc7, - 0x18, 0xe6, 0x77, 0x95, 0x78, 0x31, 0x71, 0x46, 0xf2, 0xbb, 0xc6, 0x8a, 0x91, 0x2a, 0x0c, 0xf8, - 0xd1, 0x16, 0x07, 0x77, 0x54, 0xd2, 0xd3, 0x22, 0xda, 0xe2, 0x80, 0x1c, 0x67, 0xd1, 0x2f, 0x4f, - 0xe6, 0x60, 0x4c, 0xb3, 0xbb, 0x1e, 0x5d, 0xb1, 0xc5, 0xe9, 0x80, 0x88, 0xea, 0x89, 0x6d, 0x72, - 0x18, 0xa6, 0xe1, 0xd9, 0x8d, 0x26, 0xc7, 0xc9, 0xf9, 0xf1, 0xa3, 0xa5, 0xc8, 0x12, 0x9c, 0x4a, - 0xb8, 0x83, 0xe0, 0x33, 0xd8, 0x21, 0x1e, 0x37, 0x4f, 0xfa, 0xbc, 0x24, 0xb3, 0x64, 0x51, 0xf2, - 0xa3, 0x39, 0x28, 0xad, 0x38, 0xba, 0xe9, 0xb9, 0xe2, 0x49, 0xec, 0xd9, 0xa9, 0x0d, 0x47, 0xef, - 0x30, 0xfd, 0x98, 0xc2, 0x80, 0xc3, 0x77, 0xf5, 0x56, 0x97, 0xba, 0xd3, 0xf7, 0xd8, 0xd7, 0xfd, - 0xf7, 0x5b, 0x93, 0x6f, 0xae, 0xe1, 0xa1, 0xf3, 0x54, 0xd3, 0x6e, 0x5f, 0x5b, 0x73, 0xf4, 0x87, - 0xa6, 0x87, 0xa6, 0xbd, 0xde, 0xba, 0xe6, 0xd1, 0x16, 0x9e, 0x6d, 0x5f, 0xd3, 0x3b, 0xe6, 0x35, - 0x0c, 0x6c, 0x7f, 0x2d, 0xe0, 0xc4, 0x6b, 0x60, 0x2a, 0xe0, 0xe1, 0x5f, 0xb2, 0x0a, 0x70, 0x1c, - 0x59, 0x02, 0x10, 0x9f, 0x5a, 0xee, 0x74, 0xc4, 0xfb, 0x5a, 0xe9, 0x44, 0xd8, 0xc7, 0x70, 0xc5, - 0x0e, 0x04, 0xa6, 0x77, 0xa4, 0x60, 0xce, 0x9a, 0xc4, 0x81, 0x69, 0xc1, 0x8a, 0x68, 0x91, 0x2f, - 0xa6, 0xd1, 0x50, 0xe2, 0x7e, 0x63, 0x53, 0x84, 0x14, 0x2f, 0x46, 0x56, 0x61, 0x5c, 0xf0, 0x0d, - 0x52, 0xbf, 0x8c, 0x45, 0x67, 0x85, 0x18, 0x9a, 0x2b, 0x6d, 0xd0, 0x46, 0x43, 0x80, 0xe5, 0x3a, - 0x62, 0x25, 0xc8, 0x74, 0x98, 0xaa, 0x7a, 0x49, 0x6f, 0x53, 0x57, 0x19, 0x47, 0x8d, 0xbd, 0xb8, - 0xbd, 0x35, 0xa9, 0xf8, 0xe5, 0x31, 0xf0, 0xa8, 0x2c, 0xba, 0x68, 0x11, 0x99, 0x07, 0xd7, 0xfa, - 0x89, 0x14, 0x1e, 0x71, 0x9d, 0x8f, 0x16, 0x21, 0x33, 0x30, 0x1a, 0x3c, 0xef, 0xb9, 0x73, 0xa7, - 0x5a, 0xc1, 0x07, 0xbc, 0x22, 0xf6, 0x6c, 0x2c, 0x39, 0x8b, 0xcc, 0x24, 0x52, 0x46, 0x8a, 0x89, - 0xc2, 0x5f, 0xf4, 0xc6, 0x62, 0xa2, 0x74, 0x52, 0x62, 0xa2, 0xd4, 0xc8, 0xdb, 0x30, 0x5c, 0xbe, - 0x57, 0x17, 0xb1, 0x5e, 0x5c, 0xe5, 0x74, 0x98, 0xe9, 0x4b, 0xdf, 0x70, 0x1b, 0x7e, 0x5c, 0x18, - 0xb9, 0xe9, 0x32, 0x3d, 0x99, 0x85, 0xb1, 0x88, 0x87, 0xa0, 0xab, 0x9c, 0x41, 0x0e, 0xd8, 0x72, - 0x1d, 0x31, 0x0d, 0x47, 0xa0, 0xe4, 0xe1, 0x15, 0x2d, 0xc4, 0xb4, 0xa6, 0x62, 0xba, 0x98, 0x35, - 0x49, 0xa3, 0x18, 0x56, 0x06, 0x9f, 0x03, 0x0f, 0x72, 0xad, 0x31, 0x04, 0xaa, 0xe1, 0x70, 0x9c, - 0xdc, 0xa3, 0xb1, 0x62, 0xe4, 0x7d, 0x20, 0x98, 0x67, 0x89, 0x1a, 0xfe, 0x85, 0x71, 0xb5, 0xe2, - 0x2a, 0xe7, 0x30, 0xf0, 0x3a, 0x89, 0x87, 0xb1, 0xa8, 0x56, 0xa6, 0x2f, 0x8b, 0xe9, 0xe3, 0x29, - 0x9d, 0x97, 0x6a, 0xf8, 0x21, 0x2c, 0x1a, 0x66, 0x24, 0x09, 0x75, 0x0a, 0x57, 0xb2, 0x01, 0xe7, - 0x6b, 0x0e, 0x7d, 0x68, 0xda, 0x5d, 0xd7, 0x5f, 0x3e, 0xfc, 0x79, 0xeb, 0xfc, 0x8e, 0xf3, 0xd6, - 0x33, 0xa2, 0xe2, 0xb3, 0x1d, 0x87, 0x3e, 0x6c, 0xf8, 0xe1, 0xb6, 0x23, 0xd1, 0x62, 0xb3, 0xb8, - 0x63, 0x2a, 0xed, 0x0f, 0xba, 0x0e, 0x15, 0x70, 0x93, 0xba, 0x8a, 0x12, 0x4e, 0xb5, 0x3c, 0x42, - 0x90, 0x19, 0xe0, 0x22, 0xa9, 0xb4, 0xa3, 0xc5, 0x88, 0x06, 0xe4, 0xe6, 0x8c, 0xef, 0x3c, 0x50, - 0x6e, 0xf2, 0x84, 0xc3, 0xca, 0x13, 0xc8, 0x4c, 0x65, 0x62, 0x59, 0x6b, 0x06, 0xa1, 0xf7, 0x1b, - 0xba, 0xc0, 0xcb, 0x62, 0x49, 0x96, 0x26, 0x0b, 0x30, 0x51, 0x73, 0xf0, 0x28, 0xf3, 0x36, 0xdd, - 0xac, 0xd9, 0x2d, 0xb3, 0xb9, 0x89, 0xaf, 0x92, 0xc5, 0x54, 0xd9, 0xe1, 0xb8, 0xc6, 0x03, 0xba, - 0xd9, 0xe8, 0x20, 0x56, 0x5e, 0x56, 0xe2, 0x25, 0xe5, 0x50, 0xd8, 0x4f, 0xee, 0x2e, 0x14, 0x36, - 0x85, 0x09, 0xe1, 0x7a, 0xf0, 0xc8, 0xa3, 0x16, 0x5b, 0xea, 0x5d, 0xf1, 0x02, 0x59, 0x89, 0xb9, - 0x2a, 0x04, 0x78, 0x3e, 0x75, 0x88, 0x51, 0x46, 0x03, 0xb0, 0xdc, 0xb0, 0x78, 0x91, 0x64, 0xbc, - 0xe8, 0x4b, 0xfb, 0x88, 0x17, 0xfd, 0xbf, 0x17, 0xe4, 0xf9, 0x97, 0x5c, 0x84, 0xa2, 0x94, 0xce, - 0x09, 0x83, 0xe1, 0x62, 0xe8, 0xfb, 0xa2, 0x88, 0xf1, 0x3d, 0x24, 0x6c, 0x97, 0x20, 0xea, 0x11, - 0xe6, 0xef, 0x0c, 0x03, 0xa4, 0x6a, 0x21, 0x01, 0xe6, 0x4e, 0xec, 0xae, 0xb6, 0xcc, 0x26, 0x26, - 0x44, 0x28, 0x48, 0x61, 0x4e, 0x10, 0xca, 0xf3, 0x21, 0x48, 0x24, 0xe4, 0x3a, 0x0c, 0xfb, 0x47, - 0xe8, 0x61, 0x30, 0x68, 0x8c, 0x93, 0x2f, 0x66, 0x6b, 0x11, 0x86, 0x5f, 0x22, 0x22, 0x6f, 0x00, - 0x84, 0xd3, 0x81, 0xb0, 0xb4, 0x70, 0xa9, 0x90, 0x67, 0x0f, 0x79, 0xa9, 0x08, 0xa9, 0xd9, 0xc4, - 0x29, 0xab, 0xa3, 0x9f, 0x2d, 0x16, 0x27, 0xce, 0x88, 0x0e, 0xcb, 0x0a, 0x12, 0x2d, 0x42, 0x96, - 0xe1, 0x54, 0x42, 0x03, 0x45, 0xe8, 0xe8, 0x67, 0xb6, 0xb7, 0x26, 0x2f, 0xa5, 0xa8, 0xaf, 0xbc, - 0x30, 0x27, 0xca, 0x92, 0x67, 0xa1, 0x70, 0x47, 0xab, 0x8a, 0xf0, 0xb5, 0x3c, 0xf2, 0x71, 0x24, - 0xb6, 0x15, 0xc3, 0x92, 0xd7, 0x01, 0x78, 0x7a, 0x98, 0x9a, 0xed, 0x78, 0x68, 0x51, 0x8c, 0x4e, - 0x3f, 0xc1, 0xc6, 0x32, 0x4f, 0x1f, 0xd3, 0x60, 0xcb, 0x98, 0xfc, 0xd1, 0x21, 0xb1, 0xfa, 0xa7, - 0xf3, 0x89, 0x65, 0x8d, 0x09, 0x5e, 0xb4, 0x42, 0xea, 0x7c, 0x14, 0xbc, 0xdf, 0x74, 0x2e, 0x78, - 0x89, 0x88, 0x5c, 0x81, 0xc1, 0x1a, 0x9b, 0x54, 0x9a, 0x76, 0x4b, 0xa8, 0x02, 0xc6, 0x30, 0xeb, - 0x08, 0x98, 0x16, 0x60, 0xc9, 0x75, 0x29, 0x3f, 0xb2, 0x14, 0x4c, 0xde, 0xcf, 0x8f, 0x1c, 0x8f, - 0xaa, 0x8e, 0x99, 0x92, 0xaf, 0xc7, 0xf2, 0xad, 0x89, 0x32, 0x29, 0x4b, 0x6a, 0x98, 0x5f, 0x2d, - 0x30, 0x68, 0xfb, 0x77, 0x32, 0x68, 0xd5, 0xbf, 0x9d, 0x4b, 0x0e, 0x51, 0x72, 0x23, 0x19, 0xd7, - 0x19, 0xd7, 0xaf, 0x00, 0x28, 0xd7, 0x1a, 0x44, 0x78, 0x8e, 0x44, 0x68, 0xce, 0xef, 0x3b, 0x42, - 0x73, 0x61, 0x8f, 0x11, 0x9a, 0xd5, 0xff, 0xb7, 0xd8, 0xd3, 0xcb, 0xfe, 0x48, 0x22, 0xf9, 0xbd, - 0xce, 0x36, 0x65, 0xac, 0xf6, 0xb2, 0x9b, 0xd8, 0x5a, 0x70, 0x27, 0xe2, 0x86, 0xce, 0x47, 0xa5, - 0xab, 0x45, 0x29, 0xc9, 0x3b, 0x30, 0xe2, 0x7f, 0x00, 0x46, 0xfe, 0x96, 0x22, 0x56, 0x07, 0x0b, - 0x62, 0x2c, 0x46, 0x76, 0xa4, 0x00, 0x79, 0x05, 0x86, 0xd0, 0x1c, 0xea, 0xe8, 0x4d, 0x3f, 0x2c, - 0x3c, 0x8f, 0x23, 0xef, 0x03, 0xe5, 0x68, 0x75, 0x01, 0x25, 0xf9, 0x1c, 0x94, 0x44, 0x6e, 0x94, - 0x12, 0x2e, 0xd1, 0xd7, 0x76, 0xf1, 0x2c, 0x61, 0x4a, 0xce, 0x8b, 0xc2, 0x37, 0x38, 0x08, 0x88, - 0x6c, 0x70, 0x78, 0x4a, 0x94, 0x15, 0x38, 0x5d, 0x73, 0xa8, 0x81, 0x0f, 0x60, 0x66, 0x1f, 0x75, - 0x1c, 0x91, 0xb5, 0x86, 0x4f, 0x10, 0xb8, 0xbe, 0x75, 0x7c, 0x34, 0x5b, 0x79, 0x05, 0x5e, 0x8e, - 0x4d, 0x9d, 0x52, 0x9c, 0x19, 0x3d, 0xbc, 0x25, 0xb7, 0xe9, 0xe6, 0x86, 0xed, 0x18, 0x3c, 0xb1, - 0x8b, 0x98, 0xfa, 0x85, 0xa0, 0x1f, 0x08, 0x94, 0x6c, 0xf4, 0x44, 0x0b, 0x5d, 0x78, 0x1d, 0x86, - 0xf7, 0x9b, 0x5b, 0xe4, 0x57, 0xf2, 0x19, 0xef, 0xd5, 0x1e, 0xdf, 0xf4, 0x8e, 0x41, 0xce, 0xf1, - 0xfe, 0x8c, 0x9c, 0xe3, 0x7f, 0x9c, 0xcf, 0x78, 0x8c, 0xf7, 0x58, 0xe7, 0x06, 0x0e, 0x84, 0x11, - 0xcd, 0x0d, 0x1c, 0xa6, 0x65, 0x36, 0x0d, 0x4d, 0x26, 0x8a, 0x65, 0x11, 0x2f, 0xed, 0x98, 0x45, - 0xfc, 0x17, 0x0a, 0xbd, 0x1e, 0x2b, 0x9e, 0xc8, 0x7e, 0x2f, 0xb2, 0xbf, 0x0e, 0xc3, 0x81, 0x64, - 0xab, 0x15, 0xb4, 0x97, 0x46, 0x83, 0x4c, 0x46, 0x1c, 0x8c, 0x65, 0x24, 0x22, 0x72, 0x95, 0xb7, - 0xb5, 0x6e, 0x7e, 0xc0, 0x73, 0x6a, 0x8c, 0x8a, 0x6c, 0x09, 0xba, 0xa7, 0x37, 0x5c, 0xf3, 0x03, - 0xaa, 0x05, 0x68, 0xf5, 0xef, 0xe6, 0x53, 0x5f, 0x7c, 0x9e, 0xf4, 0xd1, 0x1e, 0xfa, 0x28, 0x45, - 0x88, 0xfc, 0xad, 0xea, 0x89, 0x10, 0xf7, 0x20, 0xc4, 0x3f, 0xca, 0xa7, 0xbe, 0xec, 0x3d, 0x11, - 0xe2, 0x5e, 0x66, 0x8b, 0x17, 0x60, 0x48, 0xb3, 0x37, 0xdc, 0x19, 0xdc, 0x13, 0xf1, 0xb9, 0x02, - 0x27, 0x6a, 0xc7, 0xde, 0x70, 0x1b, 0xb8, 0xdb, 0xd1, 0x42, 0x02, 0xf5, 0xbb, 0xf9, 0x1e, 0x6f, - 0x9f, 0x4f, 0x04, 0xff, 0x61, 0x2e, 0x91, 0xbf, 0x9e, 0x8f, 0xbc, 0xad, 0x7e, 0x7c, 0x85, 0x7d, - 0x0d, 0xa0, 0xde, 0x5c, 0xa7, 0x6d, 0x5d, 0xca, 0x4b, 0x86, 0x47, 0x16, 0x2e, 0x42, 0x45, 0x3e, - 0xeb, 0x90, 0x44, 0xfd, 0x46, 0x3e, 0xf6, 0xb8, 0xfc, 0x44, 0x76, 0xbb, 0x96, 0x5d, 0xa0, 0x75, - 0xe2, 0xbd, 0xfc, 0x89, 0xe4, 0x76, 0x2b, 0xb9, 0x1f, 0xcb, 0xc7, 0x42, 0x0b, 0x3c, 0xb6, 0xb2, - 0x63, 0x03, 0x30, 0x19, 0xf2, 0xe0, 0xb1, 0xd5, 0xa4, 0x17, 0x60, 0x48, 0xc8, 0x21, 0x58, 0x2a, - 0xf8, 0xbc, 0xcf, 0x81, 0x78, 0x40, 0x1b, 0x10, 0xa8, 0x7f, 0x26, 0x0f, 0xd1, 0x90, 0x0f, 0x8f, - 0xa9, 0x0e, 0xfd, 0x7a, 0x3e, 0x1a, 0xec, 0xe2, 0xf1, 0xd5, 0x9f, 0x29, 0x80, 0x7a, 0x77, 0xb5, - 0x29, 0x62, 0x25, 0xf7, 0x4b, 0x27, 0xfc, 0x01, 0x54, 0x93, 0x28, 0xd4, 0x7f, 0x95, 0x4f, 0x8d, - 0xc0, 0xf1, 0xf8, 0x0a, 0xf0, 0x65, 0x3c, 0x15, 0x6f, 0x5a, 0xe1, 0x44, 0x8e, 0x87, 0x90, 0x6c, - 0xfc, 0x25, 0x92, 0x59, 0xfa, 0x84, 0xe4, 0x53, 0x29, 0xe6, 0x1a, 0xa6, 0xda, 0x08, 0xcd, 0x35, - 0xf9, 0x30, 0x5f, 0x32, 0xdc, 0x7e, 0x27, 0xbf, 0x53, 0xc0, 0x92, 0xc7, 0x79, 0x55, 0x1d, 0xa8, - 0xe9, 0x9b, 0x18, 0x58, 0x93, 0xf5, 0xc4, 0x08, 0x4f, 0xb5, 0xd8, 0xe1, 0x20, 0xf9, 0xda, 0x4e, - 0x50, 0xa9, 0xff, 0xbc, 0x3f, 0x3d, 0x5a, 0xc6, 0xe3, 0x2b, 0xc2, 0x8b, 0x50, 0xac, 0xe9, 0xde, - 0xba, 0xd0, 0x64, 0xbc, 0x0d, 0xec, 0xe8, 0xde, 0xba, 0x86, 0x50, 0x72, 0x15, 0x06, 0x35, 0x7d, - 0x83, 0x9f, 0x79, 0x96, 0xc2, 0x34, 0x98, 0x8e, 0xbe, 0xd1, 0xe0, 0xe7, 0x9e, 0x01, 0x9a, 0xa8, - 0x41, 0x1a, 0x56, 0x7e, 0xf2, 0x8d, 0x39, 0x00, 0x79, 0x1a, 0xd6, 0x20, 0xf9, 0xea, 0x45, 0x28, - 0x4e, 0xdb, 0xc6, 0x26, 0xde, 0x7c, 0x8d, 0xf0, 0xca, 0x56, 0x6d, 0x63, 0x53, 0x43, 0x28, 0xf9, - 0xf1, 0x1c, 0x0c, 0xcc, 0x53, 0xdd, 0x60, 0x23, 0x64, 0xa8, 0x97, 0xc3, 0xca, 0x67, 0x0e, 0xc7, - 0x61, 0xe5, 0xd4, 0x3a, 0xaf, 0x4c, 0x56, 0x14, 0x51, 0x3f, 0xb9, 0x09, 0x83, 0x33, 0xba, 0x47, - 0xd7, 0x6c, 0x67, 0x13, 0x5d, 0x70, 0xc6, 0xc2, 0x17, 0x17, 0x11, 0xfd, 0xf1, 0x89, 0xf8, 0xcd, - 0x58, 0x53, 0xfc, 0xd2, 0x82, 0xc2, 0x4c, 0x2c, 0xfc, 0x66, 0x4e, 0xa4, 0x1c, 0x47, 0xb1, 0xf0, - 0x2b, 0x3c, 0x4d, 0x60, 0xc2, 0x63, 0xe5, 0x91, 0xf4, 0x63, 0x65, 0xb4, 0x1e, 0xd1, 0x4d, 0x0f, - 0x93, 0x9f, 0x8e, 0xe2, 0xa2, 0xcf, 0xad, 0x47, 0x84, 0x62, 0xee, 0x53, 0x4d, 0x22, 0x51, 0xbf, - 0xd3, 0x0f, 0xa9, 0x6f, 0xeb, 0x4f, 0x94, 0xfc, 0x44, 0xc9, 0x43, 0x25, 0xaf, 0x24, 0x94, 0xfc, - 0x42, 0x32, 0x5a, 0xc3, 0x47, 0x54, 0xc3, 0xbf, 0x5a, 0x4c, 0xc4, 0x7a, 0x79, 0xbc, 0x77, 0x97, - 0xa1, 0xf4, 0xfa, 0x77, 0x94, 0x5e, 0x30, 0x20, 0x4a, 0x3b, 0x0e, 0x88, 0x81, 0xdd, 0x0e, 0x88, - 0xc1, 0xcc, 0x01, 0x11, 0x2a, 0xc8, 0x50, 0xa6, 0x82, 0x54, 0xc5, 0xa0, 0x81, 0xde, 0x29, 0x67, - 0x2e, 0x6e, 0x6f, 0x4d, 0x8e, 0xb1, 0xd1, 0x94, 0x9a, 0x6b, 0x06, 0x59, 0xa8, 0xbf, 0x5b, 0xec, - 0x11, 0xa0, 0xe9, 0x48, 0x74, 0xe4, 0x65, 0x28, 0x94, 0x3b, 0x1d, 0xa1, 0x1f, 0xa7, 0xa5, 0xd8, - 0x50, 0x19, 0xa5, 0x18, 0x35, 0x79, 0x03, 0x0a, 0xe5, 0x7b, 0xf5, 0x78, 0x9a, 0x99, 0xf2, 0xbd, - 0xba, 0xf8, 0x92, 0xcc, 0xb2, 0xf7, 0xea, 0xe4, 0xad, 0x30, 0xde, 0xeb, 0x7a, 0xd7, 0x7a, 0x20, - 0x36, 0x8a, 0xc2, 0x53, 0xd7, 0xf7, 0xe4, 0x69, 0x32, 0x14, 0xdb, 0x2e, 0xc6, 0x68, 0x63, 0xda, - 0x54, 0xda, 0xbd, 0x36, 0x0d, 0xec, 0xa8, 0x4d, 0x83, 0xbb, 0xd5, 0xa6, 0xa1, 0x5d, 0x68, 0x13, - 0xec, 0xa8, 0x4d, 0xc3, 0x07, 0xd7, 0xa6, 0x0e, 0x5c, 0x48, 0x06, 0xd5, 0x0b, 0x34, 0x42, 0x03, - 0x92, 0xc4, 0x0a, 0xc7, 0x12, 0xbc, 0xfa, 0xef, 0x72, 0x6c, 0x63, 0x03, 0xd1, 0x0d, 0x97, 0xe1, - 0x65, 0xd7, 0xb6, 0x64, 0x69, 0xf5, 0x57, 0xf2, 0xd9, 0xb1, 0x00, 0x8f, 0xe7, 0x14, 0xf7, 0x83, - 0xa9, 0x52, 0x2a, 0xc6, 0x1e, 0x4f, 0x64, 0x4a, 0x39, 0xc6, 0x36, 0x4d, 0x66, 0x5f, 0xcf, 0x67, - 0x05, 0x28, 0x3c, 0x90, 0xc4, 0x3e, 0x96, 0x74, 0x86, 0x43, 0x17, 0x7f, 0x37, 0xea, 0x05, 0x37, - 0x07, 0x23, 0xb2, 0x10, 0x85, 0x94, 0x76, 0x23, 0xe0, 0x48, 0x39, 0xf2, 0x56, 0x90, 0x0d, 0x48, - 0xf2, 0x8f, 0x41, 0x4f, 0x37, 0x7f, 0xcc, 0xc6, 0xdc, 0x63, 0x64, 0x72, 0xf2, 0x02, 0x94, 0xe6, - 0x30, 0xbc, 0xbe, 0x3c, 0xd8, 0x79, 0xc0, 0x7d, 0xd9, 0x6b, 0x85, 0xd3, 0xa8, 0x7f, 0x3b, 0x07, - 0xa7, 0x6f, 0x77, 0x57, 0xa9, 0x70, 0xb4, 0x0b, 0xda, 0xf0, 0x3e, 0x00, 0x03, 0x0b, 0x87, 0x99, - 0x1c, 0x3a, 0xcc, 0x7c, 0x42, 0x0e, 0x64, 0x18, 0x2b, 0x30, 0x15, 0x52, 0x73, 0x67, 0x99, 0x4b, - 0xbe, 0xcf, 0xe9, 0x83, 0xee, 0x2a, 0x6d, 0x24, 0xbc, 0x66, 0x24, 0xee, 0x17, 0xde, 0xe6, 0xde, - 0xfc, 0xfb, 0x75, 0x50, 0xf9, 0xe5, 0x7c, 0x66, 0xec, 0xc8, 0x63, 0x9b, 0xe4, 0xf4, 0xfb, 0x53, - 0x7b, 0x25, 0x9e, 0xec, 0x34, 0x85, 0x24, 0xc6, 0x31, 0x8d, 0x4b, 0xba, 0xc0, 0x8e, 0x79, 0xea, - 0xdd, 0x0f, 0x55, 0x60, 0x7f, 0x90, 0xcb, 0x8c, 0xf1, 0x79, 0x5c, 0x05, 0xa6, 0xfe, 0x2f, 0x05, - 0x3f, 0xb4, 0xe8, 0x81, 0x3e, 0xe1, 0x05, 0x18, 0x12, 0x11, 0x16, 0xa2, 0x7e, 0xc2, 0xe2, 0xd8, - 0x10, 0x8f, 0xa1, 0x03, 0x02, 0x66, 0x52, 0x48, 0x4e, 0xcc, 0x92, 0x9f, 0xb0, 0xe4, 0xc0, 0xac, - 0x49, 0x24, 0xcc, 0x68, 0x98, 0x7d, 0x64, 0x7a, 0x68, 0x81, 0xb0, 0xbe, 0x2c, 0x70, 0xa3, 0x81, - 0x3e, 0x32, 0x3d, 0x6e, 0x7f, 0x04, 0x68, 0x66, 0x10, 0x70, 0x5b, 0x44, 0xcc, 0x7b, 0x68, 0x10, - 0x70, 0x53, 0x45, 0x13, 0x18, 0xd6, 0x5a, 0xe1, 0x7c, 0x2b, 0x5c, 0x5a, 0x44, 0x6b, 0x85, 0xbb, - 0x2e, 0xb6, 0x36, 0x20, 0x60, 0x1c, 0x35, 0xba, 0x16, 0x3a, 0xf1, 0x21, 0x47, 0x07, 0x21, 0x9a, - 0xc0, 0x90, 0xeb, 0x30, 0x56, 0xf7, 0x74, 0xcb, 0xd0, 0x1d, 0x63, 0xb9, 0xeb, 0x75, 0xba, 0x9e, - 0x6c, 0x00, 0xbb, 0x9e, 0x61, 0x77, 0x3d, 0x2d, 0x46, 0x41, 0x3e, 0x09, 0xa3, 0x3e, 0x64, 0xd6, - 0x71, 0x6c, 0x47, 0xb6, 0x72, 0x5c, 0xcf, 0xa0, 0x8e, 0xa3, 0x45, 0x09, 0xc8, 0xa7, 0x60, 0xb4, - 0x6a, 0x3d, 0xb4, 0x9b, 0x3c, 0xca, 0x80, 0xb6, 0x20, 0x6c, 0x1e, 0x7c, 0x31, 0x66, 0x06, 0x88, - 0x46, 0xd7, 0x69, 0x69, 0x51, 0x42, 0x75, 0x3b, 0x9f, 0x8c, 0xc0, 0xfa, 0xf8, 0x6e, 0x90, 0xae, - 0x46, 0x1d, 0xf7, 0xd0, 0x5b, 0x15, 0x8d, 0x4f, 0xd9, 0x6f, 0x98, 0xdb, 0xa0, 0xd7, 0x61, 0xf0, - 0x36, 0xdd, 0xe4, 0x3e, 0xa6, 0xa5, 0xd0, 0x2d, 0xf9, 0x81, 0x80, 0xc9, 0xa7, 0xbb, 0x3e, 0x9d, - 0xfa, 0xcd, 0x7c, 0x32, 0xb6, 0xec, 0xe3, 0x2b, 0xec, 0x4f, 0xc2, 0x00, 0x8a, 0xb2, 0xea, 0x5f, - 0x2f, 0xa0, 0x00, 0x51, 0xdc, 0x51, 0x6f, 0x67, 0x9f, 0x4c, 0xfd, 0xf9, 0x52, 0x3c, 0xe0, 0xf0, - 0xe3, 0x2b, 0xbd, 0x37, 0x61, 0x78, 0xc6, 0xb6, 0x5c, 0xd3, 0xf5, 0xa8, 0xd5, 0xf4, 0x15, 0x16, - 0x1d, 0xff, 0x9b, 0x21, 0x58, 0xb6, 0x01, 0x25, 0xea, 0xfd, 0x28, 0x2f, 0x79, 0x15, 0x86, 0x50, - 0xe4, 0x68, 0x73, 0xf2, 0x09, 0x0f, 0x6f, 0x26, 0x56, 0x19, 0x30, 0x6e, 0x71, 0x86, 0xa4, 0xe4, - 0x0e, 0x0c, 0xce, 0xac, 0x9b, 0x2d, 0xc3, 0xa1, 0x16, 0xfa, 0x26, 0x4b, 0x71, 0x5d, 0xa2, 0x7d, - 0x39, 0x85, 0xff, 0x22, 0x2d, 0x6f, 0x4e, 0x53, 0x14, 0x8b, 0x3c, 0x16, 0x13, 0xb0, 0x0b, 0x3f, - 0x9d, 0x07, 0x08, 0x0b, 0x90, 0xa7, 0x21, 0x1f, 0xe4, 0xec, 0x46, 0x97, 0x98, 0x88, 0x06, 0xe5, - 0x71, 0xa9, 0x10, 0x63, 0x3b, 0xbf, 0xe3, 0xd8, 0xbe, 0x03, 0x25, 0x7e, 0xba, 0x86, 0x5e, 0xeb, - 0x52, 0x0c, 0xd4, 0xcc, 0x06, 0x4f, 0x21, 0x3d, 0xb7, 0xa5, 0xd1, 0xf2, 0x8c, 0x78, 0x80, 0x73, - 0x66, 0x17, 0x9a, 0xd0, 0x8f, 0x7f, 0x91, 0xcb, 0x50, 0x5c, 0xf1, 0xf3, 0xfd, 0x8e, 0xf2, 0x59, - 0x3a, 0x26, 0x3f, 0xc4, 0xb3, 0x6e, 0x9a, 0xb1, 0x2d, 0x8f, 0x55, 0x8d, 0xad, 0x1e, 0x11, 0x72, - 0x11, 0xb0, 0x88, 0x5c, 0x04, 0x4c, 0xfd, 0xaf, 0xf2, 0x29, 0xa1, 0xb0, 0x1f, 0xdf, 0x61, 0xf2, - 0x3a, 0x00, 0xbe, 0x3c, 0x67, 0xf2, 0xf4, 0x9f, 0x83, 0xe0, 0x28, 0x41, 0x46, 0xa8, 0xb6, 0x91, - 0x6d, 0x47, 0x48, 0xac, 0xfe, 0x83, 0x5c, 0x22, 0x7e, 0xf2, 0x81, 0xe4, 0x28, 0x5b, 0x65, 0xf9, - 0x7d, 0x9a, 0xb1, 0x7e, 0x5f, 0x14, 0xf6, 0xd6, 0x17, 0xd1, 0x6f, 0x39, 0x04, 0xcb, 0xf4, 0x28, - 0xbf, 0xe5, 0x3b, 0xf9, 0xb4, 0x68, 0xd2, 0xc7, 0x53, 0xc5, 0x6f, 0x04, 0x46, 0x69, 0x31, 0x16, - 0xbf, 0x1f, 0xa1, 0xf1, 0x9c, 0xe4, 0xc2, 0x4c, 0xfd, 0x3c, 0x8c, 0xc7, 0x62, 0x2c, 0x8b, 0xf4, - 0xd0, 0x97, 0x7b, 0x07, 0x6b, 0xce, 0x8e, 0x59, 0x10, 0x21, 0x53, 0xff, 0xbf, 0x5c, 0xef, 0x08, - 0xdb, 0x47, 0xae, 0x3a, 0x29, 0x02, 0x28, 0xfc, 0xeb, 0x11, 0xc0, 0x21, 0x6c, 0x83, 0x8f, 0xb7, - 0x00, 0x3e, 0x22, 0x93, 0xc7, 0x87, 0x2d, 0x80, 0x9f, 0xcf, 0xed, 0x18, 0x20, 0xfd, 0xa8, 0x65, - 0xa0, 0xfe, 0x8f, 0xb9, 0xd4, 0x40, 0xe6, 0x07, 0x6a, 0xd7, 0x5b, 0x50, 0xe2, 0x2e, 0x3c, 0xa2, - 0x55, 0x52, 0xea, 0x37, 0x06, 0xcd, 0x28, 0x2f, 0xca, 0x90, 0x05, 0x18, 0xe0, 0x6d, 0x30, 0x44, - 0x6f, 0x3c, 0xd7, 0x23, 0x9a, 0xba, 0x91, 0x35, 0x39, 0x0a, 0xb4, 0xfa, 0x77, 0x72, 0x89, 0xb8, - 0xea, 0x47, 0xf8, 0x6d, 0xe1, 0x54, 0x5d, 0xd8, 0xfd, 0x54, 0xad, 0xfe, 0xb3, 0x7c, 0x7a, 0x58, - 0xf7, 0x23, 0xfc, 0x90, 0xc3, 0x38, 0x4e, 0xdb, 0xdf, 0xba, 0xb5, 0x02, 0x63, 0x51, 0x59, 0x88, - 0x65, 0xeb, 0xa9, 0xf4, 0xe0, 0xf6, 0x19, 0xad, 0x88, 0xf1, 0x50, 0xbf, 0x9d, 0x4b, 0x46, 0xa4, - 0x3f, 0xf2, 0xf9, 0x69, 0x7f, 0xda, 0x12, 0xfd, 0x94, 0x8f, 0xc8, 0x5a, 0x73, 0x18, 0x9f, 0xf2, - 0x11, 0x59, 0x35, 0xf6, 0xf7, 0x29, 0xbf, 0x98, 0xcf, 0x0a, 0xe8, 0x7f, 0xe4, 0x1f, 0xf4, 0x59, - 0x59, 0xc8, 0xbc, 0x65, 0xe2, 0xd3, 0x9e, 0xce, 0x8a, 0xa0, 0x9f, 0xc1, 0x33, 0xc1, 0x67, 0x7f, - 0x63, 0x3c, 0x55, 0x58, 0x1f, 0x11, 0x45, 0x3e, 0x1e, 0xc2, 0xfa, 0x88, 0x0c, 0x95, 0x8f, 0x9e, - 0xb0, 0x7e, 0x33, 0xbf, 0xdb, 0x2c, 0x12, 0x27, 0xc2, 0x4b, 0x08, 0xef, 0xcb, 0xf9, 0x64, 0x76, - 0x93, 0x23, 0x17, 0xd3, 0x1c, 0x94, 0x44, 0x9e, 0x95, 0x4c, 0xe1, 0x70, 0x7c, 0x96, 0x45, 0x23, - 0xbe, 0xe3, 0x06, 0x88, 0x8b, 0x9c, 0xdd, 0x89, 0x84, 0xd3, 0xaa, 0xdf, 0xcd, 0xc5, 0x52, 0x81, - 0x1c, 0xc9, 0x11, 0xc2, 0xbe, 0x96, 0x24, 0xf2, 0xb6, 0x7f, 0x98, 0x59, 0x8c, 0x85, 0x62, 0x0f, - 0xbe, 0xa7, 0x42, 0x3d, 0xdd, 0x6c, 0xc5, 0xcb, 0x8b, 0xf8, 0x03, 0xdf, 0xcc, 0xc3, 0xa9, 0x04, - 0x29, 0xb9, 0x1c, 0x89, 0xf8, 0x83, 0xc7, 0x92, 0x31, 0x47, 0x75, 0x1e, 0xfb, 0x67, 0x0f, 0x27, - 0xa9, 0x97, 0xa1, 0x58, 0xd1, 0x37, 0xf9, 0xb7, 0xf5, 0x73, 0x96, 0x86, 0xbe, 0x29, 0x9f, 0xb8, - 0x21, 0x9e, 0xac, 0xc2, 0x59, 0x7e, 0x1f, 0x62, 0xda, 0xd6, 0x8a, 0xd9, 0xa6, 0x55, 0x6b, 0xd1, - 0x6c, 0xb5, 0x4c, 0x57, 0x5c, 0xea, 0xbd, 0xb0, 0xbd, 0x35, 0x79, 0xc5, 0xb3, 0x3d, 0xbd, 0xd5, - 0xa0, 0x3e, 0x59, 0xc3, 0x33, 0xdb, 0xb4, 0x61, 0x5a, 0x8d, 0x36, 0x52, 0x4a, 0x2c, 0xd3, 0x59, - 0x91, 0x2a, 0x8f, 0xba, 0x5f, 0x6f, 0xea, 0x96, 0x45, 0x8d, 0xaa, 0x35, 0xbd, 0xe9, 0x51, 0x7e, - 0x19, 0x58, 0xe0, 0x47, 0x82, 0xfc, 0x1d, 0x3a, 0x47, 0x33, 0xc6, 0xab, 0x8c, 0x40, 0x4b, 0x29, - 0xa4, 0xfe, 0x56, 0x31, 0x25, 0x0b, 0xcc, 0x31, 0x52, 0x1f, 0xbf, 0xa7, 0x8b, 0x3b, 0xf4, 0xf4, - 0x35, 0x18, 0x10, 0x61, 0x8d, 0xc5, 0x05, 0x03, 0x3a, 0xce, 0x3f, 0xe4, 0x20, 0xf9, 0x86, 0x46, - 0x50, 0x91, 0x16, 0x5c, 0x58, 0x61, 0xdd, 0x94, 0xde, 0x99, 0xa5, 0x7d, 0x74, 0x66, 0x0f, 0x7e, - 0xe4, 0x3d, 0x38, 0x8f, 0xd8, 0x94, 0x6e, 0x1d, 0xc0, 0xaa, 0x30, 0x94, 0x16, 0xaf, 0x2a, 0xbd, - 0x73, 0xb3, 0xca, 0x93, 0xcf, 0xc2, 0x48, 0x30, 0x40, 0x4c, 0xea, 0x8a, 0x9b, 0x8b, 0x1e, 0xe3, - 0x8c, 0xc7, 0xa9, 0x63, 0x60, 0x74, 0x57, 0x8b, 0xc6, 0x3a, 0x8b, 0xf0, 0x52, 0xff, 0x87, 0x5c, - 0xaf, 0x6c, 0x34, 0x47, 0x3e, 0x2b, 0xbf, 0x0d, 0x03, 0x06, 0xff, 0x28, 0xa1, 0x53, 0xbd, 0xf3, - 0xd5, 0x70, 0x52, 0xcd, 0x2f, 0xa3, 0xfe, 0xd3, 0x5c, 0xcf, 0x24, 0x38, 0xc7, 0xfd, 0xf3, 0xbe, - 0x5c, 0xc8, 0xf8, 0x3c, 0x31, 0x89, 0x5e, 0x85, 0x09, 0x33, 0x8c, 0xd2, 0xdf, 0x08, 0x43, 0x5d, - 0x69, 0xe3, 0x12, 0x1c, 0x47, 0xd7, 0x0d, 0x08, 0x1c, 0xb6, 0x1c, 0xdf, 0x1b, 0xcd, 0x6d, 0x74, - 0x1d, 0x93, 0x8f, 0x4b, 0xed, 0x8c, 0x1b, 0x73, 0x55, 0x73, 0xef, 0x38, 0x26, 0xab, 0x40, 0xf7, - 0xd6, 0xa9, 0xa5, 0x37, 0x36, 0x6c, 0xe7, 0x01, 0x06, 0x43, 0xe5, 0x83, 0x53, 0x1b, 0xe7, 0xf0, - 0x7b, 0x3e, 0x98, 0x3c, 0x0b, 0xa3, 0x6b, 0xad, 0x2e, 0x0d, 0xc2, 0x4f, 0xf2, 0xbb, 0x3e, 0x6d, - 0x84, 0x01, 0x83, 0x1b, 0x92, 0x4b, 0x00, 0x48, 0xe4, 0x61, 0x8a, 0x22, 0xbc, 0xd8, 0xd3, 0x86, - 0x18, 0x64, 0x45, 0x74, 0xd7, 0x05, 0xae, 0xd5, 0x5c, 0x48, 0x8d, 0x96, 0x6d, 0xad, 0x35, 0x3c, - 0xea, 0xb4, 0xb1, 0xa1, 0xe8, 0xcc, 0xa0, 0x9d, 0x43, 0x0a, 0xbc, 0x3a, 0x71, 0x17, 0x6c, 0x6b, - 0x6d, 0x85, 0x3a, 0x6d, 0xd6, 0xd4, 0x17, 0x80, 0x88, 0xa6, 0x3a, 0x78, 0xe8, 0xc1, 0x3f, 0x0e, - 0xbd, 0x19, 0x34, 0xf1, 0x11, 0xfc, 0x34, 0x04, 0x3f, 0x6c, 0x12, 0x86, 0x79, 0x0c, 0x3e, 0x2e, - 0x34, 0x74, 0x61, 0xd0, 0x80, 0x83, 0x50, 0x5e, 0xe7, 0x40, 0x78, 0x57, 0x70, 0x0f, 0x72, 0x4d, - 0xfc, 0x52, 0xbf, 0x58, 0x48, 0xcb, 0xdb, 0x73, 0x20, 0x45, 0x0b, 0xa7, 0xd5, 0xfc, 0x9e, 0xa6, - 0xd5, 0x71, 0xab, 0xdb, 0x6e, 0xe8, 0x9d, 0x4e, 0xe3, 0xbe, 0xd9, 0xc2, 0x27, 0x5c, 0xb8, 0xf0, - 0x69, 0xa3, 0x56, 0xb7, 0x5d, 0xee, 0x74, 0xe6, 0x38, 0x90, 0x3c, 0x0f, 0xa7, 0x18, 0x1d, 0x76, - 0x52, 0x40, 0x59, 0x44, 0x4a, 0xc6, 0x00, 0x83, 0xd8, 0xfa, 0xb4, 0x4f, 0xc0, 0xa0, 0xe0, 0xc9, - 0xd7, 0xaa, 0x7e, 0x6d, 0x80, 0x33, 0x73, 0x59, 0xcf, 0x05, 0x6c, 0xf8, 0xe4, 0xda, 0xaf, 0x0d, - 0xf9, 0xe5, 0x31, 0x54, 0xb3, 0xd5, 0x6d, 0xf3, 0xe8, 0x5b, 0x03, 0x88, 0x0c, 0x7e, 0x93, 0xcb, - 0x30, 0xc6, 0xb8, 0x04, 0x02, 0xe3, 0xd1, 0x6d, 0xfb, 0xb5, 0x18, 0x94, 0x5c, 0x87, 0x33, 0x11, - 0x08, 0xb7, 0x41, 0xf9, 0x93, 0x84, 0x7e, 0x2d, 0x15, 0xa7, 0x7e, 0xa3, 0x10, 0xcd, 0x26, 0x74, - 0x04, 0x1d, 0x71, 0x1e, 0x06, 0x6c, 0x67, 0xad, 0xd1, 0x75, 0x5a, 0x62, 0xec, 0x95, 0x6c, 0x67, - 0xed, 0x8e, 0xd3, 0x22, 0x67, 0xa1, 0xc4, 0x7a, 0xc7, 0x34, 0xc4, 0x10, 0xeb, 0xd7, 0x3b, 0x9d, - 0xaa, 0x41, 0xca, 0xbc, 0x43, 0x30, 0x32, 0x6a, 0xa3, 0x89, 0x5b, 0x7b, 0xee, 0x94, 0xd0, 0xcf, - 0x57, 0xbc, 0x04, 0x12, 0xfb, 0x09, 0xe3, 0xa5, 0xf2, 0x83, 0x80, 0x18, 0x0b, 0x03, 0xb7, 0x25, - 0x06, 0xef, 0x93, 0x38, 0x0b, 0x81, 0x0c, 0x59, 0xf0, 0x4d, 0x8c, 0x41, 0x2a, 0x40, 0x42, 0xaa, - 0xb6, 0x6d, 0x98, 0xf7, 0x4d, 0xca, 0x5f, 0x90, 0xf4, 0xf3, 0x8b, 0xdf, 0x24, 0x56, 0x9b, 0xf0, - 0x99, 0x2c, 0x0a, 0x08, 0x79, 0x93, 0x2b, 0x21, 0xa7, 0xc3, 0xb5, 0x8f, 0xf7, 0x2d, 0xb7, 0xd3, - 0x62, 0x28, 0xd4, 0x4c, 0x2c, 0x8f, 0x0b, 0xa1, 0xfa, 0xdf, 0xf5, 0x27, 0x53, 0x4a, 0x1d, 0x89, - 0x5d, 0x33, 0x0f, 0x20, 0x32, 0xc6, 0x85, 0x97, 0x6b, 0x81, 0x77, 0x7b, 0x88, 0xc9, 0xe0, 0x21, - 0x95, 0x25, 0x57, 0x61, 0x90, 0x7f, 0x51, 0xb5, 0x22, 0xec, 0x1d, 0x74, 0x11, 0x73, 0x3b, 0xe6, - 0xfd, 0xfb, 0xe8, 0x4f, 0x16, 0xa0, 0xc9, 0x65, 0x18, 0xa8, 0x2c, 0xd5, 0xeb, 0xe5, 0x25, 0xff, - 0xa6, 0x18, 0xdf, 0xb2, 0x18, 0x96, 0xdb, 0x70, 0x75, 0xcb, 0xd5, 0x7c, 0x24, 0x79, 0x16, 0x4a, - 0xd5, 0x1a, 0x92, 0xf1, 0x17, 0x9a, 0xc3, 0xdb, 0x5b, 0x93, 0x03, 0x66, 0x87, 0x53, 0x09, 0x14, - 0xd6, 0x7b, 0xb7, 0x5a, 0x91, 0xdc, 0x25, 0x78, 0xbd, 0x0f, 0x4d, 0x03, 0xaf, 0x9d, 0xb5, 0x00, - 0x4d, 0x5e, 0x81, 0x91, 0x3a, 0x75, 0x4c, 0xbd, 0xb5, 0xd4, 0xc5, 0xad, 0xa2, 0x14, 0xf1, 0xd1, - 0x45, 0x78, 0xc3, 0x42, 0x84, 0x16, 0x21, 0x23, 0x17, 0xa1, 0x38, 0x6f, 0x5a, 0xfe, 0x73, 0x09, - 0xf4, 0xa7, 0x5f, 0x37, 0x2d, 0x4f, 0x43, 0x28, 0x79, 0x16, 0x0a, 0xb7, 0x56, 0xaa, 0xc2, 0x13, - 0x0c, 0x79, 0xbd, 0xef, 0x45, 0xa2, 0x47, 0xde, 0x5a, 0xa9, 0x92, 0x57, 0x60, 0x88, 0x2d, 0x62, - 0xd4, 0x6a, 0x52, 0x57, 0x19, 0xc6, 0x8f, 0xe1, 0x21, 0x0b, 0x7d, 0xa0, 0xec, 0xd3, 0x11, 0x50, - 0x92, 0xdb, 0x30, 0x11, 0x8f, 0x84, 0x2f, 0x9e, 0xec, 0xa0, 0xc5, 0xb5, 0x21, 0x70, 0x69, 0x41, - 0x33, 0x13, 0x05, 0x89, 0x01, 0x4a, 0x1c, 0xc6, 0xf6, 0x75, 0x68, 0x75, 0xf2, 0x78, 0xcd, 0x57, - 0xb6, 0xb7, 0x26, 0x9f, 0x4b, 0x30, 0x6d, 0x38, 0x82, 0x4a, 0xe2, 0x9e, 0xc9, 0x49, 0xfd, 0x3f, - 0xf3, 0xe9, 0x69, 0xca, 0x8e, 0x60, 0x76, 0xda, 0xe7, 0xc5, 0x77, 0x6c, 0x4c, 0x14, 0x0f, 0x30, - 0x26, 0xee, 0xc3, 0x78, 0xd9, 0x68, 0x9b, 0x56, 0x19, 0x7f, 0xba, 0x8b, 0x73, 0x65, 0x9c, 0xed, - 0xa4, 0xd7, 0x8b, 0x31, 0xb4, 0xf8, 0x1e, 0x1e, 0x4a, 0x99, 0xa1, 0x1a, 0x3a, 0xc7, 0x35, 0xda, - 0xf7, 0xf5, 0x46, 0x93, 0x67, 0xf8, 0xd2, 0xe2, 0x4c, 0xd5, 0x9f, 0xca, 0xef, 0x90, 0x59, 0xed, - 0x71, 0x94, 0xbe, 0xfa, 0x95, 0x7c, 0xef, 0xe4, 0x76, 0x8f, 0xa5, 0x50, 0xfe, 0x28, 0x9f, 0x92, - 0x6a, 0xee, 0x40, 0x92, 0xb8, 0x0a, 0x83, 0x9c, 0x4d, 0xe0, 0x79, 0x8c, 0x13, 0x30, 0x57, 0x56, - 0x9c, 0xf8, 0x7d, 0x34, 0x59, 0x82, 0x33, 0xe5, 0xfb, 0xf7, 0x69, 0xd3, 0x0b, 0x83, 0x6a, 0x2f, - 0x85, 0x31, 0x6a, 0x79, 0x10, 0x61, 0x81, 0x0f, 0x83, 0x72, 0x63, 0x2c, 0x96, 0xd4, 0x72, 0x64, - 0x05, 0xce, 0xc5, 0xe1, 0x75, 0xbe, 0x6b, 0x29, 0x4a, 0x71, 0x85, 0x13, 0x1c, 0xf9, 0x7f, 0x5a, - 0x46, 0xd9, 0xb4, 0x56, 0xe2, 0xea, 0xd2, 0xdf, 0xab, 0x95, 0xb8, 0xd4, 0xa4, 0x96, 0x53, 0xbf, - 0x59, 0x90, 0x33, 0xf2, 0x3d, 0xbe, 0x3e, 0x62, 0x37, 0x22, 0x9e, 0xe1, 0xbb, 0x1d, 0x32, 0xaf, - 0x88, 0x00, 0x2b, 0x46, 0xd7, 0xf1, 0x9d, 0x28, 0x83, 0x00, 0x0f, 0x08, 0x94, 0x97, 0xce, 0x80, - 0x92, 0x54, 0xa1, 0x58, 0x76, 0xd6, 0xb8, 0x45, 0xbe, 0xd3, 0x9b, 0x33, 0xdd, 0x59, 0x73, 0xd3, - 0xdf, 0x9c, 0x31, 0x16, 0xea, 0x9f, 0xcf, 0xf7, 0x48, 0xa2, 0xf7, 0x58, 0x4e, 0x22, 0x3f, 0x97, - 0xcf, 0x4a, 0x87, 0x77, 0x5c, 0xbd, 0xdd, 0x3e, 0x64, 0xe1, 0x1c, 0x6f, 0x57, 0xc0, 0x43, 0x14, - 0xce, 0xef, 0xe7, 0xb3, 0x72, 0xfb, 0x9d, 0x08, 0x67, 0x7f, 0x13, 0x64, 0xaa, 0x48, 0x1f, 0x63, - 0x9b, 0x5b, 0x56, 0x85, 0xfe, 0x7d, 0x7a, 0x7c, 0xa5, 0x89, 0xf4, 0x64, 0x08, 0x1f, 0x48, 0x4b, - 0xff, 0x20, 0x9f, 0x99, 0xc3, 0xf2, 0x44, 0xa6, 0x87, 0x29, 0xd3, 0x93, 0xa1, 0x7f, 0xa0, 0xa1, - 0x9f, 0x2a, 0xd3, 0x93, 0xb1, 0x7f, 0x20, 0x3d, 0xfd, 0xbd, 0x7c, 0x7a, 0x96, 0xd6, 0x23, 0x50, - 0xd2, 0xc3, 0x70, 0xca, 0xf4, 0xbb, 0xa1, 0x78, 0xa0, 0x6e, 0xe8, 0x3f, 0x80, 0x15, 0x95, 0x14, - 0xe8, 0x91, 0x8d, 0xfa, 0xef, 0x55, 0x81, 0x1e, 0xc2, 0x90, 0x7f, 0x9c, 0x05, 0xfa, 0x13, 0x85, - 0x64, 0x66, 0xe2, 0xc7, 0x75, 0x4d, 0x72, 0xf6, 0xb9, 0x26, 0xf9, 0xe5, 0xc8, 0x3b, 0x30, 0x1e, - 0xca, 0x52, 0x0e, 0x8c, 0x86, 0x37, 0x5e, 0x4d, 0x86, 0x6a, 0xbc, 0xcf, 0x70, 0x22, 0x82, 0x4f, - 0x9c, 0x5a, 0xfd, 0x6e, 0x21, 0x99, 0xde, 0xf9, 0xa4, 0x37, 0xf6, 0xd9, 0x1b, 0x77, 0xe0, 0xdc, - 0x4c, 0xd7, 0x71, 0xa8, 0xe5, 0xa5, 0x77, 0x0a, 0x1e, 0xde, 0x37, 0x39, 0x45, 0x23, 0xd9, 0x39, - 0x19, 0x85, 0x19, 0x5b, 0xf1, 0x20, 0x23, 0xce, 0x76, 0x20, 0x64, 0xdb, 0xe5, 0x14, 0x69, 0x6c, - 0xd3, 0x0b, 0xab, 0xbf, 0x93, 0x4f, 0x26, 0xe4, 0x3e, 0xe9, 0xfa, 0xfd, 0x75, 0xbd, 0xfa, 0xc5, - 0x42, 0x3c, 0x29, 0xf9, 0xc9, 0x02, 0xb1, 0xff, 0xee, 0xf0, 0x25, 0x89, 0xe3, 0x46, 0xfa, 0x0a, - 0x1f, 0x9e, 0xf5, 0x15, 0x3e, 0x5e, 0xfd, 0xe5, 0x62, 0x3c, 0xc1, 0xfb, 0x49, 0x77, 0x1c, 0x5d, - 0x77, 0x90, 0x65, 0x38, 0x23, 0xe6, 0x36, 0x1f, 0x84, 0x19, 0x32, 0xc4, 0xfc, 0xc5, 0x13, 0xed, - 0x89, 0x69, 0xb1, 0xeb, 0x52, 0xa7, 0xe1, 0xe9, 0xee, 0x83, 0x06, 0xa6, 0xd4, 0xd0, 0x52, 0x0b, - 0x32, 0x86, 0x62, 0x56, 0x8b, 0x32, 0x1c, 0x0c, 0x19, 0xfa, 0x13, 0x62, 0x82, 0x61, 0x5a, 0x41, - 0xf5, 0xd7, 0x73, 0x30, 0x11, 0xff, 0x1c, 0x32, 0x05, 0x83, 0xec, 0x77, 0x10, 0x29, 0x40, 0xca, - 0x00, 0xce, 0x39, 0x72, 0x2f, 0x02, 0x9f, 0x86, 0xbc, 0x0a, 0x43, 0xe8, 0xb0, 0x81, 0x05, 0xf2, - 0x61, 0x80, 0x86, 0xb0, 0x00, 0xa6, 0xa5, 0xe5, 0xc5, 0x42, 0x52, 0xf2, 0x26, 0x0c, 0x57, 0x43, - 0xcf, 0x34, 0x71, 0xe7, 0x85, 0x0e, 0xb1, 0x52, 0xc9, 0x90, 0x40, 0x93, 0xa9, 0xd5, 0x6f, 0xe7, - 0x43, 0x55, 0x3f, 0x31, 0x4d, 0x0f, 0x64, 0x9a, 0x7e, 0xb5, 0x00, 0xe7, 0xe2, 0xee, 0x0b, 0x27, - 0x07, 0x51, 0x62, 0x1e, 0xf8, 0x53, 0x70, 0x26, 0x2e, 0x9b, 0x0a, 0x93, 0x46, 0x7f, 0xef, 0x6b, - 0xb4, 0xa9, 0xed, 0xad, 0xc9, 0xa7, 0x93, 0x9e, 0x23, 0xac, 0xb2, 0xd4, 0x8b, 0xb5, 0xd4, 0x4a, - 0x52, 0x7b, 0xe6, 0x23, 0xf2, 0xa6, 0xe9, 0x31, 0xef, 0x99, 0x9f, 0xcb, 0x27, 0x7b, 0xe6, 0xe4, - 0x50, 0x4c, 0x4c, 0x28, 0xff, 0x30, 0xe7, 0xdf, 0x0f, 0x2f, 0x98, 0xae, 0x57, 0xb5, 0x1e, 0xea, - 0x2d, 0x33, 0x78, 0x74, 0x4d, 0x6e, 0xfa, 0xe9, 0xd2, 0x19, 0x52, 0x7a, 0xf7, 0x81, 0x1e, 0x5c, - 0x22, 0x5d, 0x7a, 0xcb, 0x74, 0x45, 0x6e, 0xeb, 0xa7, 0x13, 0x09, 0xd3, 0xfd, 0x62, 0xe4, 0xb2, - 0x74, 0xf7, 0x2f, 0xad, 0x51, 0xf2, 0x63, 0x02, 0x71, 0xd7, 0x3f, 0xb2, 0x68, 0xba, 0xae, 0x69, - 0xad, 0xc9, 0x19, 0x61, 0x71, 0xb5, 0x6c, 0x73, 0x78, 0x23, 0x9e, 0xa3, 0x37, 0x52, 0x40, 0xfd, - 0x57, 0x39, 0xb8, 0xc0, 0x38, 0x61, 0x20, 0x93, 0xc4, 0x87, 0x1d, 0xa8, 0xc3, 0xdb, 0x3d, 0x24, - 0x25, 0x34, 0xe0, 0x99, 0xe4, 0xe3, 0xa4, 0x18, 0x61, 0x8c, 0x7b, 0x0f, 0xd9, 0xef, 0xeb, 0xd5, - 0xc6, 0xf3, 0x8b, 0xdc, 0x16, 0xba, 0x6d, 0x5a, 0x06, 0x79, 0x02, 0xce, 0xde, 0xa9, 0xcf, 0x6a, - 0x8d, 0xdb, 0xd5, 0xa5, 0x4a, 0xe3, 0xce, 0x52, 0xbd, 0x36, 0x3b, 0x53, 0x9d, 0xab, 0xce, 0x56, - 0x26, 0xfa, 0xc8, 0x69, 0x18, 0x0f, 0x51, 0xf3, 0x77, 0x16, 0xcb, 0x4b, 0x13, 0x39, 0x72, 0x0a, - 0x46, 0x43, 0xe0, 0xf4, 0xf2, 0xca, 0x44, 0xfe, 0xf9, 0x8f, 0xc3, 0x30, 0xba, 0x6c, 0x73, 0x7f, - 0x2d, 0x32, 0x02, 0x83, 0xcb, 0xd3, 0xf5, 0x59, 0xed, 0x2e, 0x32, 0x01, 0x28, 0x55, 0x66, 0x97, - 0x18, 0xc3, 0xdc, 0xf3, 0xff, 0x4f, 0x0e, 0xa0, 0x3e, 0xb7, 0x52, 0x13, 0x84, 0xc3, 0x30, 0x50, - 0x5d, 0xba, 0x5b, 0x5e, 0xa8, 0x32, 0xba, 0x41, 0x28, 0x2e, 0xd7, 0x66, 0x59, 0x0d, 0x43, 0xd0, - 0x3f, 0xb3, 0xb0, 0x5c, 0x9f, 0x9d, 0xc8, 0x33, 0xa0, 0x36, 0x5b, 0xae, 0x4c, 0x14, 0x18, 0xf0, - 0x9e, 0x56, 0x5d, 0x99, 0x9d, 0x28, 0xb2, 0x3f, 0x17, 0xea, 0x2b, 0xe5, 0x95, 0x89, 0x7e, 0xf6, - 0xe7, 0x1c, 0xfe, 0x59, 0x62, 0xcc, 0xea, 0xb3, 0x2b, 0xf8, 0x63, 0x80, 0x35, 0x61, 0xce, 0xff, - 0x35, 0xc8, 0x50, 0x8c, 0x75, 0xa5, 0xaa, 0x4d, 0x0c, 0xb1, 0x1f, 0x8c, 0x25, 0xfb, 0x01, 0xac, - 0x71, 0xda, 0xec, 0xe2, 0xf2, 0xdd, 0xd9, 0x89, 0x61, 0xc6, 0x6b, 0xf1, 0x36, 0x03, 0x8f, 0xb0, - 0x3f, 0xb5, 0x45, 0xf6, 0xe7, 0x28, 0xe3, 0xa4, 0xcd, 0x96, 0x17, 0x6a, 0xe5, 0x95, 0xf9, 0x89, - 0x31, 0xd6, 0x1e, 0xe4, 0x39, 0xce, 0x4b, 0x2e, 0x95, 0x17, 0x67, 0x27, 0x26, 0x04, 0x4d, 0x65, - 0xa1, 0xba, 0x74, 0x7b, 0xe2, 0x14, 0x36, 0xe4, 0xbd, 0x45, 0xfc, 0x41, 0x58, 0x01, 0xfc, 0xeb, - 0xf4, 0xf3, 0x3f, 0x00, 0xa5, 0xe5, 0x3a, 0x9a, 0x49, 0xe7, 0xe1, 0xf4, 0x72, 0xbd, 0xb1, 0xf2, - 0x5e, 0x6d, 0x36, 0x26, 0xef, 0x53, 0x30, 0xea, 0x23, 0x16, 0xaa, 0x4b, 0x77, 0x3e, 0xc3, 0xa5, - 0xed, 0x83, 0x16, 0xcb, 0x33, 0xcb, 0xf5, 0x89, 0x3c, 0xeb, 0x15, 0x1f, 0x74, 0xaf, 0xba, 0x54, - 0x59, 0xbe, 0x57, 0x9f, 0x28, 0x3c, 0xff, 0x10, 0x46, 0x78, 0xee, 0xe9, 0x65, 0xc7, 0x5c, 0x33, - 0x2d, 0x72, 0x09, 0x9e, 0xa8, 0xcc, 0xde, 0xad, 0xce, 0xcc, 0x36, 0x96, 0xb5, 0xea, 0xcd, 0xea, - 0x52, 0xac, 0xa6, 0xb3, 0x70, 0x2a, 0x8a, 0x2e, 0xd7, 0xaa, 0x13, 0x39, 0x72, 0x0e, 0x48, 0x14, - 0x7c, 0xab, 0xbc, 0x38, 0x37, 0x91, 0x27, 0x0a, 0x9c, 0x89, 0xc2, 0xab, 0x4b, 0x2b, 0x77, 0x96, - 0x66, 0x27, 0x0a, 0xcf, 0xff, 0x95, 0x1c, 0x9c, 0x4d, 0xcd, 0x4f, 0x40, 0x54, 0x78, 0x6a, 0x76, - 0xa1, 0x5c, 0x5f, 0xa9, 0xce, 0xd4, 0x67, 0xcb, 0xda, 0xcc, 0x7c, 0x63, 0xa6, 0xbc, 0x32, 0x7b, - 0x73, 0x59, 0x7b, 0xaf, 0x71, 0x73, 0x76, 0x69, 0x56, 0x2b, 0x2f, 0x4c, 0xf4, 0x91, 0x67, 0x61, - 0x32, 0x83, 0xa6, 0x3e, 0x3b, 0x73, 0x47, 0xab, 0xae, 0xbc, 0x37, 0x91, 0x23, 0xcf, 0xc0, 0xa5, - 0x4c, 0x22, 0xf6, 0x7b, 0x22, 0x4f, 0x9e, 0x82, 0x0b, 0x59, 0x24, 0xef, 0x2e, 0x4c, 0x14, 0x9e, - 0xff, 0xd9, 0x1c, 0x90, 0x64, 0x80, 0x79, 0xf2, 0x34, 0x5c, 0x64, 0x7a, 0xd1, 0xc8, 0x6e, 0xe0, - 0x33, 0x70, 0x29, 0x95, 0x42, 0x6a, 0xde, 0x24, 0x3c, 0x99, 0x41, 0x22, 0x1a, 0x77, 0x11, 0x94, - 0x74, 0x02, 0x6c, 0xda, 0xaf, 0xe5, 0xe0, 0x6c, 0xaa, 0x8b, 0x24, 0xb9, 0x02, 0xcf, 0x95, 0x2b, - 0x8b, 0xac, 0x6f, 0x66, 0x56, 0xaa, 0xcb, 0x4b, 0xf5, 0xc6, 0xe2, 0x5c, 0xb9, 0xc1, 0xb4, 0xef, - 0x4e, 0x3d, 0xd6, 0x9b, 0x97, 0x41, 0xed, 0x41, 0x39, 0x33, 0x5f, 0x5e, 0xba, 0xc9, 0x86, 0x1f, - 0x79, 0x0e, 0x9e, 0xce, 0xa4, 0x9b, 0x5d, 0x2a, 0x4f, 0x2f, 0xcc, 0x56, 0x26, 0xf2, 0xe4, 0x63, - 0xf0, 0x4c, 0x26, 0x55, 0xa5, 0x5a, 0xe7, 0x64, 0x85, 0xe9, 0xca, 0xb7, 0xff, 0xa7, 0xa7, 0xfa, - 0xbe, 0xfd, 0x87, 0x4f, 0xe5, 0x7e, 0xff, 0x0f, 0x9f, 0xca, 0xfd, 0xb3, 0x3f, 0x7c, 0x2a, 0xf7, - 0xd9, 0xeb, 0x7b, 0x49, 0x1c, 0xc0, 0xa7, 0xa9, 0xd5, 0x12, 0xae, 0xe6, 0x2f, 0xff, 0xff, 0x01, - 0x00, 0x00, 0xff, 0xff, 0x7b, 0x68, 0x21, 0xe3, 0xaa, 0x8a, 0x01, 0x00, + // 17381 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x78, 0x25, 0xc9, + 0x75, 0x18, 0x86, 0xfb, 0xc0, 0x05, 0x70, 0xf0, 0x9c, 0x9a, 0x57, 0xcf, 0xec, 0xec, 0x62, 0xb7, + 0x97, 0x1c, 0xce, 0x2c, 0x77, 0x31, 0xdc, 0xd9, 0xd9, 0x5d, 0xee, 0x8b, 0xcb, 0x0b, 0x5c, 0x60, + 0x70, 0x67, 0xf0, 0xb8, 0xdb, 0x17, 0x33, 0xc3, 0xa5, 0x44, 0x5e, 0x35, 0x6e, 0xd7, 0x00, 0xbd, + 0x73, 0x6f, 0xf7, 0x55, 0x77, 0xdf, 0xc1, 0x60, 0x9d, 0x87, 0xe8, 0xc8, 0xb2, 0x64, 0x51, 0x34, + 0x43, 0x45, 0xa2, 0x2c, 0x3b, 0x09, 0x65, 0xc7, 0x89, 0x2c, 0xcb, 0x62, 0xe4, 0x28, 0x92, 0x28, + 0x89, 0xb1, 0x64, 0x3a, 0x0e, 0x2d, 0x7d, 0xd6, 0x27, 0x39, 0x89, 0xe3, 0x2f, 0x71, 0x40, 0x47, + 0x89, 0xff, 0xe0, 0x4b, 0xf2, 0x29, 0x89, 0xbe, 0x98, 0x51, 0xec, 0x7c, 0xf9, 0xea, 0x54, 0x75, + 0x77, 0xf5, 0xeb, 0xe2, 0xb9, 0xc6, 0x82, 0x83, 0x3f, 0x33, 0xb8, 0xe7, 0x9c, 0x3a, 0x55, 0x7d, + 0xea, 0x54, 0xd5, 0xa9, 0xaa, 0x53, 0xe7, 0xc0, 0x55, 0x8f, 0xb6, 0x68, 0xc7, 0x76, 0xbc, 0x6b, + 0x2d, 0xba, 0xa6, 0x37, 0x37, 0xaf, 0x79, 0x9b, 0x1d, 0xea, 0x5e, 0xa3, 0x0f, 0xa9, 0xe5, 0xf9, + 0xff, 0x4d, 0x75, 0x1c, 0xdb, 0xb3, 0x49, 0x89, 0xff, 0xba, 0x78, 0x66, 0xcd, 0x5e, 0xb3, 0x11, + 0x74, 0x8d, 0xfd, 0xc5, 0xb1, 0x17, 0x2f, 0xad, 0xd9, 0xf6, 0x5a, 0x8b, 0x5e, 0xc3, 0x5f, 0xab, + 0xdd, 0xfb, 0xd7, 0x5c, 0xcf, 0xe9, 0x36, 0x3d, 0x81, 0x9d, 0x8c, 0x63, 0x3d, 0xb3, 0x4d, 0x5d, + 0x4f, 0x6f, 0x77, 0x04, 0xc1, 0x53, 0x71, 0x82, 0x0d, 0x47, 0xef, 0x74, 0xa8, 0x23, 0x2a, 0xbf, + 0xf8, 0xb1, 0xa0, 0x9d, 0x7a, 0xb3, 0x49, 0x5d, 0xb7, 0x65, 0xba, 0xde, 0xb5, 0x87, 0x2f, 0x4a, + 0xbf, 0x04, 0xe1, 0x33, 0xe9, 0x1f, 0x84, 0xff, 0x0a, 0x92, 0x17, 0xd2, 0x49, 0xfc, 0x1a, 0x63, + 0x55, 0xab, 0x5f, 0xc9, 0xc3, 0xe0, 0x22, 0xf5, 0x74, 0x43, 0xf7, 0x74, 0x72, 0x09, 0xfa, 0xab, + 0x96, 0x41, 0x1f, 0x29, 0xb9, 0xa7, 0x73, 0x57, 0x0a, 0xd3, 0xa5, 0xed, 0xad, 0xc9, 0x3c, 0x35, + 0x35, 0x0e, 0x24, 0x4f, 0x42, 0x71, 0x65, 0xb3, 0x43, 0x95, 0xfc, 0xd3, 0xb9, 0x2b, 0x43, 0xd3, + 0x43, 0xdb, 0x5b, 0x93, 0xfd, 0x28, 0x34, 0x0d, 0xc1, 0xe4, 0x19, 0xc8, 0x57, 0x2b, 0x4a, 0x01, + 0x91, 0xa7, 0xb6, 0xb7, 0x26, 0x47, 0xbb, 0xa6, 0xf1, 0xbc, 0xdd, 0x36, 0x3d, 0xda, 0xee, 0x78, + 0x9b, 0x5a, 0xbe, 0x5a, 0x21, 0x97, 0xa1, 0x38, 0x63, 0x1b, 0x54, 0x29, 0x22, 0x11, 0xd9, 0xde, + 0x9a, 0x1c, 0x6b, 0xda, 0x06, 0x95, 0xa8, 0x10, 0x4f, 0x3e, 0x0d, 0xc5, 0x15, 0xb3, 0x4d, 0x95, + 0xfe, 0xa7, 0x73, 0x57, 0x86, 0xaf, 0x5f, 0x9c, 0xe2, 0xe2, 0x9b, 0xf2, 0xc5, 0x37, 0xb5, 0xe2, + 0xcb, 0x77, 0x7a, 0xe2, 0xdb, 0x5b, 0x93, 0x7d, 0xdb, 0x5b, 0x93, 0x45, 0x26, 0xf2, 0x2f, 0x7f, + 0x67, 0x32, 0xa7, 0x61, 0x49, 0xf2, 0x26, 0x0c, 0xcf, 0xb4, 0xba, 0xae, 0x47, 0x9d, 0x25, 0xbd, + 0x4d, 0x95, 0x12, 0x56, 0x78, 0x71, 0x7b, 0x6b, 0xf2, 0x5c, 0x93, 0x83, 0x1b, 0x96, 0xde, 0x96, + 0x2b, 0x96, 0xc9, 0xd5, 0x5f, 0xcf, 0xc1, 0x78, 0x9d, 0xba, 0xae, 0x69, 0x5b, 0x81, 0x6c, 0x3e, + 0x0a, 0x43, 0x02, 0x54, 0xad, 0xa0, 0x7c, 0x86, 0xa6, 0x07, 0xb6, 0xb7, 0x26, 0x0b, 0xae, 0x69, + 0x68, 0x21, 0x86, 0x7c, 0x02, 0x06, 0xee, 0x99, 0xde, 0xfa, 0xe2, 0x5c, 0x59, 0xc8, 0xe9, 0xdc, + 0xf6, 0xd6, 0x24, 0xd9, 0x30, 0xbd, 0xf5, 0x46, 0xfb, 0xbe, 0x2e, 0x55, 0xe8, 0x93, 0x91, 0x05, + 0x98, 0xa8, 0x39, 0xe6, 0x43, 0xdd, 0xa3, 0xb7, 0xe9, 0x66, 0xcd, 0x6e, 0x99, 0xcd, 0x4d, 0x21, + 0xc5, 0xa7, 0xb7, 0xb7, 0x26, 0x2f, 0x75, 0x38, 0xae, 0xf1, 0x80, 0x6e, 0x36, 0x3a, 0x88, 0x95, + 0x98, 0x24, 0x4a, 0xaa, 0xbf, 0x51, 0x82, 0x91, 0x3b, 0x2e, 0x75, 0x82, 0x76, 0x5f, 0x86, 0x22, + 0xfb, 0x2d, 0x9a, 0x8c, 0x32, 0xef, 0xba, 0xd4, 0x91, 0x65, 0xce, 0xf0, 0xe4, 0x2a, 0xf4, 0x2f, + 0xd8, 0x6b, 0xa6, 0x25, 0x9a, 0x7d, 0x7a, 0x7b, 0x6b, 0x72, 0xbc, 0xc5, 0x00, 0x12, 0x25, 0xa7, + 0x20, 0x9f, 0x82, 0x91, 0x6a, 0x9b, 0xe9, 0x90, 0x6d, 0xe9, 0x9e, 0xed, 0x88, 0xd6, 0xa2, 0x74, + 0x4d, 0x09, 0x2e, 0x15, 0x8c, 0xd0, 0x93, 0xd7, 0x01, 0xca, 0xf7, 0xea, 0x9a, 0xdd, 0xa2, 0x65, + 0x6d, 0x49, 0x28, 0x03, 0x96, 0xd6, 0x37, 0xdc, 0x86, 0x63, 0xb7, 0x68, 0x43, 0x77, 0xe4, 0x6a, + 0x25, 0x6a, 0x32, 0x0b, 0x63, 0x65, 0x1c, 0x15, 0x1a, 0xfd, 0xc1, 0x2e, 0x75, 0x3d, 0x57, 0xe9, + 0x7f, 0xba, 0x70, 0x65, 0x68, 0xfa, 0xc9, 0xed, 0xad, 0xc9, 0x0b, 0x7c, 0xbc, 0x34, 0x1c, 0x81, + 0x92, 0x58, 0xc4, 0x0a, 0x91, 0x69, 0x18, 0x2d, 0xbf, 0xdf, 0x75, 0x68, 0xd5, 0xa0, 0x96, 0x67, + 0x7a, 0x9b, 0x42, 0x43, 0x2e, 0x6d, 0x6f, 0x4d, 0x2a, 0x3a, 0x43, 0x34, 0x4c, 0x81, 0x91, 0x98, + 0x44, 0x8b, 0x90, 0x65, 0x38, 0x75, 0x73, 0xa6, 0x56, 0xa7, 0xce, 0x43, 0xb3, 0x49, 0xcb, 0xcd, + 0xa6, 0xdd, 0xb5, 0x3c, 0x65, 0x00, 0xf9, 0x3c, 0xb3, 0xbd, 0x35, 0xf9, 0xe4, 0x5a, 0xb3, 0xd3, + 0x70, 0x39, 0xb6, 0xa1, 0x73, 0xb4, 0xc4, 0x2c, 0x59, 0x96, 0x7c, 0x16, 0x46, 0x57, 0x1c, 0xa6, + 0x85, 0x46, 0x85, 0x32, 0xb8, 0x32, 0x88, 0xfa, 0x7f, 0x6e, 0x4a, 0xcc, 0x54, 0x1c, 0xea, 0xf7, + 0x2c, 0x6f, 0xac, 0xc7, 0x0b, 0x34, 0x0c, 0xc4, 0xc9, 0x8d, 0x8d, 0xb0, 0x22, 0x14, 0x14, 0xf6, + 0xf1, 0xa6, 0x43, 0x8d, 0x84, 0xb6, 0x0d, 0x61, 0x9b, 0xaf, 0x6e, 0x6f, 0x4d, 0x7e, 0xd4, 0x11, + 0x34, 0x8d, 0x9e, 0x6a, 0x97, 0xc9, 0x8a, 0xcc, 0xc2, 0x20, 0xd3, 0xa6, 0xdb, 0xa6, 0x65, 0x28, + 0xf0, 0x74, 0xee, 0xca, 0xd8, 0xf5, 0x09, 0xbf, 0xf5, 0x3e, 0x7c, 0xfa, 0xfc, 0xf6, 0xd6, 0xe4, + 0x69, 0xa6, 0x83, 0x8d, 0x07, 0xa6, 0x25, 0x4f, 0x11, 0x41, 0x51, 0x36, 0x8a, 0xa6, 0x6d, 0x0f, + 0x87, 0xee, 0x70, 0x38, 0x8a, 0x56, 0x6d, 0x2f, 0x3e, 0x6c, 0x7d, 0x32, 0x32, 0x03, 0xa3, 0xd3, + 0xb6, 0x57, 0xb5, 0x5c, 0x4f, 0xb7, 0x9a, 0xb4, 0x5a, 0x51, 0x46, 0xb0, 0x1c, 0xaa, 0x05, 0x2b, + 0x67, 0x0a, 0x4c, 0x23, 0x32, 0x29, 0x45, 0xcb, 0xa8, 0xff, 0xa2, 0x08, 0x63, 0xac, 0x4f, 0xa4, + 0xe1, 0x53, 0x66, 0x33, 0x01, 0x83, 0xb0, 0x5a, 0xdc, 0x8e, 0xde, 0xa4, 0x62, 0x24, 0xe1, 0x57, + 0x58, 0x3e, 0x50, 0xe2, 0x19, 0xa7, 0x27, 0x57, 0x61, 0x90, 0x83, 0xaa, 0x15, 0x31, 0xb8, 0x46, + 0xb7, 0xb7, 0x26, 0x87, 0x5c, 0x84, 0x35, 0x4c, 0x43, 0x0b, 0xd0, 0x4c, 0xbb, 0xf9, 0xdf, 0xf3, + 0xb6, 0xeb, 0x31, 0xe6, 0x62, 0x6c, 0xe1, 0x67, 0x88, 0x02, 0xeb, 0x02, 0x25, 0x6b, 0x77, 0xb4, + 0x10, 0x79, 0x0d, 0x80, 0x43, 0xca, 0x86, 0xe1, 0x88, 0x01, 0x76, 0x61, 0x7b, 0x6b, 0xf2, 0xac, + 0x60, 0xa1, 0x1b, 0x86, 0x3c, 0x3a, 0x25, 0x62, 0xd2, 0x86, 0x11, 0xfe, 0x6b, 0x41, 0x5f, 0xa5, + 0x2d, 0x3e, 0xba, 0x86, 0xaf, 0x5f, 0xf1, 0x3b, 0x31, 0x2a, 0x9d, 0x29, 0x99, 0x74, 0xd6, 0xf2, + 0x9c, 0xcd, 0xe9, 0x49, 0x31, 0x21, 0x9f, 0x17, 0x55, 0xb5, 0x10, 0x27, 0x4f, 0x05, 0x72, 0x19, + 0x36, 0x4f, 0xcf, 0xd9, 0xce, 0x86, 0xee, 0x18, 0xd4, 0x98, 0xde, 0x94, 0xe7, 0xe9, 0xfb, 0x3e, + 0xb8, 0xb1, 0x2a, 0xab, 0x9e, 0x4c, 0xce, 0x3a, 0x9d, 0x73, 0xab, 0x77, 0x57, 0x51, 0xe5, 0x06, + 0x12, 0xd2, 0x72, 0xbb, 0xab, 0x71, 0x35, 0x8b, 0x96, 0x61, 0x53, 0x01, 0x07, 0xdc, 0xa5, 0x0e, + 0x9b, 0xc4, 0x71, 0xd4, 0x89, 0xa9, 0x40, 0x30, 0x79, 0xc8, 0x31, 0x49, 0x1e, 0xa2, 0xc8, 0xc5, + 0xb7, 0xe1, 0x54, 0x42, 0x14, 0x64, 0x02, 0x0a, 0x0f, 0xe8, 0x26, 0x57, 0x17, 0x8d, 0xfd, 0x49, + 0xce, 0x40, 0xff, 0x43, 0xbd, 0xd5, 0x15, 0x4b, 0xa8, 0xc6, 0x7f, 0xbc, 0x9e, 0xff, 0x64, 0x8e, + 0xad, 0x38, 0x64, 0xc6, 0xb6, 0x2c, 0xda, 0xf4, 0xe4, 0x45, 0xe7, 0x15, 0x18, 0x5a, 0xb0, 0x9b, + 0x7a, 0x0b, 0xfb, 0x91, 0xeb, 0x9d, 0xb2, 0xbd, 0x35, 0x79, 0x86, 0x75, 0xe0, 0x54, 0x8b, 0x61, + 0xa4, 0x36, 0x85, 0xa4, 0x4c, 0x01, 0x34, 0xda, 0xb6, 0x3d, 0x8a, 0x05, 0xf3, 0xa1, 0x02, 0x60, + 0x41, 0x07, 0x51, 0xb2, 0x02, 0x84, 0xc4, 0xe4, 0x1a, 0x0c, 0xd6, 0xd8, 0x3a, 0xdb, 0xb4, 0x5b, + 0x42, 0xf9, 0x70, 0x29, 0xc0, 0xb5, 0x57, 0x1e, 0xab, 0x3e, 0x91, 0x3a, 0x0f, 0x63, 0x33, 0x2d, + 0x93, 0x5a, 0x9e, 0xdc, 0x6a, 0x36, 0x92, 0xcb, 0x6b, 0xd4, 0xf2, 0xe4, 0x56, 0xe3, 0x98, 0xd7, + 0x19, 0x54, 0x6e, 0x75, 0x40, 0xaa, 0xfe, 0x7e, 0x01, 0x2e, 0xdc, 0xee, 0xae, 0x52, 0xc7, 0xa2, + 0x1e, 0x75, 0xc5, 0x82, 0x1c, 0x70, 0x5d, 0x82, 0x53, 0x09, 0xa4, 0xe0, 0x8e, 0x0b, 0xe5, 0x83, + 0x00, 0xd9, 0x10, 0x6b, 0xbc, 0x3c, 0xdb, 0x26, 0x8a, 0x92, 0x79, 0x18, 0x0f, 0x81, 0xac, 0x11, + 0xae, 0x92, 0xc7, 0xa5, 0xe4, 0xa9, 0xed, 0xad, 0xc9, 0x8b, 0x12, 0x37, 0xd6, 0x6c, 0x59, 0x83, + 0xe3, 0xc5, 0xc8, 0x6d, 0x98, 0x08, 0x41, 0x37, 0x1d, 0xbb, 0xdb, 0x71, 0x95, 0x02, 0xb2, 0x9a, + 0xdc, 0xde, 0x9a, 0x7c, 0x42, 0x62, 0xb5, 0x86, 0x48, 0x79, 0x01, 0x8f, 0x17, 0x24, 0x3f, 0x9c, + 0x93, 0xb9, 0x89, 0x51, 0x58, 0xc4, 0x51, 0xf8, 0xaa, 0x3f, 0x0a, 0x33, 0x85, 0x34, 0x15, 0x2f, + 0x29, 0x06, 0x65, 0xac, 0x19, 0x89, 0x41, 0x99, 0xa8, 0xf1, 0xe2, 0x0c, 0x9c, 0x4d, 0xe5, 0xb5, + 0x27, 0xad, 0xfe, 0xe7, 0x05, 0x99, 0x4b, 0xcd, 0x36, 0x82, 0xce, 0x5c, 0x96, 0x3b, 0xb3, 0x66, + 0x1b, 0x38, 0xd5, 0xe7, 0xc2, 0xb5, 0x53, 0x6a, 0x6c, 0xc7, 0x36, 0xe2, 0xb3, 0x7e, 0xb2, 0x2c, + 0xf9, 0x3c, 0x9c, 0x4b, 0x00, 0xf9, 0x74, 0xcd, 0xb5, 0xff, 0xf2, 0xf6, 0xd6, 0xa4, 0x9a, 0xc2, + 0x35, 0x3e, 0x7b, 0x67, 0x70, 0x21, 0x3a, 0x9c, 0x97, 0xa4, 0x6e, 0x5b, 0x9e, 0x6e, 0x5a, 0xc2, + 0xb8, 0xe4, 0xa3, 0xe4, 0x63, 0xdb, 0x5b, 0x93, 0xcf, 0xca, 0x3a, 0xe8, 0xd3, 0xc4, 0x1b, 0x9f, + 0xc5, 0x87, 0x18, 0xa0, 0xa4, 0xa0, 0xaa, 0x6d, 0x7d, 0xcd, 0xb7, 0x98, 0xaf, 0x6c, 0x6f, 0x4d, + 0x7e, 0x24, 0xb5, 0x0e, 0x93, 0x51, 0xc9, 0x2b, 0x74, 0x16, 0x27, 0xa2, 0x01, 0x09, 0x71, 0x4b, + 0xb6, 0x41, 0xf1, 0x1b, 0xfa, 0x91, 0xbf, 0xba, 0xbd, 0x35, 0xf9, 0x94, 0xc4, 0xdf, 0xb2, 0x0d, + 0x1a, 0x6f, 0x7e, 0x4a, 0x69, 0xf5, 0xd7, 0x0b, 0xf0, 0x54, 0xbd, 0xbc, 0xb8, 0x50, 0x35, 0x7c, + 0x93, 0xa6, 0xe6, 0xd8, 0x0f, 0x4d, 0x43, 0x1a, 0xbd, 0xab, 0x70, 0x3e, 0x86, 0x9a, 0x45, 0x2b, + 0x2a, 0x30, 0xa6, 0xf1, 0xdb, 0x7c, 0x73, 0xa9, 0x23, 0x68, 0x1a, 0xdc, 0xd4, 0x8a, 0x2e, 0xda, + 0x59, 0x8c, 0x58, 0x1f, 0xc5, 0x50, 0xf5, 0x75, 0xdb, 0xf1, 0x9a, 0x5d, 0x4f, 0x28, 0x01, 0xf6, + 0x51, 0xa2, 0x0e, 0x57, 0x10, 0xf5, 0xa8, 0xc2, 0xe7, 0x43, 0x7e, 0x2c, 0x07, 0x13, 0x65, 0xcf, + 0x73, 0xcc, 0xd5, 0xae, 0x47, 0x17, 0xf5, 0x4e, 0xc7, 0xb4, 0xd6, 0x70, 0xac, 0x0f, 0x5f, 0x7f, + 0x33, 0x58, 0x23, 0x7b, 0x4a, 0x62, 0x2a, 0x5e, 0x5c, 0x1a, 0xa2, 0xba, 0x8f, 0x6a, 0xb4, 0x39, + 0x4e, 0x1e, 0xa2, 0xf1, 0x72, 0x6c, 0x88, 0xa6, 0xf2, 0xda, 0xd3, 0x10, 0xfd, 0x4a, 0x01, 0x2e, + 0x2d, 0x3f, 0xf0, 0x74, 0x8d, 0xba, 0x76, 0xd7, 0x69, 0x52, 0xf7, 0x4e, 0xc7, 0xd0, 0x3d, 0x1a, + 0x8e, 0xd4, 0x49, 0xe8, 0x2f, 0x1b, 0x06, 0x35, 0x90, 0x5d, 0x3f, 0xdf, 0xf6, 0xe9, 0x0c, 0xa0, + 0x71, 0x38, 0xf9, 0x28, 0x0c, 0x88, 0x32, 0xc8, 0xbd, 0x7f, 0x7a, 0x78, 0x7b, 0x6b, 0x72, 0xa0, + 0xcb, 0x41, 0x9a, 0x8f, 0x63, 0x64, 0x15, 0xda, 0xa2, 0x8c, 0xac, 0x10, 0x92, 0x19, 0x1c, 0xa4, + 0xf9, 0x38, 0xf2, 0x0e, 0x8c, 0x21, 0xdb, 0xa0, 0x3d, 0x62, 0xee, 0x3b, 0xe3, 0x4b, 0x57, 0x6e, + 0x2c, 0x5f, 0x9a, 0xb0, 0x35, 0x0d, 0xc7, 0x2f, 0xa0, 0xc5, 0x18, 0x90, 0x7b, 0x30, 0x21, 0x1a, + 0x11, 0x32, 0xed, 0xef, 0xc1, 0xf4, 0xec, 0xf6, 0xd6, 0xe4, 0x29, 0xd1, 0x7e, 0x89, 0x6d, 0x82, + 0x09, 0x63, 0x2c, 0x9a, 0x1d, 0x32, 0x2e, 0xed, 0xc4, 0x58, 0x7c, 0xb1, 0xcc, 0x38, 0xce, 0x44, + 0x7d, 0x17, 0x46, 0xe4, 0x82, 0xe4, 0x1c, 0x6e, 0xad, 0xf9, 0x38, 0xc1, 0x4d, 0xb9, 0x69, 0xe0, + 0x7e, 0xfa, 0x45, 0x18, 0xae, 0x50, 0xb7, 0xe9, 0x98, 0x1d, 0x66, 0x35, 0x08, 0x25, 0x1f, 0xdf, + 0xde, 0x9a, 0x1c, 0x36, 0x42, 0xb0, 0x26, 0xd3, 0xa8, 0xff, 0x77, 0x0e, 0xce, 0x31, 0xde, 0x65, + 0xd7, 0x35, 0xd7, 0xac, 0xb6, 0xbc, 0x6c, 0x3f, 0x0f, 0xa5, 0x3a, 0xd6, 0x27, 0x6a, 0x3a, 0xb3, + 0xbd, 0x35, 0x39, 0xc1, 0x5b, 0x20, 0xe9, 0xa1, 0xa0, 0x09, 0xf6, 0x95, 0xf9, 0x1d, 0xf6, 0x95, + 0xcc, 0xa4, 0xf5, 0x74, 0xc7, 0x33, 0xad, 0xb5, 0xba, 0xa7, 0x7b, 0x5d, 0x37, 0x62, 0xd2, 0x0a, + 0x4c, 0xc3, 0x45, 0x54, 0xc4, 0xa4, 0x8d, 0x14, 0x22, 0x6f, 0xc3, 0xc8, 0xac, 0x65, 0x84, 0x4c, + 0xf8, 0x84, 0xf8, 0x04, 0xb3, 0x34, 0x29, 0xc2, 0x93, 0x2c, 0x22, 0x05, 0xd4, 0xbf, 0x95, 0x03, + 0x85, 0x6f, 0x02, 0x17, 0x4c, 0xd7, 0x5b, 0xa4, 0xed, 0x55, 0x69, 0x76, 0x9a, 0xf3, 0x77, 0x95, + 0x0c, 0x27, 0xad, 0x45, 0x68, 0x0a, 0x88, 0x5d, 0x65, 0xcb, 0x74, 0x13, 0xdb, 0x8f, 0x58, 0x29, + 0x52, 0x85, 0x01, 0xce, 0x99, 0xdb, 0x12, 0xc3, 0xd7, 0x15, 0x5f, 0x11, 0xe2, 0x55, 0x73, 0x65, + 0x68, 0x73, 0x62, 0x79, 0x43, 0x23, 0xca, 0xab, 0x5f, 0x2b, 0xc0, 0x44, 0xbc, 0x10, 0xb9, 0x07, + 0x83, 0xb7, 0x6c, 0xd3, 0xa2, 0xc6, 0xb2, 0x85, 0x2d, 0xec, 0x7d, 0x38, 0xe2, 0xdb, 0xe2, 0xa7, + 0xdf, 0xc3, 0x32, 0x0d, 0xd9, 0x82, 0xc5, 0xb3, 0x92, 0x80, 0x19, 0xf9, 0x2c, 0x0c, 0x31, 0x1b, + 0xf0, 0x21, 0x72, 0xce, 0xef, 0xc8, 0xf9, 0x69, 0xc1, 0xf9, 0x8c, 0xc3, 0x0b, 0x25, 0x59, 0x87, + 0xec, 0x98, 0x5e, 0x69, 0x54, 0x77, 0x6d, 0x4b, 0xf4, 0x3c, 0xea, 0x95, 0x83, 0x10, 0x59, 0xaf, + 0x38, 0x0d, 0x33, 0x5d, 0xf9, 0xc7, 0x62, 0x37, 0x48, 0x7b, 0x17, 0x2e, 0xab, 0x78, 0x0f, 0x48, + 0xc4, 0xc4, 0x82, 0x71, 0x21, 0xd0, 0x75, 0xb3, 0x83, 0x56, 0x3f, 0xae, 0x6b, 0x63, 0xd7, 0x2f, + 0x4f, 0xf9, 0x87, 0x62, 0x53, 0xd2, 0x91, 0xda, 0xc3, 0x17, 0xa7, 0x16, 0x03, 0x72, 0xdc, 0x99, + 0xa2, 0x4e, 0xc6, 0x58, 0xc8, 0xbd, 0xdd, 0x8e, 0x90, 0xab, 0x3f, 0x92, 0x87, 0x17, 0xc2, 0x2e, + 0xd2, 0xe8, 0x43, 0x93, 0x6e, 0x84, 0x1c, 0xc5, 0x1e, 0x99, 0x0d, 0x31, 0x77, 0x66, 0x5d, 0xb7, + 0xd6, 0xa8, 0x41, 0xae, 0x42, 0xbf, 0x66, 0xb7, 0xa8, 0xab, 0xe4, 0xd0, 0x3c, 0xc4, 0xe9, 0xcb, + 0x61, 0x00, 0xf9, 0x90, 0x05, 0x29, 0x88, 0x0d, 0xa5, 0x15, 0x47, 0x37, 0x3d, 0x5f, 0x93, 0xca, + 0x49, 0x4d, 0xda, 0x45, 0x8d, 0x53, 0x9c, 0x07, 0x5f, 0x63, 0x50, 0xf0, 0x1e, 0x02, 0x64, 0xc1, + 0x73, 0x92, 0x8b, 0xaf, 0xc1, 0xb0, 0x44, 0xbc, 0xa7, 0x45, 0xe4, 0x1b, 0x45, 0x79, 0x6c, 0xf9, + 0xcd, 0x12, 0x63, 0xeb, 0x1a, 0x1b, 0x13, 0xae, 0xcb, 0xac, 0x18, 0x3e, 0xa8, 0x84, 0xe6, 0x23, + 0x28, 0xaa, 0xf9, 0x08, 0x22, 0x2f, 0xc1, 0x20, 0x67, 0x11, 0xec, 0x97, 0x71, 0xaf, 0xed, 0x20, + 0x2c, 0x6a, 0x0a, 0x04, 0x84, 0xe4, 0x17, 0x72, 0xf0, 0x64, 0x4f, 0x49, 0xa0, 0xf2, 0x0d, 0x5f, + 0x7f, 0x79, 0x5f, 0x62, 0x9c, 0x7e, 0x61, 0x7b, 0x6b, 0xf2, 0xaa, 0xa4, 0x19, 0x8e, 0x44, 0xd3, + 0x68, 0x72, 0x22, 0xa9, 0x5d, 0xbd, 0x9b, 0xc2, 0x8c, 0x55, 0x5e, 0xe9, 0x1c, 0x1e, 0x55, 0x59, + 0xcd, 0x4d, 0xbf, 0x91, 0xc5, 0xd0, 0x58, 0x15, 0xdf, 0x7b, 0xdf, 0x27, 0x49, 0xa9, 0x26, 0x83, + 0x0b, 0x69, 0xc2, 0x79, 0x8e, 0xa9, 0xe8, 0x9b, 0xcb, 0xf7, 0x17, 0x6d, 0xcb, 0x5b, 0xf7, 0x2b, + 0xe8, 0x97, 0xcf, 0x7a, 0xb0, 0x02, 0x43, 0xdf, 0x6c, 0xd8, 0xf7, 0x1b, 0x6d, 0x46, 0x95, 0x52, + 0x47, 0x16, 0x27, 0x36, 0xb1, 0x8b, 0x31, 0xee, 0x4f, 0x79, 0xa5, 0xf0, 0x24, 0xce, 0x9f, 0x17, + 0x92, 0x13, 0x5c, 0xac, 0x90, 0x5a, 0x85, 0x91, 0x05, 0xbb, 0xf9, 0x20, 0x50, 0x97, 0xd7, 0xa0, + 0xb4, 0xa2, 0x3b, 0x6b, 0xd4, 0x43, 0x59, 0x0c, 0x5f, 0x3f, 0x35, 0xc5, 0x4f, 0xb7, 0x19, 0x11, + 0x47, 0x4c, 0x8f, 0x89, 0xd9, 0xa7, 0xe4, 0xe1, 0x6f, 0x4d, 0x14, 0x50, 0xbf, 0xd3, 0x0f, 0x23, + 0xe2, 0x24, 0x16, 0x57, 0x0f, 0xf2, 0x7a, 0x78, 0xb6, 0x2d, 0xa6, 0xcb, 0xe0, 0x34, 0x2a, 0x38, + 0x45, 0x1b, 0x61, 0xcc, 0xfe, 0x60, 0x6b, 0x32, 0xb7, 0xbd, 0x35, 0xd9, 0xa7, 0x0d, 0x4a, 0x9b, + 0xd8, 0x70, 0x7d, 0x93, 0x16, 0x74, 0xf9, 0x6c, 0x35, 0x56, 0x96, 0xaf, 0x77, 0x6f, 0xc3, 0x80, + 0x68, 0x83, 0xd0, 0xb8, 0xf3, 0xe1, 0xd9, 0x49, 0xe4, 0x44, 0x39, 0x56, 0xda, 0x2f, 0x45, 0xde, + 0x84, 0x12, 0x3f, 0x4b, 0x10, 0x02, 0x38, 0x97, 0x7e, 0xf6, 0x12, 0x2b, 0x2e, 0xca, 0x90, 0x79, + 0x80, 0xf0, 0x1c, 0x21, 0x38, 0x40, 0x17, 0x1c, 0x92, 0x27, 0x0c, 0x31, 0x2e, 0x52, 0x59, 0xf2, + 0x0a, 0x8c, 0xac, 0x50, 0xa7, 0x6d, 0x5a, 0x7a, 0xab, 0x6e, 0xbe, 0xef, 0x9f, 0xa1, 0xe3, 0x42, + 0xef, 0x9a, 0xef, 0xcb, 0x23, 0x37, 0x42, 0x47, 0x3e, 0x97, 0xb6, 0x4f, 0x1f, 0xc0, 0x86, 0x3c, + 0xb3, 0xe3, 0x06, 0x36, 0xd6, 0x9e, 0x94, 0x6d, 0xfb, 0x3b, 0x30, 0x1a, 0xd9, 0xa2, 0x89, 0x43, + 0xd2, 0x27, 0x93, 0xac, 0xa5, 0xfd, 0x66, 0x8c, 0x6d, 0x94, 0x03, 0xd3, 0xe4, 0xaa, 0x65, 0x7a, + 0xa6, 0xde, 0x9a, 0xb1, 0xdb, 0x6d, 0xdd, 0x32, 0x94, 0xa1, 0x50, 0x93, 0x4d, 0x8e, 0x69, 0x34, + 0x39, 0x4a, 0xd6, 0xe4, 0x68, 0x21, 0x72, 0x1b, 0x26, 0x44, 0x1f, 0x6a, 0xb4, 0x69, 0x3b, 0xcc, + 0xf6, 0xc0, 0x33, 0x50, 0x71, 0x0c, 0xe0, 0x72, 0x5c, 0xc3, 0xf1, 0x91, 0xb2, 0x71, 0x1f, 0x2f, + 0x78, 0xab, 0x38, 0x38, 0x3c, 0x31, 0x12, 0x3f, 0xb6, 0x56, 0xff, 0x46, 0x01, 0x86, 0x05, 0x29, + 0x5b, 0xba, 0x4f, 0x14, 0xfc, 0x20, 0x0a, 0x9e, 0xaa, 0xa8, 0xa5, 0xc3, 0x52, 0x54, 0xf5, 0x8b, + 0xf9, 0x60, 0x36, 0xaa, 0x39, 0xa6, 0x75, 0xb0, 0xd9, 0xe8, 0x32, 0xc0, 0xcc, 0x7a, 0xd7, 0x7a, + 0xc0, 0xaf, 0xe7, 0xf2, 0xe1, 0xf5, 0x5c, 0xd3, 0xd4, 0x24, 0x0c, 0x79, 0x12, 0x8a, 0x15, 0xc6, + 0x9f, 0xf5, 0xcc, 0xc8, 0xf4, 0xd0, 0xb7, 0x39, 0xa7, 0xdc, 0x0b, 0x1a, 0x82, 0xd9, 0x66, 0x6e, + 0x7a, 0xd3, 0xa3, 0xdc, 0x7c, 0x2e, 0xf0, 0xcd, 0xdc, 0x2a, 0x03, 0x68, 0x1c, 0x4e, 0x6e, 0xc0, + 0xa9, 0x0a, 0x6d, 0xe9, 0x9b, 0x8b, 0x66, 0xab, 0x65, 0xba, 0xb4, 0x69, 0x5b, 0x86, 0x8b, 0x42, + 0x16, 0xd5, 0xb5, 0x5d, 0x2d, 0x49, 0x40, 0x54, 0x28, 0x2d, 0xdf, 0xbf, 0xef, 0x52, 0x0f, 0xc5, + 0x57, 0x98, 0x06, 0x36, 0x39, 0xdb, 0x08, 0xd1, 0x04, 0x46, 0xfd, 0x7a, 0x8e, 0xed, 0x96, 0xdc, + 0x07, 0x9e, 0xdd, 0x09, 0xb4, 0xfc, 0x40, 0x22, 0xb9, 0x1a, 0xda, 0x15, 0x79, 0xfc, 0xda, 0x71, + 0xf1, 0xb5, 0x03, 0xc2, 0xb6, 0x08, 0x2d, 0x8a, 0xd4, 0xaf, 0x2a, 0xec, 0xf0, 0x55, 0xea, 0x1f, + 0xe7, 0xe1, 0xbc, 0x68, 0xf1, 0x4c, 0xcb, 0xec, 0xac, 0xda, 0xba, 0x63, 0x68, 0xb4, 0x49, 0xcd, + 0x87, 0xf4, 0x78, 0x0e, 0xbc, 0xe8, 0xd0, 0x29, 0x1e, 0x60, 0xe8, 0x5c, 0xc7, 0x8d, 0x27, 0x93, + 0x0c, 0x1e, 0x30, 0x73, 0xa3, 0x62, 0x62, 0x7b, 0x6b, 0x72, 0xc4, 0xe0, 0x60, 0xbc, 0x62, 0xd0, + 0x64, 0x22, 0xa6, 0x24, 0x0b, 0xd4, 0x5a, 0xf3, 0xd6, 0x51, 0x49, 0xfa, 0xb9, 0x92, 0xb4, 0x10, + 0xa2, 0x09, 0x8c, 0xfa, 0xbf, 0xe5, 0xe1, 0x4c, 0x5c, 0xe4, 0x75, 0x6a, 0x19, 0x27, 0xf2, 0xfe, + 0x60, 0xe4, 0xfd, 0x27, 0x05, 0x78, 0x42, 0x94, 0xa9, 0xaf, 0xeb, 0x0e, 0x35, 0x2a, 0xa6, 0x43, + 0x9b, 0x9e, 0xed, 0x6c, 0x1e, 0x63, 0x03, 0xea, 0xf0, 0xc4, 0x7e, 0x03, 0x4a, 0xe2, 0xb8, 0x81, + 0xaf, 0x33, 0x63, 0x41, 0x4b, 0x10, 0x9a, 0x58, 0xa1, 0xf8, 0x51, 0x45, 0xac, 0xb3, 0x4a, 0xbb, + 0xe9, 0xac, 0x4f, 0xc2, 0x68, 0x20, 0x7a, 0xdc, 0xf8, 0x0e, 0x84, 0xd6, 0x96, 0xe1, 0x23, 0x70, + 0xef, 0xab, 0x45, 0x09, 0xb1, 0x36, 0x1f, 0x50, 0xad, 0xa0, 0x35, 0x34, 0x2a, 0x6a, 0x0b, 0xca, + 0x99, 0x86, 0x26, 0x13, 0xa9, 0x5b, 0x45, 0xb8, 0x98, 0xde, 0xed, 0x1a, 0xd5, 0x8d, 0x93, 0x5e, + 0xff, 0x9e, 0xec, 0x75, 0xf2, 0x0c, 0x14, 0x6b, 0xba, 0xb7, 0x2e, 0xae, 0xfb, 0xf1, 0x0e, 0xfa, + 0xbe, 0xd9, 0xa2, 0x8d, 0x8e, 0xee, 0xad, 0x6b, 0x88, 0x92, 0xe6, 0x0c, 0x40, 0x8e, 0x29, 0x73, + 0x86, 0xb4, 0xd8, 0x0f, 0x3f, 0x9d, 0xbb, 0x52, 0x4c, 0x5d, 0xec, 0xbf, 0x53, 0xcc, 0x9a, 0x57, + 0xee, 0x39, 0xa6, 0x47, 0x4f, 0x34, 0xec, 0x44, 0xc3, 0x0e, 0xa8, 0x61, 0xff, 0x28, 0x0f, 0xa3, + 0xc1, 0xa6, 0xe9, 0x3d, 0xda, 0x3c, 0x9a, 0xb5, 0x2a, 0xdc, 0xca, 0x14, 0x0e, 0xbc, 0x95, 0x39, + 0x88, 0x42, 0xa9, 0xc1, 0x11, 0x2b, 0x37, 0x0d, 0x50, 0x62, 0xfc, 0x88, 0x35, 0x38, 0x58, 0x7d, + 0x06, 0x06, 0x16, 0xf5, 0x47, 0x66, 0xbb, 0xdb, 0x16, 0x56, 0x3a, 0xba, 0xaf, 0xb5, 0xf5, 0x47, + 0x9a, 0x0f, 0x57, 0xff, 0x9b, 0x1c, 0x8c, 0x09, 0xa1, 0x0a, 0xe6, 0x07, 0x92, 0x6a, 0x28, 0x9d, + 0xfc, 0x81, 0xa5, 0x53, 0xd8, 0xbf, 0x74, 0xd4, 0xbf, 0x52, 0x00, 0x65, 0xce, 0x6c, 0xd1, 0x15, + 0x47, 0xb7, 0xdc, 0xfb, 0xd4, 0x11, 0xdb, 0xe9, 0x59, 0xc6, 0xea, 0x40, 0x1f, 0x28, 0x4d, 0x29, + 0xf9, 0x7d, 0x4d, 0x29, 0x1f, 0x87, 0x21, 0xd1, 0x98, 0xc0, 0x75, 0x12, 0x47, 0x8d, 0xe3, 0x03, + 0xb5, 0x10, 0xcf, 0x88, 0xcb, 0x9d, 0x8e, 0x63, 0x3f, 0xa4, 0x0e, 0xbf, 0x15, 0x13, 0xc4, 0xba, + 0x0f, 0xd4, 0x42, 0xbc, 0xc4, 0x99, 0xfa, 0xf6, 0xa2, 0xcc, 0x99, 0x3a, 0x5a, 0x88, 0x27, 0x57, + 0x60, 0x70, 0xc1, 0x6e, 0xea, 0x28, 0x68, 0x3e, 0xad, 0x8c, 0x6c, 0x6f, 0x4d, 0x0e, 0xb6, 0x04, + 0x4c, 0x0b, 0xb0, 0x8c, 0xb2, 0x62, 0x6f, 0x58, 0x2d, 0x5b, 0xe7, 0xce, 0x36, 0x83, 0x9c, 0xd2, + 0x10, 0x30, 0x2d, 0xc0, 0x32, 0x4a, 0x26, 0x73, 0x74, 0x62, 0x1a, 0x0c, 0x79, 0xde, 0x17, 0x30, + 0x2d, 0xc0, 0xaa, 0x5f, 0x2f, 0x32, 0xed, 0x75, 0xcd, 0xf7, 0x1f, 0xfb, 0x75, 0x21, 0x1c, 0x30, + 0xfd, 0xfb, 0x18, 0x30, 0x8f, 0xcd, 0x81, 0x9d, 0xfa, 0x2f, 0x06, 0x00, 0x84, 0xf4, 0x67, 0x4f, + 0x36, 0x87, 0x07, 0xd3, 0x9a, 0x0a, 0x9c, 0x9a, 0xb5, 0xd6, 0x75, 0xab, 0x49, 0x8d, 0xf0, 0xd8, + 0xb2, 0x84, 0x43, 0x1b, 0x9d, 0x2e, 0xa9, 0x40, 0x86, 0xe7, 0x96, 0x5a, 0xb2, 0x00, 0x79, 0x11, + 0x86, 0xab, 0x96, 0x47, 0x1d, 0xbd, 0xe9, 0x99, 0x0f, 0xa9, 0x98, 0x1a, 0xf0, 0x26, 0xda, 0x0c, + 0xc1, 0x9a, 0x4c, 0x43, 0x6e, 0xc0, 0x48, 0x4d, 0x77, 0x3c, 0xb3, 0x69, 0x76, 0x74, 0xcb, 0x73, + 0x95, 0x41, 0x9c, 0xd1, 0xd0, 0xc2, 0xe8, 0x48, 0x70, 0x2d, 0x42, 0x45, 0x3e, 0x07, 0x43, 0xb8, + 0x35, 0x45, 0xff, 0xf0, 0xa1, 0x1d, 0x2f, 0x2a, 0x9f, 0x0d, 0xdd, 0x11, 0xf9, 0xe9, 0x2b, 0xde, + 0x38, 0xc7, 0xef, 0x2a, 0x03, 0x8e, 0xe4, 0x33, 0x30, 0x30, 0x6b, 0x19, 0xc8, 0x1c, 0x76, 0x64, + 0xae, 0x0a, 0xe6, 0xe7, 0x42, 0xe6, 0x76, 0x27, 0xc6, 0xdb, 0x67, 0x97, 0x3e, 0xca, 0x86, 0x3f, + 0xb8, 0x51, 0x36, 0xf2, 0x01, 0x1c, 0x8b, 0x8f, 0x1e, 0xd6, 0xb1, 0xf8, 0xd8, 0x3e, 0x8f, 0xc5, + 0xd5, 0xf7, 0x61, 0x78, 0xba, 0x36, 0x17, 0x8c, 0xde, 0x0b, 0x50, 0xa8, 0x09, 0xcf, 0x88, 0x22, + 0xb7, 0x67, 0x3a, 0xa6, 0xa1, 0x31, 0x18, 0xb9, 0x0a, 0x83, 0x33, 0xe8, 0x6e, 0x27, 0x6e, 0x11, + 0x8b, 0x7c, 0xfd, 0x6b, 0x22, 0x0c, 0xbd, 0x6e, 0x7d, 0x34, 0xf9, 0x28, 0x0c, 0xd4, 0x1c, 0x7b, + 0xcd, 0xd1, 0xdb, 0x62, 0x0d, 0x46, 0xd7, 0x94, 0x0e, 0x07, 0x69, 0x3e, 0x4e, 0xfd, 0xc9, 0x9c, + 0x6f, 0xb6, 0xb3, 0x12, 0xf5, 0x2e, 0x1e, 0xcd, 0x63, 0xdd, 0x83, 0xbc, 0x84, 0xcb, 0x41, 0x9a, + 0x8f, 0x23, 0x57, 0xa1, 0x7f, 0xd6, 0x71, 0x6c, 0x47, 0xf6, 0xa9, 0xa7, 0x0c, 0x20, 0x5f, 0xf7, + 0x22, 0x05, 0x79, 0x15, 0x86, 0xf9, 0x9c, 0xc3, 0x4f, 0x34, 0x0b, 0xbd, 0x6e, 0x4a, 0x65, 0x4a, + 0xf5, 0x5b, 0x05, 0xc9, 0x66, 0xe3, 0x12, 0x7f, 0x0c, 0x6f, 0x05, 0x5e, 0x82, 0xc2, 0x74, 0x6d, + 0x4e, 0x4c, 0x80, 0xa7, 0xfd, 0xa2, 0x92, 0xaa, 0xc4, 0xca, 0x31, 0x6a, 0x72, 0x09, 0x8a, 0x35, + 0xa6, 0x3e, 0x25, 0x54, 0x8f, 0xc1, 0xed, 0xad, 0xc9, 0x62, 0x87, 0xe9, 0x0f, 0x42, 0x11, 0xcb, + 0x36, 0x33, 0x7c, 0xc7, 0xc4, 0xb1, 0xe1, 0x3e, 0xe6, 0x12, 0x14, 0xcb, 0xce, 0xda, 0x43, 0x31, + 0x6b, 0x21, 0x56, 0x77, 0xd6, 0x1e, 0x6a, 0x08, 0x25, 0xd7, 0x00, 0x34, 0xea, 0x75, 0x1d, 0x0b, + 0x9f, 0xbb, 0x0c, 0xe1, 0xf9, 0x1b, 0xce, 0x86, 0x0e, 0x42, 0x1b, 0x4d, 0xdb, 0xa0, 0x9a, 0x44, + 0xa2, 0xfe, 0xf5, 0xf0, 0x62, 0xa7, 0x62, 0xba, 0x0f, 0x4e, 0xba, 0x70, 0x0f, 0x5d, 0xa8, 0x8b, + 0x23, 0xce, 0x64, 0x27, 0x4d, 0x42, 0xff, 0x5c, 0x4b, 0x5f, 0x73, 0xb1, 0x0f, 0x85, 0xef, 0xda, + 0x7d, 0x06, 0xd0, 0x38, 0x3c, 0xd6, 0x4f, 0x83, 0x3b, 0xf7, 0xd3, 0x57, 0xfb, 0x83, 0xd1, 0xb6, + 0x44, 0xbd, 0x0d, 0xdb, 0x39, 0xe9, 0xaa, 0xdd, 0x76, 0xd5, 0x65, 0x18, 0xa8, 0x3b, 0x4d, 0xe9, + 0xe8, 0x02, 0xf7, 0x03, 0xae, 0xd3, 0xe4, 0xc7, 0x16, 0x3e, 0x92, 0xd1, 0x55, 0x5c, 0x0f, 0xe9, + 0x06, 0x42, 0x3a, 0xc3, 0xf5, 0x04, 0x9d, 0x40, 0x0a, 0xba, 0x9a, 0xed, 0x78, 0xa2, 0xe3, 0x02, + 0xba, 0x8e, 0xed, 0x78, 0x9a, 0x8f, 0x24, 0x1f, 0x07, 0x58, 0x99, 0xa9, 0xf9, 0xce, 0xfd, 0x43, + 0xa1, 0xef, 0xa1, 0xf0, 0xea, 0xd7, 0x24, 0x34, 0x59, 0x81, 0xa1, 0xe5, 0x0e, 0x75, 0xf8, 0x56, + 0x88, 0x3f, 0x60, 0xf9, 0x58, 0x4c, 0xb4, 0xa2, 0xdf, 0xa7, 0xc4, 0xff, 0x01, 0x39, 0x5f, 0x5f, + 0x6c, 0xff, 0xa7, 0x16, 0x32, 0x22, 0xaf, 0x42, 0xa9, 0xcc, 0xed, 0xbc, 0x61, 0x64, 0x19, 0x88, + 0x0c, 0xb7, 0xa0, 0x1c, 0xc5, 0xf7, 0xec, 0x3a, 0xfe, 0xad, 0x09, 0x72, 0xf5, 0x2a, 0x4c, 0xc4, + 0xab, 0x21, 0xc3, 0x30, 0x30, 0xb3, 0xbc, 0xb4, 0x34, 0x3b, 0xb3, 0x32, 0xd1, 0x47, 0x06, 0xa1, + 0x58, 0x9f, 0x5d, 0xaa, 0x4c, 0xe4, 0xd4, 0x5f, 0x94, 0x66, 0x10, 0xa6, 0x5a, 0x27, 0x57, 0xc3, + 0x07, 0xba, 0x6f, 0x99, 0xc0, 0xfb, 0x50, 0x3c, 0x31, 0x68, 0x9b, 0x9e, 0x47, 0x0d, 0xb1, 0x4a, + 0xe0, 0x7d, 0xa1, 0xf7, 0x48, 0x4b, 0xe0, 0xc9, 0xf3, 0x30, 0x8a, 0x30, 0x71, 0x45, 0xc8, 0xf7, + 0xc7, 0xa2, 0x80, 0xf3, 0x48, 0x8b, 0x22, 0xd5, 0xdf, 0x0b, 0x6f, 0x87, 0x17, 0xa8, 0x7e, 0x5c, + 0x6f, 0x14, 0x3f, 0x24, 0xfd, 0xa5, 0xfe, 0xcb, 0x22, 0x7f, 0x72, 0xc2, 0xdf, 0x27, 0x1e, 0x85, + 0x28, 0xc3, 0x23, 0xdd, 0xc2, 0x1e, 0x8e, 0x74, 0x9f, 0x87, 0xd2, 0x22, 0xf5, 0xd6, 0x6d, 0xdf, + 0xf1, 0x0b, 0x3d, 0xf4, 0xda, 0x08, 0x91, 0x3d, 0xf4, 0x38, 0x0d, 0x79, 0x00, 0xc4, 0x7f, 0x7c, + 0x18, 0x38, 0x7e, 0xfb, 0x47, 0xc8, 0xe7, 0x13, 0xfb, 0x94, 0x3a, 0x3e, 0x51, 0x46, 0x9f, 0xfe, + 0x33, 0x81, 0x63, 0xb9, 0xe4, 0x89, 0xf5, 0xa7, 0x5b, 0x93, 0x25, 0x4e, 0xa3, 0xa5, 0xb0, 0x25, + 0xef, 0xc0, 0xd0, 0xe2, 0x5c, 0x59, 0x3c, 0x44, 0xe4, 0x5e, 0x11, 0x17, 0x02, 0x29, 0xfa, 0x88, + 0x40, 0x24, 0xf8, 0xbe, 0xa7, 0x7d, 0x5f, 0x4f, 0xbe, 0x43, 0x0c, 0xb9, 0x30, 0x6d, 0xe1, 0x2f, + 0x85, 0xc4, 0xe9, 0x42, 0xa0, 0x2d, 0xd1, 0xf7, 0x43, 0x71, 0x59, 0x71, 0x6c, 0x4c, 0x5b, 0x06, + 0x0f, 0x30, 0xba, 0x97, 0xe1, 0x54, 0xb9, 0xd3, 0x69, 0x99, 0xd4, 0x40, 0x7d, 0xd1, 0xba, 0x2d, + 0xea, 0x0a, 0x97, 0x1f, 0x7c, 0x7c, 0xa2, 0x73, 0x64, 0x03, 0x9f, 0xbf, 0x36, 0x9c, 0x6e, 0xd4, + 0x3f, 0x33, 0x59, 0x56, 0xfd, 0xe9, 0x3c, 0x9c, 0x9b, 0x71, 0xa8, 0xee, 0xd1, 0xc5, 0xb9, 0x72, + 0xb9, 0x8b, 0x3e, 0x72, 0xad, 0x16, 0xb5, 0xd6, 0x8e, 0x66, 0x58, 0xbf, 0x01, 0x63, 0x41, 0x03, + 0xea, 0x4d, 0xbb, 0x43, 0xe5, 0x87, 0x5c, 0x4d, 0x1f, 0xd3, 0x70, 0x19, 0x4a, 0x8b, 0x91, 0x92, + 0xdb, 0x70, 0x3a, 0x80, 0x94, 0x5b, 0x2d, 0x7b, 0x43, 0xa3, 0x5d, 0x97, 0x3b, 0xe2, 0x0e, 0x72, + 0x47, 0xdc, 0x90, 0x83, 0xce, 0xf0, 0x0d, 0x87, 0x11, 0x68, 0x69, 0xa5, 0xd4, 0xaf, 0x15, 0xe0, + 0xfc, 0x5d, 0xbd, 0x65, 0x1a, 0xa1, 0x68, 0x34, 0xea, 0x76, 0x6c, 0xcb, 0xa5, 0xc7, 0x68, 0x94, + 0x46, 0x86, 0x42, 0xf1, 0x50, 0x86, 0x42, 0xb2, 0x8b, 0xfa, 0x0f, 0xdc, 0x45, 0xa5, 0x7d, 0x75, + 0xd1, 0xff, 0x9a, 0x83, 0x09, 0xff, 0xa1, 0x81, 0xfc, 0x68, 0x5c, 0xf2, 0x82, 0xc7, 0x23, 0xc4, + 0x98, 0xdf, 0x35, 0xe2, 0x49, 0x1d, 0x06, 0x66, 0x1f, 0x75, 0x4c, 0x87, 0xba, 0xbb, 0x70, 0x1a, + 0x7f, 0x52, 0x1c, 0x97, 0x9c, 0xa2, 0xbc, 0x48, 0xe2, 0xa4, 0x84, 0x83, 0xf1, 0xf9, 0x20, 0x7f, + 0x6a, 0x31, 0xed, 0xbf, 0x84, 0xe7, 0xcf, 0x07, 0xc5, 0x93, 0x8c, 0xc8, 0x7b, 0xd0, 0x90, 0x94, + 0x3c, 0x0b, 0x85, 0x95, 0x95, 0x05, 0x31, 0x93, 0x62, 0x04, 0x02, 0xcf, 0x93, 0xdf, 0x47, 0x32, + 0xac, 0xfa, 0x4f, 0xf3, 0x00, 0x4c, 0x15, 0xf8, 0x70, 0x3d, 0x12, 0x25, 0x9c, 0x86, 0x41, 0x5f, + 0xe0, 0x42, 0x0d, 0x83, 0x57, 0x02, 0xf1, 0x8e, 0x88, 0xd7, 0x1d, 0xbc, 0x08, 0x99, 0xf4, 0x1d, + 0xc9, 0xf9, 0x3d, 0x00, 0xee, 0x6c, 0xd0, 0x91, 0xdc, 0x77, 0x1f, 0xff, 0x38, 0x0c, 0x89, 0x19, + 0xcf, 0x8e, 0x9c, 0xff, 0x37, 0x7d, 0xa0, 0x16, 0xe2, 0x63, 0x53, 0x6b, 0xe9, 0x00, 0x0b, 0xb1, + 0x2f, 0x5e, 0xde, 0x2b, 0x27, 0xe2, 0x3d, 0x64, 0xf1, 0x7e, 0x49, 0x88, 0x97, 0xbf, 0x18, 0x3a, + 0xb6, 0xe2, 0x3d, 0xb4, 0xb3, 0x6f, 0xf5, 0x1f, 0xe5, 0x80, 0xb0, 0x66, 0xd5, 0x74, 0xd7, 0xdd, + 0xb0, 0x1d, 0x83, 0x3b, 0xa7, 0x1f, 0x89, 0x60, 0x0e, 0xef, 0xbe, 0xf2, 0x5b, 0x83, 0x70, 0x3a, + 0xe2, 0xf8, 0x7b, 0xcc, 0x27, 0xab, 0xab, 0xd1, 0xd1, 0xd4, 0xeb, 0xd5, 0xcb, 0x47, 0xe4, 0x0b, + 0xd1, 0xfe, 0xc8, 0x83, 0x37, 0xe9, 0x26, 0xf4, 0x05, 0x18, 0x11, 0x3f, 0xd8, 0x0a, 0xed, 0xdf, + 0x74, 0xe1, 0x28, 0x75, 0x19, 0x40, 0x8b, 0xa0, 0xc9, 0xcb, 0x30, 0xc4, 0x06, 0xcc, 0x1a, 0x06, + 0x2b, 0x19, 0x08, 0x5f, 0x94, 0x18, 0x3e, 0x50, 0x5e, 0x4f, 0x02, 0x4a, 0xe9, 0xdd, 0xd2, 0xe0, + 0x2e, 0xde, 0x2d, 0x7d, 0x1e, 0x86, 0xcb, 0x96, 0x65, 0x7b, 0xb8, 0x49, 0x77, 0xc5, 0xd5, 0x44, + 0xa6, 0x55, 0xfe, 0x2c, 0x3e, 0xc6, 0x0f, 0xe9, 0x53, 0xcd, 0x72, 0x99, 0x21, 0xb9, 0xee, 0xbf, + 0x8a, 0xa1, 0x8e, 0xf0, 0x2a, 0xc7, 0xeb, 0x19, 0x47, 0xc0, 0x92, 0x8f, 0x62, 0xb0, 0xf3, 0x46, + 0x6b, 0x8e, 0xdd, 0xb1, 0x5d, 0x6a, 0x70, 0x41, 0x0d, 0x87, 0xa1, 0x0d, 0x3a, 0x02, 0x81, 0xef, + 0xe6, 0x22, 0x81, 0x43, 0x22, 0x45, 0xc8, 0x7d, 0x38, 0xe3, 0x5f, 0x14, 0x07, 0x2f, 0x14, 0xab, + 0x15, 0x57, 0x19, 0xc1, 0x57, 0x49, 0x24, 0xae, 0x0c, 0xd5, 0xca, 0xf4, 0x53, 0xfe, 0xb5, 0x88, + 0xff, 0xc4, 0xb1, 0x61, 0x1a, 0x72, 0x57, 0xa7, 0xf2, 0x23, 0x3f, 0x00, 0xc3, 0x8b, 0xfa, 0xa3, + 0x4a, 0x57, 0x9c, 0xbd, 0x8c, 0xee, 0xfe, 0xf6, 0xa5, 0xad, 0x3f, 0x6a, 0x18, 0xa2, 0x5c, 0xcc, + 0xa6, 0x90, 0x59, 0x92, 0x06, 0x9c, 0xab, 0x39, 0x76, 0xdb, 0xf6, 0xa8, 0x11, 0x7b, 0xec, 0x37, + 0x1e, 0xbe, 0x0e, 0xee, 0x08, 0x8a, 0x46, 0x8f, 0x57, 0x7f, 0x19, 0x6c, 0x48, 0x1b, 0xc6, 0xcb, + 0xae, 0xdb, 0x6d, 0xd3, 0xf0, 0x86, 0x6a, 0x62, 0xc7, 0xcf, 0xf8, 0x98, 0xf0, 0x5a, 0x7e, 0x42, + 0xc7, 0xa2, 0xfc, 0x82, 0xaa, 0xe1, 0x99, 0x72, 0x8d, 0xf8, 0x2d, 0x71, 0xde, 0xb7, 0x8a, 0x83, + 0x63, 0x13, 0xe3, 0xda, 0xf9, 0x64, 0x63, 0x56, 0x4c, 0xaf, 0x45, 0xd5, 0x6f, 0xe6, 0x00, 0x42, + 0x01, 0x93, 0x17, 0xa2, 0x11, 0x91, 0x72, 0xe1, 0x45, 0x87, 0x88, 0x96, 0x10, 0x09, 0x81, 0x44, + 0x2e, 0x41, 0x11, 0x23, 0x6a, 0xe4, 0xc3, 0x83, 0xd5, 0x07, 0xa6, 0x65, 0x68, 0x08, 0x65, 0x58, + 0xe9, 0xe9, 0x3b, 0x62, 0xf1, 0x52, 0x9f, 0x5b, 0x85, 0x15, 0x18, 0xaf, 0x77, 0x57, 0xfd, 0xba, + 0xa5, 0x77, 0x7c, 0x18, 0xd8, 0xc3, 0xed, 0xae, 0x06, 0x8f, 0x5f, 0x23, 0x61, 0x53, 0xa2, 0x45, + 0xd4, 0xaf, 0xe7, 0x62, 0xb3, 0xe0, 0x11, 0x2e, 0x7a, 0x1f, 0x49, 0xfa, 0x69, 0x24, 0xa7, 0x25, + 0xf5, 0x0f, 0x0b, 0x30, 0x5c, 0xb3, 0x1d, 0x4f, 0x84, 0x28, 0x39, 0xde, 0xab, 0x90, 0xb4, 0x57, + 0x2a, 0xee, 0x61, 0xaf, 0x74, 0x09, 0x8a, 0x92, 0x8b, 0x32, 0xbf, 0x17, 0x31, 0x0c, 0x47, 0x43, + 0xe8, 0x07, 0xfc, 0xe4, 0x22, 0x79, 0x09, 0x3a, 0x70, 0x60, 0x57, 0x83, 0x1f, 0xca, 0x03, 0x7c, + 0xe6, 0xc5, 0x17, 0x1f, 0xe3, 0x2e, 0x55, 0xff, 0x72, 0x0e, 0xc6, 0xc5, 0xd5, 0xa2, 0x14, 0x0d, + 0x6d, 0xc0, 0xbf, 0x14, 0x96, 0x67, 0x12, 0x0e, 0xd2, 0x7c, 0x1c, 0x5b, 0xb4, 0x66, 0x1f, 0x99, + 0x1e, 0xde, 0xae, 0x48, 0xe1, 0xd0, 0xa8, 0x80, 0xc9, 0x8b, 0x96, 0x4f, 0x47, 0x5e, 0xf0, 0x2f, + 0x4d, 0x0b, 0xe1, 0x4a, 0xcd, 0x0a, 0xcc, 0xa6, 0x5e, 0x9c, 0xaa, 0xbf, 0x5a, 0x84, 0xe2, 0xec, + 0x23, 0xda, 0x3c, 0xe6, 0x5d, 0x23, 0x1d, 0xc5, 0x16, 0x0f, 0x78, 0x14, 0xbb, 0x1f, 0x2f, 0x90, + 0xb7, 0xc3, 0xfe, 0x2c, 0x45, 0xab, 0x8f, 0xf5, 0x7c, 0xbc, 0x7a, 0xbf, 0xa7, 0x8f, 0x9f, 0x13, + 0xd1, 0x7f, 0x59, 0x80, 0x42, 0x7d, 0xa6, 0x76, 0xa2, 0x37, 0x47, 0xaa, 0x37, 0xbd, 0x6f, 0xd9, + 0xd5, 0xe0, 0xe2, 0x6c, 0x30, 0xf4, 0x6b, 0x8d, 0xdd, 0x91, 0xfd, 0x49, 0x01, 0xc6, 0xea, 0x73, + 0x2b, 0x35, 0xe9, 0xec, 0xfa, 0x36, 0xf7, 0x3d, 0x44, 0x2f, 0x38, 0xde, 0xa5, 0x97, 0x12, 0x16, + 0xd8, 0x9d, 0xaa, 0xe5, 0xbd, 0x72, 0xe3, 0xae, 0xde, 0xea, 0x52, 0x3c, 0x2c, 0xe2, 0x9e, 0xca, + 0xae, 0xf9, 0x3e, 0xfd, 0x1a, 0x86, 0x46, 0xf0, 0x19, 0x90, 0x37, 0xa0, 0x70, 0x47, 0xf8, 0x90, + 0x64, 0xf1, 0x79, 0xe9, 0x3a, 0xe7, 0xc3, 0x26, 0xc1, 0x42, 0xd7, 0x34, 0x90, 0x03, 0x2b, 0xc5, + 0x0a, 0xdf, 0x14, 0x26, 0xc3, 0xae, 0x0a, 0xaf, 0xf9, 0x85, 0x6f, 0x56, 0x2b, 0xa4, 0x0e, 0xc3, + 0x35, 0xea, 0xb4, 0x4d, 0xec, 0x28, 0x7f, 0xce, 0xee, 0xcd, 0x84, 0xed, 0xad, 0x86, 0x3b, 0x61, + 0x21, 0x64, 0x26, 0x73, 0x21, 0xef, 0x02, 0x70, 0xab, 0x6a, 0x97, 0x11, 0x36, 0x9f, 0xc4, 0x9d, + 0x0a, 0x37, 0x86, 0x53, 0xac, 0x52, 0x89, 0x19, 0x79, 0x00, 0x13, 0x8b, 0xb6, 0x61, 0xde, 0x37, + 0xb9, 0xb3, 0x28, 0x56, 0x50, 0xda, 0xd9, 0x45, 0x8b, 0x19, 0xbf, 0x6d, 0xa9, 0x5c, 0x5a, 0x35, + 0x09, 0xc6, 0xea, 0xdf, 0xe9, 0x87, 0x22, 0xeb, 0xf6, 0x93, 0xf1, 0x7b, 0x90, 0xf1, 0x5b, 0x86, + 0x89, 0x7b, 0xb6, 0xf3, 0xc0, 0xb4, 0xd6, 0x02, 0x3f, 0x7e, 0xb1, 0x9b, 0x46, 0xdf, 0xa3, 0x0d, + 0x8e, 0x6b, 0x04, 0x2e, 0xff, 0x5a, 0x82, 0x7c, 0x87, 0x11, 0xfc, 0x1a, 0x00, 0x7f, 0x9d, 0x8f, + 0x34, 0x83, 0x61, 0x38, 0x0f, 0xfe, 0x76, 0x1f, 0x9f, 0x06, 0xc8, 0xe1, 0x3c, 0x42, 0x62, 0x72, + 0xd5, 0xf7, 0xde, 0x18, 0xc2, 0x97, 0x02, 0x78, 0x6c, 0x80, 0xde, 0x1b, 0xb2, 0x11, 0xc0, 0xfd, + 0x38, 0x6a, 0x00, 0xd2, 0x8d, 0x18, 0xc4, 0x04, 0x11, 0x99, 0x1c, 0x44, 0x00, 0xbd, 0x94, 0x0b, + 0x31, 0x4d, 0xe2, 0x41, 0x5e, 0x89, 0x5d, 0xd9, 0x93, 0x08, 0xb7, 0xcc, 0x1b, 0xfb, 0xd0, 0xe5, + 0x6b, 0x64, 0x27, 0x97, 0x2f, 0xf5, 0x6f, 0x16, 0x60, 0x98, 0x71, 0xab, 0x77, 0xdb, 0x6d, 0xdd, + 0xd9, 0x3c, 0x51, 0xe4, 0x83, 0x28, 0x72, 0x03, 0x4e, 0xc9, 0x2e, 0xfe, 0xcc, 0x74, 0xf5, 0x83, + 0x31, 0x05, 0x07, 0x56, 0x71, 0x02, 0x6e, 0x5b, 0xe2, 0xbc, 0xef, 0x09, 0x30, 0x9e, 0x86, 0xb8, + 0x5a, 0x92, 0x97, 0xfa, 0x53, 0x39, 0x98, 0x88, 0x43, 0x03, 0xdd, 0xcf, 0xa5, 0xea, 0xfe, 0xf3, + 0x30, 0x24, 0x2e, 0xfd, 0x75, 0x43, 0xf8, 0x20, 0x8e, 0x6d, 0x6f, 0x4d, 0x02, 0xbe, 0xb8, 0x6e, + 0x38, 0x54, 0x37, 0xb4, 0x90, 0x80, 0xbc, 0x0c, 0x23, 0xf8, 0xe3, 0x9e, 0x63, 0x7a, 0x1e, 0xe5, + 0x9d, 0x51, 0xe4, 0xf7, 0x18, 0xbc, 0xc0, 0x06, 0x47, 0x68, 0x11, 0x32, 0xf5, 0x77, 0xf3, 0x30, + 0x54, 0xef, 0xae, 0xba, 0x9b, 0xae, 0x47, 0xdb, 0xc7, 0x5c, 0x87, 0xfc, 0x63, 0x85, 0x62, 0xea, + 0xb1, 0xc2, 0xb3, 0xfe, 0xd0, 0x92, 0xce, 0xdb, 0x83, 0x8d, 0x81, 0xef, 0x47, 0x19, 0x6a, 0x51, + 0x69, 0xef, 0x5a, 0xa4, 0xfe, 0xed, 0x3c, 0x4c, 0xf0, 0xeb, 0xe6, 0x8a, 0xe9, 0x36, 0x0f, 0xe1, + 0x09, 0xcc, 0xd1, 0xcb, 0xf4, 0x60, 0x2e, 0x1a, 0xbb, 0x78, 0x58, 0xa4, 0x7e, 0x21, 0x0f, 0xc3, + 0xe5, 0xae, 0xb7, 0x5e, 0xf6, 0x70, 0x7e, 0x7b, 0x2c, 0xf7, 0xc8, 0xff, 0x20, 0x07, 0xe3, 0xac, + 0x21, 0x2b, 0xf6, 0x03, 0x6a, 0x1d, 0xc2, 0x71, 0xbd, 0x7c, 0xec, 0x9e, 0xdf, 0xe7, 0xb1, 0xbb, + 0x2f, 0xcb, 0xc2, 0xde, 0x64, 0x89, 0x97, 0x4c, 0x9a, 0xdd, 0xa2, 0xc7, 0xfb, 0x33, 0x0e, 0xf1, + 0x92, 0xc9, 0x17, 0xc8, 0x21, 0x5c, 0x6a, 0x7e, 0x6f, 0x09, 0xe4, 0x10, 0x4e, 0x64, 0xbf, 0x37, + 0x04, 0xf2, 0xad, 0x1c, 0x0c, 0x4d, 0xdb, 0xde, 0x31, 0x1f, 0xf8, 0xe2, 0x2b, 0x8e, 0xb7, 0x9a, + 0xfb, 0x5f, 0x71, 0xbc, 0x75, 0x53, 0xfd, 0x99, 0x3c, 0x9c, 0x11, 0x11, 0xfc, 0xc5, 0x19, 0xd8, + 0xc9, 0x74, 0x2c, 0x06, 0x5b, 0x52, 0x34, 0x27, 0xf3, 0x90, 0x10, 0xcd, 0xcf, 0x17, 0xe0, 0x0c, + 0x06, 0x1c, 0x66, 0x3b, 0xaa, 0xef, 0x01, 0x5b, 0x84, 0x34, 0xa3, 0xae, 0x03, 0x8b, 0x29, 0xae, + 0x03, 0x7f, 0xba, 0x35, 0xf9, 0xca, 0x9a, 0xe9, 0xad, 0x77, 0x57, 0xa7, 0x9a, 0x76, 0xfb, 0xda, + 0x9a, 0xa3, 0x3f, 0x34, 0xf9, 0xa5, 0xb9, 0xde, 0xba, 0x16, 0x26, 0xd6, 0xe9, 0x98, 0x22, 0x4d, + 0x4e, 0x1d, 0x77, 0x4a, 0x8c, 0xab, 0xef, 0x74, 0xe0, 0x02, 0xdc, 0xb2, 0x4d, 0x4b, 0x78, 0xe2, + 0x72, 0x43, 0xb7, 0xbe, 0xbd, 0x35, 0x79, 0xf6, 0x3d, 0xdb, 0xb4, 0x1a, 0x71, 0x77, 0xdc, 0xbd, + 0xd6, 0x17, 0xb2, 0xd6, 0xa4, 0x6a, 0xd4, 0xff, 0x3a, 0x07, 0x17, 0xa2, 0x5a, 0xfc, 0xbd, 0x60, + 0x3b, 0xfe, 0xa5, 0x3c, 0x9c, 0xbd, 0x89, 0xc2, 0x09, 0xdc, 0x9f, 0x4e, 0xe6, 0x2d, 0x31, 0x38, + 0x53, 0x64, 0x73, 0x62, 0x51, 0x66, 0xcb, 0xe6, 0x64, 0x52, 0x17, 0xb2, 0xf9, 0x87, 0x39, 0x38, + 0xbd, 0x5c, 0xad, 0xcc, 0x7c, 0x8f, 0x8c, 0xa8, 0xe4, 0xf7, 0x1c, 0x73, 0x83, 0x33, 0xf1, 0x3d, + 0xc7, 0xdc, 0xf4, 0xfc, 0x4a, 0x1e, 0x4e, 0xd7, 0xcb, 0x8b, 0x0b, 0xdf, 0x2b, 0x33, 0xf8, 0x8c, + 0xec, 0xab, 0xeb, 0x1f, 0x82, 0x09, 0x5b, 0x40, 0xfe, 0xcc, 0xbb, 0xd7, 0xb3, 0x7d, 0x78, 0x93, + 0x42, 0x39, 0xe6, 0x53, 0xf7, 0xa1, 0x08, 0x85, 0x69, 0x7e, 0x84, 0xfa, 0x98, 0x6b, 0xfe, 0xdf, + 0x2b, 0xc1, 0xf0, 0xed, 0xee, 0x2a, 0x15, 0x2e, 0x5d, 0x8f, 0xf5, 0xc9, 0xef, 0x75, 0x18, 0x16, + 0x62, 0xc0, 0x1b, 0x0e, 0x29, 0xe4, 0xa4, 0x08, 0x21, 0xc4, 0xa3, 0x7a, 0xc9, 0x44, 0xe4, 0x12, + 0x14, 0xef, 0x52, 0x67, 0x55, 0x7e, 0x8d, 0xfd, 0x90, 0x3a, 0xab, 0x1a, 0x42, 0xc9, 0x42, 0xf8, + 0xd0, 0xa4, 0x5c, 0xab, 0x62, 0xba, 0x23, 0x71, 0x69, 0x88, 0xf9, 0x9b, 0x02, 0x6f, 0x51, 0xbd, + 0x63, 0xf2, 0x44, 0x49, 0x72, 0x24, 0x88, 0x78, 0x49, 0xb2, 0x04, 0xa7, 0x64, 0x77, 0x41, 0x9e, + 0xeb, 0x67, 0x30, 0x85, 0x5d, 0x5a, 0x96, 0x9f, 0x64, 0x51, 0xf2, 0x36, 0x8c, 0xf8, 0x40, 0x74, + 0x7c, 0x1c, 0x0a, 0x13, 0x4c, 0x04, 0xac, 0x62, 0xf9, 0x00, 0x22, 0x05, 0x64, 0x06, 0x78, 0x89, + 0x01, 0x29, 0x0c, 0x62, 0x8e, 0xa4, 0x91, 0x02, 0xe4, 0x65, 0x64, 0x80, 0x8f, 0xa3, 0xd0, 0x61, + 0x6a, 0x18, 0x9f, 0x2a, 0xe3, 0x05, 0x90, 0x23, 0xe0, 0xfc, 0x41, 0x7a, 0x84, 0x8c, 0x2c, 0x03, + 0x84, 0x8e, 0x2d, 0x22, 0xec, 0xc7, 0x9e, 0x5d, 0x6e, 0x24, 0x16, 0xf2, 0x4d, 0xde, 0xe8, 0x7e, + 0x6e, 0xf2, 0xd4, 0x9f, 0x2e, 0xc0, 0x70, 0xb9, 0xd3, 0x09, 0x86, 0xc2, 0x0b, 0x50, 0x2a, 0x77, + 0x3a, 0x77, 0xb4, 0xaa, 0x9c, 0x00, 0x40, 0xef, 0x74, 0x1a, 0x5d, 0xc7, 0x94, 0x3d, 0xa9, 0x39, + 0x11, 0x99, 0x81, 0xd1, 0x72, 0xa7, 0x53, 0xeb, 0xae, 0xb6, 0xcc, 0xa6, 0x94, 0xbf, 0x8c, 0x67, + 0x78, 0xec, 0x74, 0x1a, 0x1d, 0xc4, 0xc4, 0x93, 0xd8, 0x45, 0xcb, 0x90, 0xcf, 0x63, 0xb0, 0x2c, + 0x91, 0x3e, 0x8b, 0x27, 0xe8, 0x51, 0x83, 0xd0, 0xff, 0x61, 0xdb, 0xa6, 0x02, 0x22, 0x9e, 0x22, + 0xe1, 0x92, 0x9f, 0xd8, 0x82, 0x55, 0x94, 0x48, 0x93, 0x15, 0xb2, 0x24, 0x9f, 0x80, 0x81, 0x72, + 0xa7, 0x23, 0xdd, 0x56, 0xa1, 0x63, 0x1b, 0x2b, 0x15, 0xcf, 0x50, 0x28, 0xc8, 0xc4, 0x67, 0x89, + 0xfb, 0x6d, 0xdb, 0xf1, 0x70, 0x48, 0x8d, 0x86, 0x9f, 0xe5, 0x5f, 0x88, 0xdb, 0x72, 0x7c, 0x1a, + 0x2d, 0x5a, 0xe6, 0xe2, 0x9b, 0x30, 0x16, 0x6d, 0xf1, 0x9e, 0xf2, 0x34, 0x7c, 0x37, 0x87, 0x52, + 0x39, 0xe6, 0xcf, 0x09, 0x5e, 0x82, 0x42, 0xb9, 0xd3, 0x11, 0x93, 0xda, 0xe9, 0x94, 0x4e, 0x8d, + 0x47, 0x1f, 0x28, 0x77, 0x3a, 0xfe, 0xa7, 0x1f, 0xf3, 0x77, 0x49, 0xfb, 0xfa, 0xf4, 0x6f, 0xf1, + 0x4f, 0x3f, 0xde, 0x6f, 0x86, 0xd4, 0x5f, 0x2d, 0xc0, 0x78, 0xb9, 0xd3, 0x39, 0xc9, 0xef, 0x70, + 0x58, 0x31, 0x0e, 0x5e, 0x04, 0x90, 0xe6, 0xd8, 0x81, 0xe0, 0xd5, 0xe4, 0xb0, 0x34, 0xbf, 0x2a, + 0x39, 0x4d, 0x22, 0xf2, 0xd5, 0x6f, 0x70, 0x4f, 0xea, 0xf7, 0x85, 0x02, 0x4e, 0x7c, 0xc7, 0x3d, + 0x5e, 0xdb, 0x87, 0xa5, 0xdb, 0x44, 0x1f, 0x94, 0xf6, 0xd4, 0x07, 0xbf, 0x13, 0x19, 0x3c, 0x98, + 0x2f, 0xe0, 0xa4, 0x17, 0xfa, 0x0f, 0x64, 0x5b, 0x8f, 0xc9, 0xc2, 0x14, 0x41, 0xa4, 0xfc, 0x9c, + 0x69, 0x22, 0xa4, 0x59, 0x93, 0xa1, 0x1a, 0xa6, 0xa1, 0xc5, 0x68, 0xfd, 0x3e, 0x1c, 0xd8, 0x53, + 0x1f, 0x6e, 0xe5, 0x31, 0x6c, 0x41, 0x10, 0x12, 0xed, 0xe0, 0x5b, 0x94, 0x6b, 0x00, 0xdc, 0x7d, + 0x21, 0xf0, 0xcf, 0x1f, 0xe5, 0xd1, 0x8f, 0x78, 0x2a, 0x35, 0x11, 0xfd, 0x28, 0x24, 0x09, 0xdc, + 0x9d, 0x0a, 0xa9, 0xee, 0x4e, 0x57, 0x61, 0x50, 0xd3, 0x37, 0xde, 0xe9, 0x52, 0x67, 0x53, 0xd8, + 0x44, 0x3c, 0xe2, 0xa8, 0xbe, 0xd1, 0xf8, 0x41, 0x06, 0xd4, 0x02, 0x34, 0x51, 0x83, 0xb8, 0x17, + 0x92, 0x5b, 0x09, 0x3f, 0x68, 0x0f, 0xa2, 0x5d, 0xec, 0x47, 0xd1, 0xc9, 0xeb, 0x50, 0x28, 0xdf, + 0xab, 0x0b, 0xc9, 0x06, 0x5d, 0x5b, 0xbe, 0x57, 0x17, 0xf2, 0xca, 0x2c, 0x7b, 0xaf, 0xae, 0x7e, + 0x21, 0x0f, 0x24, 0x49, 0x49, 0x5e, 0x81, 0x21, 0x84, 0xae, 0x31, 0x9d, 0x91, 0x73, 0xf0, 0x6e, + 0xb8, 0x0d, 0x07, 0xa1, 0x11, 0x0b, 0xd1, 0x27, 0x25, 0xaf, 0x61, 0x96, 0x73, 0x91, 0x05, 0x32, + 0x92, 0x83, 0x77, 0xc3, 0xf5, 0xf3, 0x82, 0xc7, 0x92, 0x9c, 0x0b, 0x62, 0x34, 0x2e, 0xef, 0xd5, + 0xe7, 0x6d, 0xd7, 0x13, 0xa2, 0xe6, 0xc6, 0xe5, 0x86, 0x8b, 0xc9, 0x9f, 0x23, 0xc6, 0x25, 0x27, + 0xc3, 0x04, 0x76, 0xf7, 0xea, 0xfc, 0x85, 0x98, 0xa1, 0xd9, 0x2d, 0xdf, 0x2a, 0xe5, 0x09, 0xec, + 0x36, 0xdc, 0x06, 0x7f, 0x5d, 0x66, 0x60, 0x7a, 0xf5, 0x48, 0x02, 0xbb, 0x48, 0x29, 0xf5, 0x27, + 0x06, 0x61, 0xa2, 0xa2, 0x7b, 0xfa, 0xaa, 0xee, 0x52, 0x69, 0x4b, 0x3e, 0xee, 0xc3, 0xfc, 0xcf, + 0x91, 0xe4, 0x60, 0xac, 0xa6, 0x7c, 0x4d, 0xbc, 0x00, 0x79, 0x23, 0xe4, 0x1b, 0xa4, 0x17, 0x96, + 0xf3, 0x15, 0xae, 0x36, 0x3a, 0x02, 0xac, 0x25, 0x08, 0xc9, 0xf3, 0x30, 0xec, 0xc3, 0xd8, 0x2e, + 0xa2, 0x10, 0xea, 0x8c, 0xb1, 0xca, 0x36, 0x11, 0x9a, 0x8c, 0x26, 0xaf, 0xc1, 0x88, 0xff, 0x53, + 0xb2, 0xcf, 0x79, 0xf2, 0xc5, 0xd5, 0xc4, 0x16, 0x4c, 0x26, 0x95, 0x8b, 0xe2, 0xfc, 0xd6, 0x1f, + 0x29, 0x1a, 0xcb, 0x6f, 0x18, 0x21, 0x25, 0x3f, 0x08, 0x63, 0xfe, 0x6f, 0xb1, 0xeb, 0xe0, 0xde, + 0x87, 0xcf, 0x07, 0xd9, 0xdb, 0x63, 0x62, 0x9d, 0x8a, 0x92, 0xf3, 0xfd, 0xc7, 0x13, 0x7e, 0xca, + 0x3e, 0x63, 0x35, 0xb9, 0xfd, 0x88, 0x55, 0x40, 0xaa, 0x70, 0xca, 0x87, 0x84, 0x1a, 0x3a, 0x10, + 0x6e, 0x3b, 0x8d, 0xd5, 0x46, 0xaa, 0x92, 0x26, 0x4b, 0x91, 0x16, 0x5c, 0x8a, 0x00, 0x0d, 0x77, + 0xdd, 0xbc, 0xef, 0x89, 0x3d, 0xa3, 0x08, 0xff, 0x2d, 0x72, 0xb4, 0x06, 0x5c, 0x39, 0x8d, 0x9f, + 0x6c, 0x39, 0x9a, 0x98, 0xad, 0x27, 0x37, 0x52, 0x87, 0x33, 0x3e, 0xfe, 0xe6, 0x4c, 0xad, 0xe6, + 0xd8, 0xef, 0xd1, 0xa6, 0x57, 0xad, 0x88, 0x3d, 0x37, 0x86, 0x85, 0x34, 0x56, 0x1b, 0x6b, 0xcd, + 0x0e, 0x53, 0x0a, 0x86, 0x8b, 0x32, 0x4f, 0x2d, 0x4c, 0xee, 0xc2, 0x59, 0x09, 0x2e, 0x65, 0x82, + 0x87, 0xf0, 0x50, 0x40, 0x70, 0x4d, 0x4f, 0x06, 0x9f, 0x5e, 0x9c, 0xbc, 0x09, 0xa3, 0x3e, 0x82, + 0x5f, 0x45, 0x0e, 0xe3, 0x55, 0x24, 0x0e, 0x49, 0x63, 0xb5, 0x11, 0x7f, 0xc8, 0x1c, 0x25, 0x96, + 0x35, 0x6a, 0x65, 0xb3, 0x43, 0x85, 0x5b, 0xb0, 0xaf, 0x51, 0xde, 0x66, 0x27, 0x55, 0x19, 0x19, + 0x29, 0x79, 0x3b, 0xd4, 0xa8, 0x65, 0xc7, 0x5c, 0x33, 0xf9, 0x76, 0xdc, 0x7f, 0xbb, 0xbc, 0xda, + 0xb0, 0x11, 0x98, 0xa6, 0x1f, 0x9c, 0xfc, 0x62, 0x19, 0x4e, 0xa7, 0xe8, 0xd8, 0x9e, 0x76, 0x8c, + 0x5f, 0xcc, 0x87, 0x8d, 0x38, 0xe6, 0xdb, 0xc6, 0x69, 0x18, 0xf4, 0xbf, 0x44, 0x18, 0x0f, 0x4a, + 0xd6, 0xd0, 0x8c, 0xf3, 0xf0, 0xf1, 0x11, 0x71, 0x1c, 0xf3, 0xad, 0xe4, 0x61, 0x88, 0xe3, 0xdb, + 0xb9, 0x50, 0x1c, 0xc7, 0x7c, 0x7b, 0xf9, 0xe3, 0xc5, 0x70, 0x4e, 0x3a, 0xd9, 0x63, 0x1e, 0x96, + 0x99, 0x1c, 0x3a, 0xd3, 0x96, 0xf6, 0xf0, 0x86, 0x58, 0x56, 0xcd, 0x81, 0xfd, 0xa9, 0x26, 0x79, + 0x13, 0x86, 0x6b, 0xb6, 0xeb, 0xad, 0x39, 0xd4, 0xad, 0x05, 0xe9, 0x2b, 0xf0, 0xfd, 0x79, 0x47, + 0x80, 0x1b, 0x9d, 0xc8, 0xec, 0x2f, 0x93, 0xab, 0xff, 0xb8, 0x90, 0xd0, 0x06, 0x6e, 0xb8, 0x1e, + 0x4b, 0x6d, 0x38, 0x84, 0xa1, 0x4e, 0xae, 0x87, 0xab, 0x20, 0xb7, 0xf0, 0xfb, 0xa5, 0xd8, 0x9c, + 0xab, 0xc2, 0xc0, 0x8f, 0x92, 0x90, 0xef, 0x83, 0xf3, 0x11, 0x40, 0x4d, 0x77, 0xf4, 0x36, 0xf5, + 0xc2, 0x54, 0xa1, 0x18, 0x6d, 0xcd, 0x2f, 0xdd, 0xe8, 0x04, 0x68, 0x39, 0xfd, 0x68, 0x06, 0x07, + 0x49, 0xb5, 0x06, 0xf6, 0xe0, 0xa7, 0xfd, 0xd5, 0x42, 0x68, 0xe8, 0x44, 0xa3, 0x26, 0x6b, 0xd4, + 0xed, 0xb6, 0xbc, 0xc7, 0xb7, 0x83, 0xf7, 0x97, 0x93, 0x66, 0x1e, 0xc6, 0xcb, 0xf7, 0xef, 0xd3, + 0xa6, 0xe7, 0x07, 0x83, 0x77, 0x45, 0x9c, 0x4c, 0xbe, 0xf1, 0x10, 0x28, 0x11, 0xdc, 0x5b, 0xee, + 0xd7, 0x78, 0x31, 0xf5, 0x9f, 0x14, 0x41, 0x09, 0x0c, 0xff, 0xe0, 0xbd, 0xe2, 0x11, 0x2e, 0xb2, + 0x1f, 0x8a, 0x5e, 0x31, 0xe1, 0x54, 0x28, 0x0c, 0xf1, 0x50, 0x4c, 0x24, 0xaf, 0x9f, 0x8c, 0x33, + 0x0b, 0x09, 0xf9, 0x5e, 0xe2, 0xa2, 0xd8, 0x4b, 0x90, 0xf0, 0x3d, 0x68, 0xc3, 0xe5, 0x2c, 0xb4, + 0x24, 0x57, 0xf2, 0xa5, 0x1c, 0x9c, 0xf1, 0x3b, 0x65, 0x79, 0x95, 0x19, 0xd5, 0x33, 0x76, 0xd7, + 0x0a, 0x5e, 0x51, 0xbd, 0x9e, 0x5d, 0x1d, 0xef, 0xa4, 0xa9, 0xb4, 0xc2, 0xbc, 0x25, 0x41, 0x44, + 0x98, 0x40, 0x21, 0x6c, 0xa4, 0x69, 0x34, 0x91, 0x48, 0x4b, 0xad, 0xf7, 0xe2, 0x4d, 0xb8, 0x90, + 0xc9, 0x72, 0x27, 0x23, 0xb6, 0x5f, 0x36, 0x62, 0xff, 0xdb, 0x5c, 0x38, 0x11, 0xc5, 0x84, 0x44, + 0xa6, 0x00, 0x42, 0x90, 0xd8, 0xd6, 0xe2, 0x23, 0xad, 0x50, 0x68, 0x9a, 0x44, 0x41, 0x96, 0xa1, + 0x24, 0xc4, 0xc2, 0xd3, 0x72, 0x7f, 0x7c, 0x87, 0x5e, 0x98, 0x92, 0xe5, 0x80, 0x5b, 0x56, 0xf1, + 0xcd, 0x82, 0xcd, 0xc5, 0xd7, 0x60, 0x78, 0xbf, 0xdf, 0xf5, 0xa5, 0x02, 0x10, 0x79, 0x0f, 0x7a, + 0x84, 0x06, 0xfa, 0x31, 0x9e, 0xc2, 0xae, 0xc0, 0x20, 0xfb, 0x04, 0x4c, 0x54, 0x23, 0x05, 0xa6, + 0xee, 0x0a, 0x98, 0x16, 0x60, 0xc3, 0xa8, 0x70, 0x03, 0xe9, 0x51, 0xe1, 0xd4, 0x9f, 0x2a, 0xc0, + 0x39, 0xb9, 0x43, 0x2a, 0x14, 0x73, 0x5d, 0x9c, 0x74, 0xca, 0x07, 0xd8, 0x29, 0x2a, 0x94, 0xf8, + 0xd6, 0x43, 0x24, 0x1d, 0xe1, 0xc7, 0x42, 0x08, 0xd1, 0x04, 0x46, 0xfd, 0x9f, 0xf3, 0x30, 0x1a, + 0x98, 0x77, 0xba, 0xe3, 0x3e, 0xc6, 0xdd, 0xf1, 0x49, 0x18, 0xc5, 0xb8, 0x5e, 0x6d, 0x6a, 0xf1, + 0xd8, 0x57, 0xfd, 0x52, 0x96, 0x20, 0x1f, 0x21, 0x12, 0xc2, 0x45, 0x08, 0x99, 0xf6, 0x73, 0xcb, + 0x4f, 0x8a, 0xb6, 0xc6, 0xcd, 0x3e, 0x0e, 0x57, 0xff, 0x6a, 0x01, 0x46, 0x7c, 0x29, 0x4f, 0x9b, + 0xc7, 0xf5, 0x9e, 0xe7, 0x68, 0x85, 0x7c, 0x0d, 0xa0, 0x66, 0x3b, 0x9e, 0xde, 0x5a, 0x0a, 0x35, + 0x1f, 0x0f, 0x48, 0x3b, 0x08, 0xe5, 0x65, 0x24, 0x12, 0x5c, 0xbf, 0x42, 0xb3, 0x9a, 0x4f, 0x4c, + 0x7c, 0xfd, 0x0a, 0xa0, 0x9a, 0x44, 0xa1, 0xfe, 0x66, 0x1e, 0xc6, 0xfd, 0x4e, 0x9a, 0x7d, 0x44, + 0x9b, 0xdd, 0xc7, 0x79, 0x6e, 0x8a, 0x4a, 0xbb, 0x7f, 0x47, 0x69, 0xab, 0xff, 0x97, 0x34, 0x91, + 0xcc, 0xb4, 0xec, 0x93, 0x89, 0xe4, 0x5f, 0x87, 0x8e, 0xab, 0x3f, 0x5c, 0x80, 0x33, 0xbe, 0xd4, + 0xe7, 0xba, 0x16, 0x1e, 0x2d, 0xcc, 0xe8, 0xad, 0xd6, 0xe3, 0xbc, 0x1b, 0x1f, 0xf6, 0x05, 0xb1, + 0x2c, 0x02, 0x65, 0x8a, 0xe4, 0x9c, 0xf7, 0x05, 0xb8, 0x61, 0x9b, 0x86, 0x26, 0x13, 0x91, 0xb7, + 0x61, 0xc4, 0xff, 0x59, 0x76, 0xd6, 0xfc, 0x2d, 0x38, 0x5e, 0x14, 0x04, 0x85, 0x74, 0x27, 0x12, + 0x5d, 0x23, 0x52, 0x40, 0xfd, 0xc2, 0x00, 0x5c, 0xbc, 0x67, 0x5a, 0x86, 0xbd, 0xe1, 0xfa, 0xb9, + 0x5d, 0x8f, 0xfd, 0x41, 0xd9, 0x51, 0xe7, 0x74, 0x7d, 0x07, 0xce, 0xc6, 0x45, 0xea, 0x04, 0x11, + 0xf7, 0x45, 0xef, 0x6c, 0x70, 0x82, 0x86, 0x9f, 0xe5, 0x55, 0xdc, 0xb6, 0x69, 0xe9, 0x25, 0xe3, + 0x69, 0x62, 0x07, 0x76, 0x93, 0x26, 0xf6, 0x39, 0x28, 0x55, 0xec, 0xb6, 0x6e, 0xfa, 0x71, 0x96, + 0x70, 0x14, 0x07, 0xf5, 0x22, 0x46, 0x13, 0x14, 0x8c, 0xbf, 0xa8, 0x18, 0xbb, 0x6c, 0x28, 0xe4, + 0xef, 0x17, 0x60, 0x56, 0x9a, 0x26, 0x13, 0x11, 0x1b, 0x46, 0x45, 0x75, 0xe2, 0x6e, 0x0c, 0x70, + 0xf3, 0xf4, 0xb2, 0x2f, 0xa3, 0x6c, 0xb5, 0x9a, 0x8a, 0x94, 0xe3, 0xdb, 0x28, 0x9e, 0xbd, 0x56, + 0x7c, 0x0c, 0xbf, 0x25, 0xd3, 0xa2, 0xfc, 0x25, 0x21, 0xe0, 0x24, 0x33, 0x9c, 0x14, 0x02, 0xce, + 0x32, 0x32, 0x11, 0x99, 0x85, 0x53, 0x18, 0x17, 0x3d, 0xd8, 0x4a, 0x31, 0x95, 0x18, 0x41, 0xa3, + 0x12, 0xaf, 0x5c, 0x78, 0x28, 0x75, 0xf6, 0x71, 0x8d, 0xa6, 0x40, 0x6b, 0xc9, 0x12, 0xe4, 0x02, + 0x14, 0x96, 0x16, 0xca, 0x78, 0x57, 0x33, 0xc8, 0x73, 0x92, 0x59, 0x2d, 0x5d, 0x63, 0xb0, 0x8b, + 0x9f, 0x06, 0x92, 0xfc, 0x9c, 0x3d, 0xdd, 0xc7, 0xfc, 0x7d, 0x69, 0xcb, 0x77, 0xdc, 0x3d, 0x6a, + 0x0e, 0x63, 0x22, 0x8c, 0xa4, 0x03, 0xec, 0xff, 0x20, 0xd3, 0x01, 0x96, 0x0e, 0x35, 0x1d, 0xa0, + 0xfa, 0x4b, 0x39, 0x38, 0x95, 0xc8, 0x1d, 0x40, 0x5e, 0x02, 0xe0, 0x10, 0x29, 0x46, 0x2b, 0x86, + 0x10, 0x0a, 0xf3, 0x09, 0x88, 0xe5, 0x31, 0x24, 0x23, 0xd7, 0x60, 0x90, 0xff, 0x12, 0x51, 0xca, + 0x92, 0x45, 0xba, 0x5d, 0xd3, 0xd0, 0x02, 0xa2, 0xb0, 0x16, 0xbc, 0x91, 0x2c, 0xa4, 0x16, 0xf1, + 0x36, 0x3b, 0x41, 0x2d, 0x8c, 0x4c, 0xfd, 0x89, 0x3c, 0x8c, 0x04, 0x0d, 0x2e, 0x1b, 0x47, 0xa5, + 0x73, 0x25, 0x91, 0x86, 0xa1, 0xb0, 0x53, 0x1a, 0x86, 0xd8, 0x7c, 0x2b, 0xf2, 0x2e, 0x1c, 0xde, + 0xab, 0xac, 0x2f, 0xe7, 0x61, 0x3c, 0xa8, 0xf5, 0x08, 0x2f, 0xbf, 0x3e, 0x44, 0x22, 0xf9, 0x52, + 0x0e, 0x94, 0x69, 0xb3, 0xd5, 0x32, 0xad, 0xb5, 0xaa, 0x75, 0xdf, 0x76, 0xda, 0x38, 0x21, 0x1e, + 0xdd, 0x11, 0xae, 0xfa, 0xe7, 0x73, 0x70, 0x4a, 0x34, 0x68, 0x46, 0x77, 0x8c, 0xa3, 0x3b, 0x1f, + 0x8b, 0xb7, 0xe4, 0xe8, 0xf4, 0x45, 0xfd, 0x46, 0x1e, 0x60, 0xc1, 0x6e, 0x3e, 0x38, 0xe6, 0x8f, + 0xba, 0xde, 0x80, 0x12, 0x77, 0x8b, 0x17, 0x1a, 0x7b, 0x4a, 0x3c, 0x5e, 0x62, 0x9f, 0xc6, 0x11, + 0xd3, 0x13, 0x62, 0x3e, 0x2e, 0x71, 0xcf, 0x7a, 0x25, 0xa7, 0x89, 0x22, 0xac, 0x52, 0x46, 0x27, + 0x16, 0x8c, 0xa0, 0x52, 0x06, 0x8b, 0x56, 0xba, 0xbd, 0x35, 0x59, 0x6c, 0xd9, 0xcd, 0x07, 0x1a, + 0xd2, 0xab, 0xff, 0x32, 0xc7, 0x65, 0x77, 0xcc, 0x9f, 0xa6, 0xfa, 0x9f, 0x5f, 0xdc, 0xe3, 0xe7, + 0xff, 0x85, 0x1c, 0x9c, 0xd1, 0x68, 0xd3, 0x7e, 0x48, 0x9d, 0xcd, 0x19, 0xdb, 0xa0, 0x37, 0xa9, + 0x45, 0x9d, 0xa3, 0x1a, 0x51, 0xbf, 0x85, 0x79, 0x6b, 0xc2, 0xc6, 0xdc, 0x71, 0xa9, 0x71, 0x7c, + 0x72, 0x0a, 0xa9, 0xbf, 0x32, 0x00, 0x4a, 0xaa, 0xd5, 0x7b, 0x6c, 0xcd, 0xb9, 0xcc, 0xad, 0x4c, + 0xf1, 0xb0, 0xb6, 0x32, 0xfd, 0x7b, 0xdb, 0xca, 0x94, 0xf6, 0xba, 0x95, 0x19, 0xd8, 0xcd, 0x56, + 0xa6, 0x1d, 0xdf, 0xca, 0x0c, 0xe2, 0x56, 0xe6, 0xa5, 0x9e, 0x5b, 0x99, 0x59, 0xcb, 0xd8, 0xe7, + 0x46, 0xe6, 0xd8, 0xe6, 0xbb, 0xde, 0xcf, 0x0e, 0xec, 0x0a, 0x9b, 0x14, 0x9b, 0xb6, 0x63, 0x50, + 0x43, 0x6c, 0xbc, 0xf0, 0xd4, 0xdf, 0x11, 0x30, 0x2d, 0xc0, 0x26, 0x92, 0x87, 0x8f, 0xee, 0x26, + 0x79, 0xf8, 0x21, 0xec, 0xbf, 0xbe, 0x98, 0x87, 0x53, 0x33, 0xd4, 0xf1, 0x78, 0x2c, 0xda, 0xc3, + 0x70, 0x89, 0x2b, 0xc3, 0xb8, 0xc4, 0x10, 0x2d, 0xf2, 0x7c, 0xe8, 0xe6, 0xd7, 0xa4, 0x8e, 0x17, + 0xf7, 0x12, 0x8c, 0xd3, 0xb3, 0xea, 0xfd, 0x04, 0x7e, 0x62, 0xec, 0x06, 0xd5, 0xfb, 0x70, 0x2e, + 0x48, 0x53, 0xfc, 0xd2, 0x02, 0x7a, 0x29, 0x27, 0x5f, 0x71, 0xef, 0x39, 0xf9, 0xd4, 0x5f, 0xcc, + 0xc1, 0x65, 0x8d, 0x5a, 0x74, 0x43, 0x5f, 0x6d, 0x51, 0xa9, 0x59, 0x62, 0x65, 0x60, 0xb3, 0x86, + 0xe9, 0xb6, 0x75, 0xaf, 0xb9, 0x7e, 0x20, 0x19, 0xcd, 0xc1, 0x88, 0x3c, 0x7f, 0xed, 0x61, 0x6e, + 0x8b, 0x94, 0x53, 0x7f, 0xa5, 0x08, 0x03, 0xd3, 0xb6, 0x77, 0xcb, 0x3e, 0x60, 0x92, 0xc8, 0x70, + 0xca, 0xcf, 0xef, 0xe1, 0xac, 0xe7, 0x13, 0x58, 0xb9, 0x94, 0x37, 0x03, 0x5d, 0x48, 0x57, 0xed, + 0x44, 0x7e, 0x11, 0x9f, 0x6c, 0x8f, 0xe9, 0x21, 0x5f, 0x81, 0x21, 0x0c, 0x21, 0x23, 0x9d, 0xc6, + 0xa2, 0x83, 0xb6, 0xc7, 0x80, 0xf1, 0x3a, 0x42, 0x52, 0xf2, 0x7d, 0x91, 0xe0, 0xb9, 0xa5, 0x83, + 0xa7, 0x93, 0x94, 0xe3, 0xe8, 0xbe, 0xc4, 0x2f, 0xf2, 0xb0, 0x4d, 0x52, 0xea, 0x1d, 0x3c, 0x45, + 0x89, 0x35, 0x29, 0x20, 0x3c, 0xc4, 0x54, 0x8f, 0x33, 0x30, 0x3a, 0x6d, 0x7b, 0x92, 0x33, 0xf0, + 0x50, 0xf8, 0x96, 0x94, 0x49, 0x3e, 0xdd, 0x13, 0x38, 0x5a, 0x46, 0xfd, 0x93, 0x22, 0x8c, 0xf8, + 0x3f, 0x8f, 0x48, 0x77, 0x5e, 0x80, 0xd2, 0xbc, 0x2d, 0x65, 0x1f, 0x41, 0x07, 0xe2, 0x75, 0xdb, + 0x8d, 0x79, 0x46, 0x0b, 0x22, 0x26, 0xf5, 0x25, 0xdb, 0x90, 0xdd, 0xdf, 0x51, 0xea, 0x96, 0x6d, + 0x24, 0xde, 0x20, 0x07, 0x84, 0xe4, 0x32, 0x14, 0xf1, 0xe5, 0x80, 0x74, 0x90, 0x1f, 0x7b, 0x2d, + 0x80, 0x78, 0x49, 0x2b, 0x4b, 0x7b, 0xd5, 0xca, 0x81, 0xfd, 0x6a, 0xe5, 0xe0, 0xe1, 0x6a, 0xe5, + 0xbb, 0x30, 0x82, 0x35, 0xf9, 0xc9, 0x0b, 0x77, 0x5e, 0x58, 0x2f, 0x88, 0xb5, 0x6f, 0x94, 0xb7, + 0x5b, 0xa4, 0x30, 0xc4, 0x25, 0x2f, 0xc2, 0x2a, 0xa6, 0xbb, 0x70, 0x80, 0xed, 0xf4, 0x3f, 0xce, + 0xc1, 0xc0, 0x1d, 0xeb, 0x81, 0x65, 0x6f, 0x1c, 0x4c, 0xe3, 0x5e, 0x82, 0x61, 0xc1, 0x46, 0x5a, + 0x5d, 0xf0, 0x59, 0x79, 0x97, 0x83, 0x1b, 0xc8, 0x49, 0x93, 0xa9, 0xc8, 0x9b, 0x41, 0x21, 0x7c, + 0x1c, 0x54, 0x08, 0xf3, 0xf7, 0xf8, 0x85, 0x9a, 0xd1, 0x04, 0x1e, 0x32, 0x39, 0xb9, 0x04, 0xc5, + 0x0a, 0x6b, 0xaa, 0x14, 0xc8, 0x97, 0x35, 0x45, 0x43, 0xa8, 0xfa, 0xc5, 0x22, 0x8c, 0xc5, 0x0e, + 0xbe, 0x9e, 0x83, 0x21, 0x71, 0xf0, 0x64, 0xfa, 0x19, 0x45, 0xf0, 0xf1, 0x50, 0x00, 0xd4, 0x06, + 0xf9, 0x9f, 0x55, 0x83, 0x7c, 0x0a, 0x06, 0x6c, 0x17, 0x17, 0x45, 0xfc, 0x96, 0xb1, 0x70, 0x08, + 0x2d, 0xd7, 0x59, 0xdb, 0xf9, 0xe0, 0x10, 0x24, 0xb2, 0x46, 0xda, 0x2e, 0x7e, 0xda, 0x0d, 0x18, + 0xd2, 0x5d, 0x97, 0x7a, 0x0d, 0x4f, 0x5f, 0x93, 0x93, 0x8c, 0x04, 0x40, 0x79, 0x74, 0x20, 0x70, + 0x45, 0x5f, 0x23, 0x9f, 0x86, 0xd1, 0xa6, 0x43, 0x71, 0xd9, 0xd4, 0x5b, 0xac, 0x95, 0x92, 0x59, + 0x1b, 0x41, 0xc8, 0xf7, 0x27, 0x21, 0xa2, 0x6a, 0x90, 0xbb, 0x30, 0x2a, 0x3e, 0x87, 0x7b, 0xee, + 0xe3, 0x40, 0x1b, 0x0b, 0x97, 0x31, 0x2e, 0x12, 0xee, 0xbb, 0x2f, 0x1e, 0x70, 0xc8, 0xe4, 0x32, + 0x5f, 0x43, 0x22, 0x25, 0xcb, 0x40, 0x36, 0xe8, 0x6a, 0x43, 0xef, 0x7a, 0xeb, 0xac, 0x2e, 0x1e, + 0x23, 0x5f, 0x64, 0x03, 0xc5, 0x57, 0x0f, 0x49, 0xac, 0xfc, 0x18, 0x64, 0x83, 0xae, 0x96, 0x23, + 0x48, 0x72, 0x0f, 0xce, 0x26, 0x8b, 0xb0, 0x4f, 0xe6, 0x97, 0x03, 0xcf, 0x6e, 0x6f, 0x4d, 0x4e, + 0xa6, 0x12, 0x48, 0x6c, 0x4f, 0x27, 0xd8, 0x56, 0x8d, 0x5b, 0xc5, 0xc1, 0x81, 0x89, 0x41, 0x6d, + 0x8c, 0x95, 0xf5, 0x4d, 0x48, 0xd3, 0x50, 0x7f, 0x2f, 0xc7, 0x4c, 0x45, 0xf6, 0x41, 0x98, 0x0e, + 0x9d, 0xe9, 0x7a, 0x7b, 0x8f, 0xba, 0xde, 0x0e, 0x13, 0x97, 0x96, 0xdc, 0x1e, 0xb3, 0xab, 0x26, + 0xb0, 0x64, 0x0a, 0x4a, 0x86, 0x7c, 0x6a, 0x76, 0x2e, 0xda, 0x09, 0x7e, 0x3d, 0x9a, 0xa0, 0x22, + 0x57, 0xa0, 0xc8, 0x96, 0xac, 0xf8, 0x96, 0x59, 0xb6, 0x2e, 0x34, 0xa4, 0x50, 0x7f, 0x28, 0x0f, + 0x23, 0xd2, 0xd7, 0x5c, 0x3f, 0xd0, 0xe7, 0xbc, 0xbe, 0xbb, 0x66, 0xfa, 0x4e, 0x2f, 0xb8, 0x97, + 0xf2, 0x9b, 0x7c, 0x23, 0x10, 0xc5, 0xae, 0x2e, 0xa4, 0x84, 0x60, 0x5e, 0x11, 0x1f, 0x5a, 0xda, + 0xfd, 0xf6, 0x91, 0xd1, 0xdf, 0x2a, 0x0e, 0xe6, 0x27, 0x0a, 0xb7, 0x8a, 0x83, 0xc5, 0x89, 0x7e, + 0x0c, 0xe6, 0x85, 0xf1, 0xb3, 0xf9, 0xde, 0xdc, 0xba, 0x6f, 0xae, 0x1d, 0xf3, 0xb7, 0x23, 0x87, + 0x1b, 0xe8, 0x2c, 0x26, 0x9b, 0x63, 0xfe, 0x90, 0xe4, 0x03, 0x95, 0xcd, 0x49, 0xa2, 0x53, 0x21, + 0x9b, 0x7f, 0x92, 0x03, 0x25, 0x55, 0x36, 0xe5, 0x23, 0xf2, 0x83, 0x38, 0xbc, 0x74, 0xa7, 0x7f, + 0x94, 0x87, 0x53, 0x55, 0xcb, 0xa3, 0x6b, 0x7c, 0xc7, 0x78, 0xcc, 0xa7, 0x8a, 0xdb, 0x30, 0x2c, + 0x7d, 0x8c, 0xe8, 0xf3, 0x27, 0x82, 0xfd, 0x78, 0x88, 0xca, 0xe0, 0x24, 0x97, 0x3e, 0xbc, 0x97, + 0x38, 0x71, 0x21, 0x1f, 0xf3, 0x39, 0xe7, 0x78, 0x08, 0xf9, 0x98, 0x4f, 0x5e, 0x1f, 0x52, 0x21, + 0xff, 0x1f, 0x39, 0x38, 0x9d, 0x52, 0x39, 0xb9, 0x0c, 0x03, 0xf5, 0xee, 0x2a, 0xc6, 0xee, 0xca, + 0x85, 0x1e, 0xc3, 0x6e, 0x77, 0x15, 0xc3, 0x76, 0x69, 0x3e, 0x92, 0xac, 0xe0, 0xe3, 0xfa, 0xe5, + 0x6a, 0x65, 0x46, 0x48, 0x55, 0x95, 0xc2, 0x04, 0x30, 0x70, 0xda, 0x97, 0x05, 0x0f, 0xf0, 0x6d, + 0xd3, 0x68, 0xc6, 0x1e, 0xe0, 0xb3, 0x32, 0xe4, 0xfb, 0x61, 0xa8, 0xfc, 0x7e, 0xd7, 0xa1, 0xc8, + 0x97, 0x4b, 0xfc, 0x23, 0x01, 0x5f, 0x1f, 0x91, 0xc6, 0x99, 0xc7, 0x12, 0x60, 0x14, 0x71, 0xde, + 0x21, 0x43, 0xf5, 0x27, 0x72, 0x70, 0x31, 0xbb, 0x75, 0xe4, 0x13, 0x30, 0xc0, 0x76, 0xe6, 0x65, + 0x6d, 0x49, 0x7c, 0x3a, 0x4f, 0x0d, 0x6c, 0xb7, 0x68, 0x43, 0x77, 0x64, 0x63, 0xdf, 0x27, 0x23, + 0x6f, 0xc1, 0x70, 0xd5, 0x75, 0xbb, 0xd4, 0xa9, 0xbf, 0x74, 0x47, 0xab, 0x8a, 0x3d, 0x21, 0xee, + 0x39, 0x4c, 0x04, 0x37, 0xdc, 0x97, 0x62, 0xd1, 0xb9, 0x64, 0x7a, 0xf5, 0x47, 0x73, 0x70, 0xa9, + 0xd7, 0x57, 0x91, 0x97, 0x60, 0x70, 0x85, 0x5a, 0xba, 0xe5, 0x55, 0x2b, 0xa2, 0x49, 0xb8, 0xc5, + 0xf2, 0x10, 0x16, 0xdd, 0x29, 0x04, 0x84, 0xac, 0x10, 0x3f, 0x57, 0x0c, 0x1c, 0x19, 0xf8, 0x19, + 0x28, 0xc2, 0x62, 0x85, 0x7c, 0x42, 0xf5, 0xf7, 0xf3, 0x30, 0x52, 0x6b, 0x75, 0xd7, 0x4c, 0x69, + 0xe1, 0xd8, 0xb7, 0xbd, 0xed, 0x5b, 0xbf, 0xf9, 0xbd, 0x59, 0xbf, 0x6c, 0xb8, 0x39, 0xfb, 0x1c, + 0x6e, 0x7e, 0x39, 0xf2, 0x26, 0x94, 0x3a, 0xf8, 0x1d, 0xf1, 0x93, 0x58, 0xfe, 0x75, 0x59, 0x27, + 0xb1, 0xbc, 0x0c, 0x1b, 0x5f, 0xcd, 0x03, 0x8c, 0xaf, 0xb0, 0xac, 0x24, 0xd0, 0x70, 0x91, 0x38, + 0x11, 0xe8, 0xa1, 0x08, 0x34, 0x5c, 0x10, 0x4e, 0x04, 0x7a, 0x00, 0x81, 0xfe, 0x4a, 0x1e, 0xc6, + 0xa2, 0x55, 0x92, 0x4f, 0xc0, 0x30, 0xaf, 0x86, 0x9f, 0x0b, 0xe5, 0x24, 0xa7, 0xe2, 0x10, 0xac, + 0x01, 0xff, 0x21, 0x0e, 0xb8, 0xc6, 0xd7, 0x75, 0xb7, 0x11, 0x9e, 0xd0, 0xf0, 0xfb, 0xdb, 0x41, + 0xee, 0x09, 0x15, 0x43, 0x69, 0x63, 0xeb, 0xba, 0x3b, 0x13, 0xfe, 0x26, 0xb3, 0x40, 0x1c, 0xda, + 0x75, 0x69, 0x94, 0x41, 0x11, 0x19, 0x88, 0xbc, 0xec, 0x71, 0xac, 0x76, 0x8a, 0xc3, 0x64, 0x36, + 0x9f, 0x0b, 0x9a, 0x8d, 0xca, 0xd0, 0xbf, 0x8b, 0xa4, 0xf1, 0x12, 0x7d, 0xfa, 0x31, 0x27, 0x27, + 0xa8, 0xe8, 0x9e, 0xce, 0x37, 0xe5, 0x7e, 0x07, 0xa8, 0x7f, 0xea, 0x42, 0xff, 0xb2, 0x45, 0x97, + 0xef, 0x93, 0x17, 0x61, 0x88, 0x29, 0xcc, 0x82, 0xcd, 0xfa, 0x32, 0x27, 0xfc, 0x27, 0x24, 0x4d, + 0x42, 0xc4, 0x7c, 0x9f, 0x16, 0x52, 0x91, 0x1b, 0x00, 0xe1, 0x13, 0x33, 0xa1, 0x7d, 0x44, 0x2e, + 0xc3, 0x31, 0xf3, 0x7d, 0x9a, 0x44, 0xe7, 0x97, 0x12, 0x0f, 0x74, 0x0a, 0xc9, 0x52, 0x1c, 0xe3, + 0x97, 0x12, 0xe3, 0x63, 0x01, 0x08, 0xfb, 0x55, 0xd3, 0x5d, 0x77, 0xc3, 0x76, 0x8c, 0x99, 0x75, + 0xdd, 0x5a, 0xa3, 0xf1, 0xdd, 0x53, 0x92, 0x62, 0xbe, 0x4f, 0x4b, 0x29, 0x47, 0x5e, 0x87, 0x11, + 0xd9, 0xa1, 0x34, 0xee, 0xf4, 0x21, 0xe3, 0xe6, 0xfb, 0xb4, 0x08, 0x2d, 0x79, 0x15, 0x86, 0xc5, + 0xef, 0x5b, 0xb6, 0xb8, 0x51, 0x96, 0x62, 0x11, 0x49, 0xa8, 0xf9, 0x3e, 0x4d, 0xa6, 0x94, 0x2a, + 0xad, 0x39, 0xa6, 0xe5, 0x89, 0x37, 0xca, 0xf1, 0x4a, 0x11, 0x27, 0x55, 0x8a, 0xbf, 0xc9, 0x5b, + 0x30, 0x1a, 0x04, 0x79, 0x7a, 0x8f, 0x36, 0x3d, 0x71, 0xf8, 0x7d, 0x36, 0x56, 0x98, 0x23, 0xe7, + 0xfb, 0xb4, 0x28, 0x35, 0xb9, 0x02, 0x25, 0x8d, 0xba, 0xe6, 0xfb, 0xfe, 0x75, 0xf1, 0x98, 0x34, + 0xce, 0xcd, 0xf7, 0x99, 0x94, 0x04, 0x9e, 0xf5, 0x4e, 0x78, 0x3f, 0x2d, 0x8e, 0xaa, 0x49, 0xac, + 0x96, 0x59, 0xcb, 0x60, 0xbd, 0x23, 0x39, 0x27, 0x7c, 0x3a, 0x0c, 0x7d, 0x25, 0x32, 0xbf, 0x0e, + 0xc7, 0x63, 0x0c, 0xc8, 0xd8, 0xf9, 0x3e, 0x2d, 0x46, 0x2f, 0x49, 0xb5, 0x62, 0xba, 0x0f, 0x44, + 0xc8, 0xd2, 0xb8, 0x54, 0x19, 0x4a, 0x92, 0x2a, 0xfb, 0x29, 0x55, 0xbd, 0x44, 0xbd, 0x0d, 0xdb, + 0x79, 0x20, 0x02, 0x94, 0xc6, 0xab, 0x16, 0x58, 0xa9, 0x6a, 0x01, 0x91, 0xab, 0x66, 0x03, 0x6e, + 0x2c, 0xbd, 0x6a, 0xdd, 0xd3, 0xe5, 0xaa, 0xf9, 0x49, 0x9c, 0xdf, 0x49, 0x0b, 0x54, 0x7f, 0x48, + 0x95, 0xf1, 0xd4, 0x0e, 0x45, 0x9c, 0xd4, 0xa1, 0xf8, 0x9b, 0x55, 0x2a, 0xa5, 0x85, 0x57, 0x26, + 0xa2, 0x95, 0x4a, 0x28, 0x56, 0xa9, 0x9c, 0x40, 0xfe, 0x86, 0x9c, 0x7b, 0x5c, 0x39, 0x15, 0xed, + 0xa0, 0x10, 0xc3, 0x3a, 0x48, 0xca, 0x51, 0x3e, 0x89, 0x79, 0x8d, 0x15, 0x82, 0xe4, 0xc3, 0x41, + 0x0b, 0x67, 0x6a, 0xf3, 0x7d, 0x1a, 0x66, 0x3c, 0x56, 0x79, 0xc6, 0x6c, 0xe5, 0x34, 0x52, 0x8c, + 0xf8, 0x14, 0x0c, 0x36, 0xdf, 0xa7, 0xf1, 0x6c, 0xda, 0x2f, 0x4a, 0x59, 0x05, 0x95, 0x33, 0xd1, + 0x29, 0x22, 0x40, 0xb0, 0x29, 0x22, 0xcc, 0x3d, 0x38, 0x97, 0xcc, 0x9d, 0xa7, 0x9c, 0x8d, 0x2e, + 0x35, 0x71, 0xfc, 0x7c, 0x9f, 0x96, 0xcc, 0xb7, 0xf7, 0x6a, 0x24, 0x9d, 0x9c, 0x72, 0x2e, 0x16, + 0x00, 0x2c, 0x44, 0x31, 0x71, 0xc9, 0x89, 0xe7, 0x96, 0xe1, 0x34, 0xcf, 0x46, 0x2b, 0x42, 0x78, + 0x89, 0xc9, 0xea, 0x7c, 0x74, 0xe3, 0x92, 0x42, 0x32, 0xdf, 0xa7, 0xa5, 0x95, 0x24, 0x33, 0x89, + 0xa4, 0x6e, 0x8a, 0x12, 0xf5, 0x8d, 0x89, 0xa1, 0xe7, 0xfb, 0xb4, 0x44, 0x1a, 0xb8, 0x1b, 0x72, + 0x36, 0x35, 0xe5, 0x42, 0xb4, 0x13, 0x43, 0x0c, 0xeb, 0x44, 0x29, 0xeb, 0xda, 0x0d, 0x39, 0xc3, + 0x96, 0x72, 0x31, 0x59, 0x2a, 0x9c, 0x39, 0xa5, 0x4c, 0x5c, 0x5a, 0x7a, 0xd2, 0x20, 0xe5, 0x09, + 0x91, 0x3a, 0x58, 0x94, 0x4f, 0xa3, 0x99, 0xef, 0xd3, 0xd2, 0x13, 0x0e, 0x69, 0xe9, 0xd9, 0x76, + 0x94, 0x4b, 0xbd, 0x78, 0x06, 0xad, 0x4b, 0xcf, 0xd4, 0xa3, 0xf7, 0xc8, 0x7d, 0xa2, 0x3c, 0x19, + 0x0d, 0x61, 0x9c, 0x49, 0x38, 0xdf, 0xa7, 0xf5, 0xc8, 0xa0, 0x72, 0x27, 0x23, 0x11, 0x89, 0xf2, + 0x54, 0x34, 0x73, 0x78, 0x2a, 0xd1, 0x7c, 0x9f, 0x96, 0x91, 0xc6, 0xe4, 0x4e, 0x46, 0x9e, 0x0a, + 0x65, 0xb2, 0x27, 0xdb, 0x40, 0x1e, 0x19, 0x59, 0x2e, 0x96, 0x53, 0x53, 0x3c, 0x28, 0x4f, 0x47, + 0x55, 0x37, 0x85, 0x84, 0xa9, 0x6e, 0x5a, 0x72, 0x88, 0xe5, 0xd4, 0x9c, 0x04, 0xca, 0x33, 0x3d, + 0x18, 0x06, 0x6d, 0x4c, 0xcd, 0x66, 0xb0, 0x9c, 0x9a, 0x14, 0x40, 0x51, 0xa3, 0x0c, 0x53, 0x48, + 0x18, 0xc3, 0xb4, 0x74, 0x02, 0xcb, 0xa9, 0xb1, 0xe3, 0x95, 0x67, 0x7b, 0x30, 0x0c, 0x5b, 0x98, + 0x16, 0x75, 0xfe, 0xd5, 0x48, 0xf0, 0x76, 0xe5, 0x23, 0xd1, 0x79, 0x43, 0x42, 0xb1, 0x79, 0x43, + 0x0e, 0xf3, 0x3e, 0x93, 0x88, 0x2c, 0xab, 0x7c, 0x34, 0x3a, 0xcc, 0x63, 0x68, 0x36, 0xcc, 0xe3, + 0xb1, 0x68, 0x67, 0x12, 0x11, 0x36, 0x95, 0xcb, 0x59, 0x4c, 0x10, 0x1d, 0x65, 0xc2, 0x63, 0x72, + 0x56, 0x53, 0x42, 0x3c, 0x2a, 0x1f, 0x8b, 0xfa, 0x75, 0x27, 0x08, 0xe6, 0xfb, 0xb4, 0x94, 0xc0, + 0x90, 0x5a, 0x7a, 0x3c, 0x23, 0xe5, 0x4a, 0x74, 0xd8, 0xa6, 0xd1, 0xb0, 0x61, 0x9b, 0x1a, 0x0b, + 0x69, 0x21, 0xed, 0xf1, 0x89, 0x72, 0x35, 0x6a, 0x98, 0x25, 0x29, 0x98, 0x61, 0x96, 0xf2, 0x68, + 0x45, 0x4b, 0x8f, 0xb1, 0xa3, 0x3c, 0xd7, 0xb3, 0x85, 0x48, 0x93, 0xd2, 0x42, 0x1e, 0x72, 0x26, + 0xb4, 0x9d, 0xee, 0x74, 0x5a, 0xb6, 0x6e, 0x28, 0x1f, 0x4f, 0xb5, 0x9d, 0x38, 0x52, 0xb2, 0x9d, + 0x38, 0x80, 0xad, 0xf2, 0xf2, 0x1b, 0x07, 0xe5, 0xf9, 0xe8, 0x2a, 0x2f, 0xe3, 0xd8, 0x2a, 0x1f, + 0x79, 0x0f, 0x31, 0x93, 0x78, 0x0f, 0xa0, 0xbc, 0x10, 0x55, 0x80, 0x18, 0x9a, 0x29, 0x40, 0xfc, + 0x05, 0xc1, 0xe7, 0xb3, 0x3d, 0xe8, 0x95, 0x29, 0xe4, 0xf6, 0xb4, 0xcf, 0x2d, 0x8b, 0x6e, 0xbe, + 0x4f, 0xcb, 0xf6, 0xc2, 0xaf, 0xa6, 0x38, 0xc4, 0x2b, 0xd7, 0xa2, 0x0a, 0x96, 0x20, 0x60, 0x0a, + 0x96, 0x74, 0xa3, 0xaf, 0xa6, 0x78, 0xb4, 0x2b, 0x9f, 0xc8, 0x64, 0x15, 0x7c, 0x73, 0x8a, 0x1f, + 0xfc, 0x0d, 0xd9, 0x25, 0x5d, 0x79, 0x31, 0xba, 0xd8, 0x85, 0x18, 0xb6, 0xd8, 0x49, 0xae, 0xeb, + 0x37, 0x64, 0x67, 0x6c, 0xe5, 0x7a, 0xb2, 0x54, 0xb8, 0x44, 0x4a, 0x4e, 0xdb, 0x5a, 0xba, 0x0f, + 0xb3, 0xf2, 0x52, 0x54, 0xeb, 0xd2, 0x68, 0x98, 0xd6, 0xa5, 0xfa, 0x3f, 0xcf, 0x25, 0x5d, 0x91, + 0x95, 0x1b, 0xf1, 0x4d, 0x76, 0x14, 0xcf, 0x2c, 0x9f, 0x84, 0xfb, 0xf2, 0xa7, 0xe3, 0xc1, 0xf6, + 0x94, 0x97, 0x63, 0xd7, 0xbe, 0x11, 0x2c, 0xb3, 0x6f, 0x63, 0xc1, 0xf9, 0x3e, 0x1d, 0x8f, 0x4f, + 0xa7, 0xbc, 0x92, 0xce, 0x21, 0xd0, 0x95, 0x78, 0x3c, 0xbb, 0x4f, 0xc7, 0x43, 0xba, 0x29, 0xaf, + 0xa6, 0x73, 0x08, 0xa4, 0x1b, 0x0f, 0x01, 0xf7, 0xa2, 0x14, 0x64, 0x5e, 0xf9, 0x64, 0xd4, 0x74, + 0x0c, 0x10, 0xcc, 0x74, 0x0c, 0x43, 0xd1, 0xbf, 0x28, 0x05, 0x67, 0x57, 0x5e, 0x4b, 0x14, 0x09, + 0x1a, 0x2b, 0x85, 0x70, 0x7f, 0x51, 0x0a, 0x6a, 0xae, 0xbc, 0x9e, 0x28, 0x12, 0xb4, 0x4e, 0x0a, + 0x7d, 0x6e, 0xf4, 0x7a, 0xbf, 0xaa, 0xbc, 0x11, 0x3d, 0x0c, 0xce, 0xa6, 0x9c, 0xef, 0xd3, 0x7a, + 0xbd, 0x83, 0xfd, 0x7c, 0xb6, 0x63, 0xb7, 0xf2, 0x66, 0x74, 0x08, 0x67, 0xd1, 0xb1, 0x21, 0x9c, + 0xe9, 0x1c, 0xfe, 0x56, 0x2c, 0x96, 0x85, 0xf2, 0x56, 0x74, 0x8a, 0x8b, 0x20, 0xd9, 0x14, 0x17, + 0x8f, 0x7c, 0x11, 0x09, 0xd2, 0xa0, 0x7c, 0x2a, 0x3a, 0xc5, 0xc9, 0x38, 0x36, 0xc5, 0x45, 0x02, + 0x3a, 0xcc, 0x24, 0x62, 0x07, 0x28, 0x6f, 0x47, 0xa7, 0xb8, 0x18, 0x9a, 0x4d, 0x71, 0xf1, 0x68, + 0x03, 0x6f, 0xc5, 0x9e, 0xd0, 0x2b, 0x9f, 0x4e, 0x6f, 0x3f, 0x22, 0xe5, 0xf6, 0xf3, 0x07, 0xf7, + 0x5a, 0xfa, 0x5b, 0x70, 0xa5, 0x1c, 0x1d, 0xbf, 0x69, 0x34, 0x6c, 0xfc, 0xa6, 0xbe, 0x23, 0x8f, + 0x6f, 0x1c, 0x84, 0x56, 0x4d, 0xf7, 0xd8, 0x38, 0x84, 0xa6, 0x48, 0x0a, 0x38, 0xb2, 0x47, 0xe6, + 0x1b, 0xa1, 0x99, 0x8c, 0x3d, 0xb2, 0xbf, 0x0d, 0x8a, 0xd1, 0xb3, 0xd9, 0x35, 0xe1, 0x67, 0xac, + 0x54, 0xa2, 0xb3, 0x6b, 0x82, 0x80, 0xcd, 0xae, 0x49, 0xef, 0xe4, 0x39, 0x98, 0x10, 0x5a, 0xc4, + 0xdd, 0xa7, 0x4d, 0x6b, 0x4d, 0x99, 0x8d, 0xbd, 0xb7, 0x8c, 0xe1, 0xd9, 0xec, 0x14, 0x87, 0xe1, + 0x7a, 0xcd, 0x61, 0x33, 0x2d, 0xb3, 0xb3, 0x6a, 0xeb, 0x8e, 0x51, 0xa7, 0x96, 0xa1, 0xcc, 0xc5, + 0xd6, 0xeb, 0x14, 0x1a, 0x5c, 0xaf, 0x53, 0xe0, 0x18, 0x22, 0x2e, 0x06, 0xd7, 0x68, 0x93, 0x9a, + 0x0f, 0xa9, 0x72, 0x13, 0xd9, 0x4e, 0x66, 0xb1, 0x15, 0x64, 0xf3, 0x7d, 0x5a, 0x16, 0x07, 0x66, + 0xab, 0x2f, 0x6e, 0xd6, 0xdf, 0x59, 0x08, 0xc2, 0x0f, 0xd4, 0x1c, 0xda, 0xd1, 0x1d, 0xaa, 0xcc, + 0x47, 0x6d, 0xf5, 0x54, 0x22, 0x66, 0xab, 0xa7, 0x22, 0x92, 0x6c, 0xfd, 0xb1, 0x50, 0xed, 0xc5, + 0x36, 0x1c, 0x11, 0xe9, 0xa5, 0xd9, 0xec, 0x14, 0x45, 0x30, 0x01, 0x2d, 0xd8, 0xd6, 0x1a, 0x9e, + 0x54, 0xdc, 0x8a, 0xce, 0x4e, 0xd9, 0x94, 0x6c, 0x76, 0xca, 0xc6, 0x32, 0x55, 0x8f, 0x62, 0xf9, + 0x18, 0xbc, 0x1d, 0x55, 0xf5, 0x14, 0x12, 0xa6, 0xea, 0x29, 0xe0, 0x24, 0x43, 0x8d, 0xba, 0xd4, + 0x53, 0x16, 0x7a, 0x31, 0x44, 0x92, 0x24, 0x43, 0x04, 0x27, 0x19, 0xce, 0x51, 0xaf, 0xb9, 0xae, + 0x2c, 0xf6, 0x62, 0x88, 0x24, 0x49, 0x86, 0x08, 0x66, 0x9b, 0xcd, 0x28, 0x78, 0xba, 0xdb, 0x7a, + 0xe0, 0xf7, 0xd9, 0x52, 0x74, 0xb3, 0x99, 0x49, 0xc8, 0x36, 0x9b, 0x99, 0x48, 0xf2, 0xa3, 0xbb, + 0xf6, 0x83, 0x57, 0x96, 0xb1, 0xc2, 0xa9, 0xd0, 0x2e, 0xd8, 0x4d, 0xa9, 0xf9, 0x3e, 0x6d, 0xb7, + 0x7e, 0xf6, 0x1f, 0x0f, 0x9c, 0x46, 0x95, 0x1a, 0x56, 0x35, 0x1e, 0x9c, 0x55, 0x70, 0xf0, 0x7c, + 0x9f, 0x16, 0xb8, 0x95, 0xbe, 0x0a, 0xc3, 0xf8, 0x51, 0x55, 0xcb, 0xf4, 0x2a, 0xd3, 0xca, 0x3b, + 0xd1, 0x2d, 0x93, 0x84, 0x62, 0x5b, 0x26, 0xe9, 0x27, 0x9b, 0xc4, 0xf1, 0x27, 0x9f, 0x62, 0x2a, + 0xd3, 0x8a, 0x16, 0x9d, 0xc4, 0x23, 0x48, 0x36, 0x89, 0x47, 0x00, 0x41, 0xbd, 0x15, 0xc7, 0xee, + 0x54, 0xa6, 0x95, 0x7a, 0x4a, 0xbd, 0x1c, 0x15, 0xd4, 0xcb, 0x7f, 0x06, 0xf5, 0xd6, 0xd7, 0xbb, + 0x5e, 0x85, 0x7d, 0xe3, 0x4a, 0x4a, 0xbd, 0x3e, 0x32, 0xa8, 0xd7, 0x07, 0xb0, 0xa9, 0x10, 0x01, + 0x35, 0xc7, 0x66, 0x93, 0xf6, 0x6d, 0xb3, 0xd5, 0x52, 0xee, 0x44, 0xa7, 0xc2, 0x38, 0x9e, 0x4d, + 0x85, 0x71, 0x18, 0x33, 0x3d, 0x79, 0xab, 0xe8, 0x6a, 0x77, 0x4d, 0xb9, 0x1b, 0x35, 0x3d, 0x43, + 0x0c, 0x33, 0x3d, 0xc3, 0x5f, 0xb8, 0xbb, 0x60, 0xbf, 0x34, 0x7a, 0xdf, 0xa1, 0xee, 0xba, 0x72, + 0x2f, 0xb6, 0xbb, 0x90, 0x70, 0xb8, 0xbb, 0x90, 0x7e, 0x93, 0x35, 0x78, 0x22, 0xb2, 0xd0, 0xf8, + 0x97, 0x36, 0x75, 0xaa, 0x3b, 0xcd, 0x75, 0xe5, 0x33, 0xc8, 0xea, 0xd9, 0xd4, 0xa5, 0x2a, 0x4a, + 0x3a, 0xdf, 0xa7, 0xf5, 0xe2, 0x84, 0xdb, 0xf2, 0x77, 0x16, 0x78, 0x24, 0x58, 0xad, 0x36, 0xe3, + 0x6f, 0x42, 0xdf, 0x8d, 0x6d, 0xcb, 0x93, 0x24, 0xb8, 0x2d, 0x4f, 0x82, 0x49, 0x07, 0x9e, 0x8a, + 0x6d, 0xd5, 0x16, 0xf5, 0x16, 0xdb, 0x97, 0x50, 0xa3, 0xa6, 0x37, 0x1f, 0x50, 0x4f, 0xf9, 0x2c, + 0xf2, 0xbe, 0x9c, 0xb1, 0xe1, 0x8b, 0x51, 0xcf, 0xf7, 0x69, 0x3b, 0xf0, 0x23, 0x2a, 0x14, 0xeb, + 0x73, 0x2b, 0x35, 0xe5, 0xfb, 0xa2, 0xe7, 0x9b, 0x0c, 0x36, 0xdf, 0xa7, 0x21, 0x8e, 0x59, 0x69, + 0x77, 0x3a, 0x6b, 0x8e, 0x6e, 0x50, 0x6e, 0x68, 0xa1, 0xed, 0x26, 0x0c, 0xd0, 0xef, 0x8f, 0x5a, + 0x69, 0x59, 0x74, 0xcc, 0x4a, 0xcb, 0xc2, 0x31, 0x45, 0x8d, 0x24, 0x3d, 0x51, 0x3e, 0x17, 0x55, + 0xd4, 0x08, 0x92, 0x29, 0x6a, 0x34, 0x45, 0xca, 0x67, 0xe0, 0x5c, 0xb0, 0x9f, 0x17, 0xeb, 0x2f, + 0xef, 0x34, 0xe5, 0xf3, 0xc8, 0xe7, 0xa9, 0xc4, 0x65, 0x40, 0x84, 0x6a, 0xbe, 0x4f, 0xcb, 0x28, + 0xcf, 0x56, 0xdc, 0x44, 0x52, 0x30, 0x61, 0x5e, 0xfc, 0x40, 0x74, 0xc5, 0xcd, 0x20, 0x63, 0x2b, + 0x6e, 0x06, 0x2a, 0x95, 0xb9, 0x10, 0xaa, 0xbe, 0x03, 0xf3, 0x40, 0xa6, 0x59, 0x1c, 0x52, 0x99, + 0x0b, 0x4b, 0x6d, 0x75, 0x07, 0xe6, 0x81, 0xb5, 0x96, 0xc5, 0x81, 0x5c, 0x81, 0x52, 0xbd, 0xbe, + 0xa8, 0x75, 0x2d, 0xa5, 0x19, 0xf3, 0x96, 0x45, 0xe8, 0x7c, 0x9f, 0x26, 0xf0, 0xcc, 0x0c, 0x9a, + 0x6d, 0xe9, 0xae, 0x67, 0x36, 0x5d, 0x1c, 0x31, 0xfe, 0x08, 0x31, 0xa2, 0x66, 0x50, 0x1a, 0x0d, + 0x33, 0x83, 0xd2, 0xe0, 0xcc, 0x5e, 0x9c, 0xd1, 0x5d, 0x57, 0xb7, 0x0c, 0x47, 0x9f, 0xc6, 0x65, + 0x82, 0xc6, 0x5e, 0x63, 0x45, 0xb0, 0xcc, 0x5e, 0x8c, 0x42, 0xf0, 0xf0, 0xdd, 0x87, 0xf8, 0x66, + 0xce, 0xfd, 0xd8, 0xe1, 0x7b, 0x0c, 0x8f, 0x87, 0xef, 0x31, 0x18, 0xda, 0x9d, 0x3e, 0x4c, 0xa3, + 0x6b, 0x26, 0x13, 0x91, 0xb2, 0x16, 0xb3, 0x3b, 0xe3, 0x04, 0x68, 0x77, 0xc6, 0x81, 0x91, 0x26, + 0xf9, 0xcb, 0xed, 0x7a, 0x46, 0x93, 0xc2, 0x55, 0x36, 0x51, 0x86, 0xad, 0xdf, 0xe1, 0xe0, 0xa8, + 0x6c, 0x5a, 0x7a, 0xdb, 0xae, 0x4c, 0xfb, 0x52, 0x37, 0xa3, 0xeb, 0x77, 0x26, 0x21, 0x5b, 0xbf, + 0x33, 0x91, 0x6c, 0x76, 0xf5, 0x37, 0x5a, 0xeb, 0xba, 0x43, 0x8d, 0x8a, 0xe9, 0xe0, 0xc9, 0xe2, + 0x26, 0xdf, 0x1a, 0xbe, 0x17, 0x9d, 0x5d, 0x7b, 0x90, 0xb2, 0xd9, 0xb5, 0x07, 0x9a, 0x19, 0x79, + 0xe9, 0x68, 0x8d, 0xea, 0x86, 0xf2, 0x20, 0x6a, 0xe4, 0x65, 0x53, 0x32, 0x23, 0x2f, 0x1b, 0x9b, + 0xfd, 0x39, 0xf7, 0x1c, 0xd3, 0xa3, 0x4a, 0x6b, 0x37, 0x9f, 0x83, 0xa4, 0xd9, 0x9f, 0x83, 0x68, + 0xb6, 0x21, 0x8c, 0x77, 0x48, 0x3b, 0xba, 0x21, 0x4c, 0x76, 0x43, 0xbc, 0x04, 0xb3, 0x58, 0xc4, + 0xa3, 0x3c, 0xc5, 0x8a, 0x5a, 0x2c, 0x02, 0xcc, 0x2c, 0x96, 0xf0, 0xd9, 0x5e, 0xe4, 0x29, 0x96, + 0x62, 0x47, 0xd7, 0x50, 0x19, 0xc7, 0xd6, 0xd0, 0xc8, 0xb3, 0xad, 0x57, 0x23, 0xef, 0x0c, 0x94, + 0x4e, 0xd4, 0xea, 0x90, 0x50, 0xcc, 0xea, 0x90, 0x5f, 0x24, 0xcc, 0xc0, 0x38, 0xde, 0x82, 0x6b, + 0xdd, 0xe0, 0x1e, 0xe7, 0x07, 0xa3, 0x9f, 0x19, 0x43, 0xb3, 0xcf, 0x8c, 0x81, 0x22, 0x4c, 0xc4, + 0xb4, 0xe5, 0x64, 0x30, 0x09, 0xcf, 0x07, 0x63, 0x20, 0xb2, 0x00, 0xa4, 0x5e, 0x5e, 0x5c, 0xa8, + 0x1a, 0x35, 0xf9, 0x8a, 0xcc, 0x8d, 0x9e, 0xc0, 0x26, 0x29, 0xe6, 0xfb, 0xb4, 0x94, 0x72, 0xe4, + 0x3d, 0xb8, 0x24, 0xa0, 0xe2, 0xc5, 0x75, 0xcd, 0xb1, 0x1f, 0x9a, 0x46, 0xb0, 0x20, 0x78, 0x51, + 0x3f, 0xb6, 0x5e, 0xb4, 0xf3, 0x7d, 0x5a, 0x4f, 0x5e, 0xd9, 0x75, 0x89, 0xf5, 0xa1, 0xbb, 0x9b, + 0xba, 0x82, 0x45, 0xa2, 0x27, 0xaf, 0xec, 0xba, 0x84, 0xdc, 0x1f, 0xee, 0xa6, 0xae, 0xa0, 0x13, + 0x7a, 0xf2, 0x22, 0x2e, 0x4c, 0xf6, 0xc2, 0x97, 0x5b, 0x2d, 0x65, 0x03, 0xab, 0xfb, 0xd8, 0x6e, + 0xaa, 0x2b, 0xa3, 0xc1, 0xb9, 0x13, 0x47, 0x36, 0x4b, 0x2f, 0x77, 0xa8, 0x55, 0x8f, 0x2c, 0x40, + 0x8f, 0xa2, 0xb3, 0x74, 0x82, 0x80, 0xcd, 0xd2, 0x09, 0x20, 0x1b, 0x50, 0xf2, 0x73, 0x15, 0x65, + 0x33, 0x3a, 0xa0, 0x64, 0x1c, 0x1b, 0x50, 0x91, 0xa7, 0x2d, 0xcb, 0x70, 0x7a, 0xf9, 0x81, 0xa7, + 0xfb, 0x16, 0xa4, 0x2b, 0xba, 0xf2, 0xfd, 0xd8, 0x25, 0x53, 0x92, 0x04, 0x2f, 0x99, 0x92, 0x60, + 0x36, 0x46, 0x18, 0xb8, 0xbe, 0x69, 0x35, 0xe7, 0x74, 0xb3, 0xd5, 0x75, 0xa8, 0xf2, 0x67, 0xa2, + 0x63, 0x24, 0x86, 0x66, 0x63, 0x24, 0x06, 0x62, 0x0b, 0x34, 0x03, 0x95, 0x5d, 0xd7, 0x5c, 0xb3, + 0xc4, 0xbe, 0xb2, 0xdb, 0xf2, 0x94, 0x7f, 0x23, 0xba, 0x40, 0xa7, 0xd1, 0xb0, 0x05, 0x3a, 0x0d, + 0x8e, 0xa7, 0x4e, 0xac, 0x17, 0xd8, 0xe2, 0x21, 0xdf, 0x55, 0xfe, 0x9b, 0xb1, 0x53, 0xa7, 0x14, + 0x1a, 0x3c, 0x75, 0x4a, 0x81, 0xb3, 0xf5, 0x91, 0xdb, 0x64, 0x0b, 0x66, 0x70, 0x57, 0xfd, 0x6f, + 0x45, 0xd7, 0xc7, 0x38, 0x9e, 0xad, 0x8f, 0x71, 0x58, 0x94, 0x8f, 0xe8, 0x82, 0x7f, 0x3b, 0x8b, + 0x4f, 0x20, 0xff, 0x44, 0x19, 0x72, 0x53, 0xe6, 0x23, 0x46, 0xca, 0x0f, 0xe5, 0xb2, 0x18, 0x05, + 0xc3, 0x23, 0x51, 0x28, 0xca, 0x48, 0xa3, 0x0f, 0x4d, 0xba, 0xa1, 0x7c, 0x21, 0x93, 0x11, 0x27, + 0x88, 0x32, 0xe2, 0x30, 0xf2, 0x2e, 0x9c, 0x0b, 0x61, 0x8b, 0xb4, 0xbd, 0x1a, 0xcc, 0x4c, 0x7f, + 0x36, 0x17, 0x35, 0x83, 0xd3, 0xc9, 0x98, 0x19, 0x9c, 0x8e, 0x49, 0x63, 0x2d, 0x44, 0xf7, 0xef, + 0xec, 0xc0, 0x3a, 0x90, 0x60, 0x06, 0x83, 0x34, 0xd6, 0x42, 0x9a, 0x3f, 0xbc, 0x03, 0xeb, 0x40, + 0xa6, 0x19, 0x0c, 0xc8, 0x8f, 0xe5, 0xe0, 0x72, 0x3a, 0xaa, 0xdc, 0x6a, 0xcd, 0xd9, 0x4e, 0x88, + 0x53, 0xfe, 0x5c, 0x2e, 0x7a, 0xd0, 0xb0, 0xbb, 0x62, 0xf3, 0x7d, 0xda, 0x2e, 0x2b, 0x20, 0x9f, + 0x82, 0xd1, 0x72, 0xd7, 0x30, 0x3d, 0xbc, 0x78, 0x63, 0x86, 0xf3, 0x8f, 0xe4, 0x62, 0x5b, 0x1c, + 0x19, 0x8b, 0x5b, 0x1c, 0x19, 0x40, 0x6e, 0xc1, 0xa9, 0x3a, 0x6d, 0x76, 0x1d, 0xd3, 0xdb, 0xd4, + 0x68, 0xc7, 0x76, 0x3c, 0xc6, 0xe3, 0xcf, 0xe7, 0xa2, 0x93, 0x58, 0x82, 0x82, 0x4d, 0x62, 0x09, + 0x20, 0xb9, 0x9b, 0xb8, 0x95, 0x17, 0x9d, 0xf9, 0xa3, 0xb9, 0x9e, 0xd7, 0xf2, 0x41, 0x5f, 0xa6, + 0x17, 0x27, 0xb5, 0xd8, 0x2d, 0xba, 0xe0, 0xfa, 0x63, 0xb9, 0x1e, 0xd7, 0xe8, 0xd2, 0x0c, 0x97, + 0x04, 0x33, 0x8e, 0x29, 0x69, 0xe4, 0x95, 0xbf, 0x90, 0xeb, 0x71, 0xed, 0x1d, 0x72, 0x4c, 0xcb, + 0x40, 0xff, 0x32, 0xf7, 0x14, 0x11, 0x8c, 0x7e, 0x3c, 0x97, 0x74, 0x15, 0x09, 0xca, 0x4b, 0x84, + 0xac, 0xd8, 0x1d, 0x37, 0x50, 0xfa, 0x2f, 0xe6, 0x92, 0xbe, 0x79, 0x61, 0xb1, 0xf0, 0x17, 0xa1, + 0x70, 0x71, 0xf6, 0x91, 0x47, 0x1d, 0x4b, 0x6f, 0x61, 0x77, 0xd6, 0x3d, 0xdb, 0xd1, 0xd7, 0xe8, + 0xac, 0xa5, 0xaf, 0xb6, 0xa8, 0xf2, 0x13, 0xb9, 0xa8, 0x05, 0x9b, 0x4d, 0xca, 0x2c, 0xd8, 0x6c, + 0x2c, 0x59, 0x87, 0x27, 0xd2, 0xb0, 0x15, 0xd3, 0xc5, 0x7a, 0xbe, 0x94, 0x8b, 0x9a, 0xb0, 0x3d, + 0x68, 0x99, 0x09, 0xdb, 0x03, 0x4d, 0xae, 0xc3, 0xd0, 0xb4, 0xed, 0x4f, 0xbf, 0x7f, 0x31, 0xe6, + 0x0c, 0x19, 0x60, 0xe6, 0xfb, 0xb4, 0x90, 0x4c, 0x94, 0x11, 0x83, 0xfa, 0xcb, 0xc9, 0x32, 0xe1, + 0xe5, 0x53, 0xf0, 0x43, 0x94, 0x11, 0xe2, 0xfe, 0x77, 0x93, 0x65, 0xc2, 0x3b, 0xae, 0xe0, 0x07, + 0x9b, 0x49, 0x78, 0x8d, 0x8b, 0x73, 0x65, 0x66, 0xb7, 0xcd, 0xac, 0xeb, 0xad, 0x16, 0xb5, 0xd6, + 0xa8, 0xf2, 0x95, 0xd8, 0x4c, 0x92, 0x4e, 0xc6, 0x66, 0x92, 0x74, 0x0c, 0xf9, 0x7e, 0x38, 0x7f, + 0x57, 0x6f, 0x99, 0x46, 0x88, 0xf3, 0x93, 0x8a, 0x2b, 0x3f, 0x99, 0x8b, 0xee, 0xa6, 0x33, 0xe8, + 0xd8, 0x6e, 0x3a, 0x03, 0x45, 0x16, 0x81, 0xe0, 0x32, 0x1a, 0xcc, 0x16, 0x6c, 0x7d, 0x56, 0xfe, + 0xbd, 0x5c, 0xd4, 0x4e, 0x4d, 0x92, 0x30, 0x3b, 0x35, 0x09, 0x25, 0x8d, 0xec, 0xd4, 0x20, 0xca, + 0x4f, 0xe5, 0xa2, 0xa7, 0x35, 0x59, 0x84, 0xf3, 0x7d, 0x5a, 0x76, 0x7e, 0x91, 0x9b, 0x30, 0x51, + 0xaf, 0x55, 0xe7, 0xe6, 0x66, 0xeb, 0x77, 0xab, 0x15, 0x7c, 0xe8, 0x60, 0x28, 0x3f, 0x1d, 0x5b, + 0xb1, 0xe2, 0x04, 0x6c, 0xc5, 0x8a, 0xc3, 0xc8, 0x1b, 0x30, 0xc2, 0xda, 0xcf, 0x06, 0x0c, 0x7e, + 0xf2, 0x57, 0x73, 0x51, 0x73, 0x4a, 0x46, 0x32, 0x73, 0x4a, 0xfe, 0x4d, 0xea, 0x70, 0x86, 0x49, + 0xb1, 0xe6, 0xd0, 0xfb, 0xd4, 0xa1, 0x56, 0xd3, 0x1f, 0xd3, 0x3f, 0x93, 0x8b, 0x5a, 0x19, 0x69, + 0x44, 0xcc, 0xca, 0x48, 0x83, 0x93, 0x07, 0x70, 0x29, 0x7e, 0x12, 0x24, 0x3f, 0x3b, 0x55, 0xfe, + 0x52, 0x2e, 0x66, 0x0c, 0xf7, 0x20, 0x46, 0x63, 0xb8, 0x07, 0x9e, 0x58, 0xf0, 0xa4, 0x38, 0x56, + 0x11, 0x0e, 0x97, 0xf1, 0xda, 0x7e, 0x96, 0xd7, 0xf6, 0xd1, 0xd0, 0x21, 0xb0, 0x07, 0xf5, 0x7c, + 0x9f, 0xd6, 0x9b, 0x1d, 0xd3, 0xb3, 0x64, 0x02, 0x0c, 0xe5, 0x2f, 0xe7, 0xd2, 0x3d, 0x52, 0x22, + 0x6e, 0xca, 0x69, 0x99, 0x33, 0xde, 0xcd, 0x4a, 0xdf, 0xa0, 0xfc, 0x95, 0xd8, 0x78, 0x4b, 0x27, + 0x63, 0xe3, 0x2d, 0x23, 0xff, 0xc3, 0x2d, 0x38, 0xc5, 0x95, 0xba, 0xa6, 0xe3, 0x30, 0xb4, 0xd6, + 0xa8, 0xa1, 0xfc, 0xfb, 0xb1, 0xd5, 0x2e, 0x41, 0x81, 0xae, 0x3d, 0x71, 0x20, 0x9b, 0xba, 0xeb, + 0x1d, 0xdd, 0xb2, 0xf0, 0x98, 0x55, 0xf9, 0x0f, 0x62, 0x53, 0x77, 0x88, 0x42, 0xc7, 0xdd, 0xe0, + 0x17, 0xd3, 0x84, 0x5e, 0xa9, 0x8f, 0x94, 0xff, 0x30, 0xa6, 0x09, 0xbd, 0x88, 0x99, 0x26, 0xf4, + 0xcc, 0xa3, 0x74, 0x37, 0xe3, 0x09, 0xb8, 0xf2, 0xb5, 0xd8, 0x8a, 0x9c, 0x4a, 0xc5, 0x56, 0xe4, + 0xf4, 0x17, 0xe4, 0x77, 0x33, 0x9e, 0x4f, 0x2b, 0x3f, 0xd7, 0x9b, 0x6f, 0xb8, 0xd2, 0xa7, 0xbf, + 0xbe, 0xbe, 0x9b, 0xf1, 0xf4, 0x58, 0xf9, 0xab, 0xbd, 0xf9, 0x86, 0x8e, 0x7d, 0xe9, 0x2f, 0x97, + 0x1b, 0xd9, 0xcf, 0x76, 0x95, 0xbf, 0x16, 0x9f, 0xba, 0x32, 0x08, 0x71, 0xea, 0xca, 0x7a, 0xfb, + 0xbb, 0x0a, 0x17, 0xb8, 0x86, 0xdc, 0x74, 0xf4, 0xce, 0x7a, 0x9d, 0x7a, 0x9e, 0x69, 0xad, 0xf9, + 0x3b, 0xb1, 0xff, 0x28, 0x17, 0x3b, 0x1e, 0xcb, 0xa2, 0xc4, 0xe3, 0xb1, 0x2c, 0x24, 0x53, 0xde, + 0xc4, 0x03, 0x5d, 0xe5, 0xaf, 0xc7, 0x94, 0x37, 0x41, 0xc1, 0x94, 0x37, 0xf9, 0xae, 0xf7, 0x56, + 0xca, 0x3b, 0x54, 0xe5, 0x3f, 0xce, 0xe6, 0x15, 0xb4, 0x2f, 0xe5, 0xf9, 0xea, 0xad, 0x94, 0xe7, + 0x96, 0xca, 0x7f, 0x92, 0xcd, 0x2b, 0xf4, 0x41, 0x4a, 0xbe, 0xd2, 0x7c, 0x17, 0xce, 0xf1, 0xd9, + 0x7c, 0x8e, 0x1a, 0x34, 0xf2, 0xa1, 0x3f, 0x1f, 0x1b, 0xfb, 0xe9, 0x64, 0x78, 0xe4, 0x9e, 0x8a, + 0x49, 0x63, 0x2d, 0xda, 0xfa, 0x37, 0x76, 0x60, 0x1d, 0x6e, 0x08, 0xd2, 0x31, 0x6c, 0xbd, 0x91, + 0x1f, 0xbf, 0x29, 0xbf, 0x10, 0x5b, 0x6f, 0x64, 0x24, 0xba, 0x73, 0xc8, 0x2f, 0xe5, 0xde, 0x88, + 0x3e, 0xf4, 0x52, 0xfe, 0x66, 0x6a, 0xe1, 0xa0, 0x03, 0xa2, 0xaf, 0xc2, 0xde, 0x88, 0x3e, 0x6a, + 0x52, 0x7e, 0x31, 0xb5, 0x70, 0xf0, 0x01, 0xd1, 0x17, 0x50, 0x6c, 0x8b, 0xd4, 0xf5, 0x6c, 0xce, + 0x2a, 0x32, 0x3d, 0xfc, 0xad, 0xf8, 0x16, 0x29, 0x95, 0x0c, 0xb7, 0x48, 0xa9, 0x98, 0x34, 0xd6, + 0xe2, 0xf3, 0x7e, 0x69, 0x07, 0xd6, 0xd2, 0xc6, 0x2e, 0x15, 0x93, 0xc6, 0x5a, 0x7c, 0xfc, 0xd7, + 0x77, 0x60, 0x2d, 0x6d, 0xec, 0x52, 0x31, 0xcc, 0x1c, 0x0b, 0x31, 0x77, 0xa9, 0xe3, 0x86, 0xea, + 0xf7, 0x9f, 0xc6, 0xcc, 0xb1, 0x0c, 0x3a, 0x66, 0x8e, 0x65, 0xa0, 0x52, 0xb9, 0x0b, 0xa1, 0xfc, + 0xf2, 0x4e, 0xdc, 0xc3, 0x7b, 0x99, 0x0c, 0x54, 0x2a, 0x77, 0x21, 0x97, 0xbf, 0xbd, 0x13, 0xf7, + 0xf0, 0x62, 0x26, 0x03, 0xc5, 0x8c, 0xa2, 0xba, 0xa7, 0x7b, 0x66, 0x73, 0xde, 0x76, 0x3d, 0x69, + 0x91, 0xff, 0xcf, 0x62, 0x46, 0x51, 0x1a, 0x11, 0x33, 0x8a, 0xd2, 0xe0, 0x49, 0xa6, 0x42, 0x1a, + 0xbf, 0xd2, 0x93, 0x69, 0x68, 0x69, 0xa5, 0xc1, 0x93, 0x4c, 0x85, 0x10, 0xfe, 0xf3, 0x9e, 0x4c, + 0x43, 0x4f, 0xf9, 0x34, 0x38, 0xb3, 0x4c, 0x67, 0x1c, 0x7b, 0xc3, 0xba, 0x45, 0x37, 0x68, 0x4b, + 0x7c, 0xfa, 0xaf, 0xc6, 0x2c, 0xd3, 0x38, 0x01, 0xde, 0xa2, 0xc4, 0x60, 0x51, 0x46, 0xe2, 0x73, + 0x7f, 0x2d, 0x93, 0x51, 0x78, 0x4c, 0x14, 0x87, 0x45, 0x19, 0x89, 0x4f, 0xfc, 0xf5, 0x4c, 0x46, + 0xe1, 0x31, 0x51, 0x1c, 0x46, 0xca, 0x30, 0x86, 0x6f, 0x25, 0x74, 0xd7, 0xf7, 0xfc, 0xfc, 0xad, + 0x5c, 0xf4, 0xd6, 0x2b, 0x8a, 0x9e, 0xef, 0xd3, 0x62, 0x05, 0x64, 0x16, 0xe2, 0x93, 0xbe, 0x99, + 0xc1, 0x22, 0xf4, 0x77, 0x8c, 0x42, 0x64, 0x16, 0xe2, 0x63, 0xfe, 0x8b, 0x0c, 0x16, 0xa1, 0xc3, + 0x63, 0x14, 0x42, 0x3e, 0x09, 0xc3, 0xf5, 0xb9, 0x95, 0x9a, 0x9f, 0x9e, 0xef, 0xef, 0xe4, 0x62, + 0xaf, 0x8a, 0x42, 0x1c, 0xbe, 0x2a, 0x0a, 0x7f, 0x92, 0x4f, 0xc1, 0xe8, 0x8c, 0x6d, 0x79, 0x7a, + 0xd3, 0xdf, 0x80, 0xfe, 0x76, 0xec, 0x0c, 0x25, 0x82, 0x9d, 0xef, 0xd3, 0xa2, 0xe4, 0x52, 0x79, + 0xd1, 0xf6, 0xdf, 0x49, 0x2f, 0x1f, 0x34, 0x3d, 0x4a, 0xce, 0x66, 0xb4, 0x7b, 0xb6, 0xf3, 0xa0, + 0x65, 0xeb, 0x86, 0x1f, 0x11, 0x52, 0x34, 0xe4, 0xef, 0xc6, 0x66, 0xb4, 0x74, 0x32, 0x36, 0xa3, + 0xa5, 0x63, 0xd2, 0x58, 0x8b, 0x2e, 0xfa, 0xd6, 0x0e, 0xac, 0xc3, 0x79, 0x38, 0x1d, 0x93, 0xc6, + 0x5a, 0x7c, 0xfe, 0xdf, 0xdb, 0x81, 0x75, 0x38, 0x0f, 0xa7, 0x63, 0x08, 0x85, 0x8b, 0xc1, 0x9b, + 0xc7, 0x70, 0x13, 0x5a, 0xb5, 0x1e, 0xb2, 0x8d, 0xae, 0xf2, 0xf7, 0x63, 0xc7, 0x1b, 0xd9, 0xa4, + 0xf3, 0x7d, 0x5a, 0x0f, 0x46, 0xd3, 0x03, 0xd0, 0x8f, 0x47, 0xe8, 0xb7, 0x4a, 0x83, 0xdf, 0xc8, + 0x4d, 0xfc, 0x46, 0xee, 0x56, 0x69, 0xf0, 0x37, 0x72, 0x13, 0xbf, 0xc9, 0xfe, 0xff, 0xcd, 0xdc, + 0xc4, 0x6f, 0xe5, 0xb4, 0x0b, 0xe1, 0x74, 0x58, 0x5e, 0xa3, 0x96, 0x57, 0x6b, 0xe9, 0x62, 0x32, + 0x4f, 0x45, 0xf1, 0x9f, 0xa9, 0x28, 0x91, 0x02, 0xed, 0x6b, 0x39, 0x18, 0xa9, 0x7b, 0x0e, 0xd5, + 0xdb, 0x22, 0xa2, 0xe0, 0x45, 0x18, 0xe4, 0x4e, 0xef, 0xfe, 0x0b, 0x7d, 0x2d, 0xf8, 0x4d, 0x2e, + 0xc3, 0xd8, 0x82, 0xee, 0x7a, 0xd8, 0xc4, 0xaa, 0x65, 0xd0, 0x47, 0xf8, 0xe0, 0xb3, 0xa0, 0xc5, + 0xa0, 0x64, 0x81, 0xd3, 0xf1, 0x72, 0x18, 0x44, 0xb6, 0xb0, 0x63, 0x20, 0xbd, 0xc1, 0x6f, 0x6f, + 0x4d, 0xf6, 0x61, 0xdc, 0xbc, 0x58, 0x59, 0xf5, 0xf7, 0x72, 0x90, 0x70, 0xc7, 0xdf, 0x7f, 0xe4, + 0x8c, 0x65, 0x18, 0x8f, 0x05, 0x2e, 0x16, 0xaf, 0x56, 0x77, 0x19, 0xd7, 0x38, 0x5e, 0x9a, 0x7c, + 0x2c, 0x78, 0x2d, 0x79, 0x47, 0x5b, 0x10, 0x41, 0x12, 0x31, 0xbd, 0x47, 0xd7, 0x69, 0x69, 0x12, + 0x4a, 0x04, 0xc1, 0xfa, 0xee, 0x44, 0x18, 0x95, 0x95, 0x5c, 0x16, 0x61, 0x3c, 0x72, 0x61, 0x68, + 0xc5, 0xae, 0x4b, 0x1d, 0x39, 0xb4, 0x22, 0x86, 0xed, 0xf8, 0x14, 0x8c, 0x54, 0xdb, 0x1d, 0xea, + 0xb8, 0xb6, 0xa5, 0x7b, 0xb6, 0x23, 0xa2, 0x20, 0x60, 0xd8, 0x3d, 0x53, 0x82, 0xcb, 0xa1, 0xe0, + 0x64, 0x7a, 0x72, 0xd5, 0xcf, 0x50, 0x58, 0xc0, 0x78, 0xb8, 0xf8, 0x94, 0x39, 0x9e, 0xa0, 0x9e, + 0x53, 0x30, 0xd2, 0x3b, 0xae, 0x8e, 0xef, 0x6a, 0x03, 0xd2, 0x2e, 0x03, 0xc8, 0xa4, 0x48, 0x41, + 0x9e, 0x87, 0x12, 0xea, 0xb1, 0x8b, 0x99, 0x47, 0x45, 0xc0, 0xc7, 0x16, 0x42, 0xe4, 0xf0, 0x7a, + 0x9c, 0x86, 0xdc, 0x86, 0x89, 0xd0, 0xc9, 0xe2, 0xa6, 0x63, 0x77, 0x3b, 0x7e, 0xae, 0x21, 0x4c, + 0xec, 0xff, 0x20, 0xc0, 0x35, 0xd6, 0x10, 0x29, 0xb1, 0x48, 0x14, 0x24, 0xf3, 0x30, 0x1e, 0xc2, + 0x98, 0x88, 0xfc, 0x1c, 0x67, 0x98, 0x5f, 0x56, 0xe2, 0xc5, 0xc4, 0x19, 0xc9, 0x2f, 0x1b, 0x2b, + 0x46, 0xaa, 0x30, 0xe0, 0x47, 0x7b, 0x1c, 0xdc, 0x51, 0x49, 0x4f, 0x8b, 0x68, 0x8f, 0x03, 0x72, + 0x9c, 0x47, 0xbf, 0x3c, 0x99, 0x83, 0x31, 0xcd, 0xee, 0x7a, 0x74, 0xc5, 0x16, 0xa7, 0x13, 0x22, + 0xaa, 0x28, 0xb6, 0xc9, 0x61, 0x98, 0x86, 0x67, 0x37, 0x9a, 0x1c, 0x27, 0xe7, 0xe7, 0x8f, 0x96, + 0x22, 0x4b, 0x70, 0x2a, 0xe1, 0x8e, 0x82, 0xcf, 0x70, 0x87, 0x78, 0xdc, 0x3e, 0xe9, 0xf3, 0x92, + 0xcc, 0x92, 0x45, 0xc9, 0x8f, 0xe4, 0xa0, 0xb4, 0xe2, 0xe8, 0xa6, 0xe7, 0x8a, 0x27, 0xb9, 0x67, + 0xa7, 0x36, 0x1c, 0xbd, 0xc3, 0xf4, 0x63, 0x0a, 0x03, 0x1e, 0xdf, 0xd5, 0x5b, 0x5d, 0xea, 0x4e, + 0xdf, 0x63, 0x5f, 0xf7, 0xdf, 0x6f, 0x4d, 0xbe, 0xb1, 0x86, 0x87, 0xde, 0x53, 0x4d, 0xbb, 0x7d, + 0x6d, 0xcd, 0xd1, 0x1f, 0x9a, 0x1e, 0x6e, 0x2d, 0xf4, 0xd6, 0x35, 0x8f, 0xb6, 0xf0, 0x6c, 0xfd, + 0x9a, 0xde, 0x31, 0xaf, 0x61, 0x60, 0xfd, 0x6b, 0x01, 0x27, 0x5e, 0x03, 0x53, 0x01, 0x0f, 0xff, + 0x92, 0x55, 0x80, 0xe3, 0xc8, 0x12, 0x80, 0xf8, 0xd4, 0x72, 0xa7, 0x23, 0xde, 0xf7, 0x4a, 0x27, + 0xd2, 0x3e, 0x86, 0x2b, 0x76, 0x20, 0x30, 0xbd, 0x23, 0x05, 0x93, 0xd6, 0x24, 0x0e, 0x4c, 0x0b, + 0x56, 0x44, 0x8b, 0x7c, 0x31, 0x8d, 0x86, 0x12, 0xf7, 0x1b, 0x9b, 0x22, 0xa4, 0x78, 0x31, 0xb2, + 0x0a, 0xe3, 0x82, 0x6f, 0x90, 0x7a, 0x66, 0x2c, 0x3a, 0x2b, 0xc4, 0xd0, 0x5c, 0x69, 0x83, 0x36, + 0x1a, 0x02, 0x2c, 0xd7, 0x11, 0x2b, 0x41, 0xa6, 0xc3, 0x54, 0xd9, 0x4b, 0x7a, 0x9b, 0xba, 0xca, + 0x38, 0x6a, 0xec, 0xa5, 0xed, 0xad, 0x49, 0xc5, 0x2f, 0x8f, 0x81, 0x4f, 0x65, 0xd1, 0x45, 0x8b, + 0xc8, 0x3c, 0xb8, 0xd6, 0x4f, 0xa4, 0xf0, 0x88, 0xeb, 0x7c, 0xb4, 0x08, 0x99, 0x81, 0xd1, 0xe0, + 0x79, 0xd1, 0x9d, 0x3b, 0xd5, 0x0a, 0x3e, 0x20, 0x16, 0xb1, 0x6f, 0x63, 0xc9, 0x61, 0x64, 0x26, + 0x91, 0x32, 0x52, 0x4c, 0x16, 0xfe, 0xa2, 0x38, 0x16, 0x93, 0xa5, 0x93, 0x12, 0x93, 0xa5, 0x46, + 0xde, 0x82, 0xe1, 0xf2, 0xbd, 0xba, 0x88, 0x35, 0xe3, 0x2a, 0xa7, 0xc3, 0x4c, 0x63, 0xfa, 0x86, + 0xdb, 0xf0, 0xe3, 0xd2, 0xc8, 0x4d, 0x97, 0xe9, 0xc9, 0x2c, 0x8c, 0x45, 0x3c, 0x14, 0x5d, 0xe5, + 0x0c, 0x72, 0xc0, 0x96, 0xeb, 0x88, 0x69, 0x38, 0x02, 0x25, 0x0f, 0xaf, 0x68, 0x21, 0xa6, 0x35, + 0x15, 0xd3, 0xc5, 0xac, 0x4d, 0x1a, 0xc5, 0xb0, 0x36, 0xf8, 0x1c, 0x79, 0x90, 0x6b, 0x8d, 0x21, + 0x50, 0x0d, 0x87, 0xe3, 0xe4, 0x1e, 0x8d, 0x15, 0x23, 0xef, 0x01, 0xc1, 0x3c, 0x4f, 0xd4, 0xf0, + 0x2f, 0xac, 0xab, 0x15, 0x57, 0x39, 0x87, 0x81, 0xdf, 0x49, 0x3c, 0x8c, 0x46, 0xb5, 0x32, 0x7d, + 0x59, 0x4c, 0x1f, 0x4f, 0xe9, 0xbc, 0x54, 0xc3, 0x0f, 0xa1, 0xd1, 0x30, 0x23, 0x49, 0xb0, 0x53, + 0xb8, 0x92, 0x0d, 0x38, 0x5f, 0x73, 0xe8, 0x43, 0xd3, 0xee, 0xba, 0xfe, 0xf2, 0xe1, 0xcf, 0x5b, + 0xe7, 0x77, 0x9c, 0xb7, 0x9e, 0x11, 0x15, 0x9f, 0xed, 0x38, 0xf4, 0x61, 0xc3, 0x0f, 0xf7, 0x1d, + 0x89, 0x56, 0x9b, 0xc5, 0x1d, 0x53, 0x79, 0xbf, 0xdf, 0x75, 0xa8, 0x80, 0x9b, 0xd4, 0x55, 0x94, + 0x70, 0xaa, 0xe5, 0x11, 0x8a, 0xcc, 0x00, 0x17, 0x49, 0xe5, 0x1d, 0x2d, 0x46, 0x34, 0x20, 0x37, + 0x67, 0x7c, 0xe7, 0x85, 0x72, 0x93, 0x27, 0x3c, 0x56, 0x2e, 0x20, 0x33, 0x95, 0x89, 0x65, 0xad, + 0x19, 0x84, 0xfe, 0x6f, 0xe8, 0x02, 0x2f, 0x8b, 0x25, 0x59, 0x9a, 0x2c, 0xc0, 0x44, 0xcd, 0xc1, + 0xa3, 0xd4, 0xdb, 0x74, 0xb3, 0x66, 0xb7, 0xcc, 0xe6, 0x26, 0xbe, 0x8a, 0x16, 0x53, 0x65, 0x87, + 0xe3, 0x1a, 0x0f, 0xe8, 0x66, 0xa3, 0x83, 0x58, 0x79, 0x59, 0x89, 0x97, 0x94, 0x43, 0x71, 0x3f, + 0xb1, 0xbb, 0x50, 0xdc, 0x14, 0x26, 0x84, 0xeb, 0xc3, 0x23, 0x8f, 0x5a, 0x6c, 0xa9, 0x77, 0xc5, + 0x0b, 0x68, 0x25, 0xe6, 0x2a, 0x11, 0xe0, 0xf9, 0xd4, 0x21, 0x46, 0x19, 0x0d, 0xc0, 0x72, 0xc3, + 0xe2, 0x45, 0x92, 0xf1, 0xaa, 0x9f, 0xdc, 0x47, 0xbc, 0xea, 0xff, 0xbd, 0x20, 0xcf, 0xbf, 0xe4, + 0x12, 0x14, 0xa5, 0x74, 0x52, 0x18, 0x8c, 0x17, 0x43, 0xef, 0x17, 0x45, 0x8c, 0xf1, 0x21, 0x61, + 0xbb, 0x04, 0x51, 0x97, 0x30, 0x7f, 0x68, 0x18, 0xa0, 0x55, 0x0b, 0x09, 0x30, 0x77, 0x63, 0x77, + 0xb5, 0x65, 0x36, 0x31, 0x21, 0x43, 0x41, 0x0a, 0xb3, 0x82, 0x50, 0x9e, 0x8f, 0x41, 0x22, 0x21, + 0xd7, 0x61, 0xd8, 0x3f, 0xc2, 0x0f, 0x83, 0x51, 0x63, 0x9c, 0x7e, 0x31, 0x5b, 0x8b, 0x34, 0x00, + 0x12, 0x11, 0x79, 0x1d, 0x20, 0x9c, 0x0e, 0x84, 0xa5, 0x85, 0x4b, 0x85, 0x3c, 0x7b, 0xc8, 0x4b, + 0x45, 0x48, 0xcd, 0x26, 0x4e, 0x59, 0x1d, 0xfd, 0x6c, 0xb5, 0x38, 0x71, 0x46, 0x74, 0x58, 0x56, + 0x90, 0x68, 0x11, 0xb2, 0x0c, 0xa7, 0x12, 0x1a, 0x28, 0x42, 0x57, 0x3f, 0xb3, 0xbd, 0x35, 0xf9, + 0x64, 0x8a, 0xfa, 0xca, 0x0b, 0x73, 0xa2, 0x2c, 0x79, 0x16, 0x0a, 0x77, 0xb4, 0xaa, 0x08, 0x9f, + 0xcb, 0x23, 0x2f, 0x47, 0x62, 0x6b, 0x31, 0x2c, 0x79, 0x0d, 0x80, 0xa7, 0xa7, 0xa9, 0xd9, 0x8e, + 0x87, 0x16, 0xc5, 0xe8, 0xf4, 0x05, 0x36, 0x96, 0x79, 0xfa, 0x9a, 0x06, 0x5b, 0xc6, 0xe4, 0x8f, + 0x0e, 0x89, 0xd5, 0x3f, 0x9b, 0x4f, 0x2c, 0x6b, 0x4c, 0xf0, 0xa2, 0x15, 0x52, 0xe7, 0xa3, 0xe0, + 0xfd, 0xa6, 0x73, 0xc1, 0x4b, 0x44, 0xe4, 0x0a, 0x0c, 0xd6, 0xd8, 0xa4, 0xd2, 0xb4, 0x5b, 0x42, + 0x15, 0x30, 0x86, 0x5a, 0x47, 0xc0, 0xb4, 0x00, 0x4b, 0xae, 0x4b, 0xf9, 0x99, 0xa5, 0x60, 0xf6, + 0x7e, 0x7e, 0xe6, 0x78, 0x54, 0x77, 0xcc, 0xd4, 0x7c, 0x3d, 0x96, 0xef, 0x4d, 0x94, 0x49, 0x59, + 0x52, 0xc3, 0xfc, 0x6e, 0x81, 0x41, 0xdb, 0xbf, 0x93, 0x41, 0xab, 0xfe, 0x76, 0x2e, 0x39, 0x44, + 0xc9, 0x8d, 0x64, 0x5c, 0x69, 0x5c, 0xbf, 0x02, 0xa0, 0x5c, 0x6b, 0x10, 0x61, 0x3a, 0x12, 0x21, + 0x3a, 0xbf, 0xef, 0x08, 0xd1, 0x85, 0x3d, 0x46, 0x88, 0x56, 0xff, 0xdf, 0x62, 0x4f, 0x2f, 0xff, + 0x23, 0x89, 0x24, 0xf8, 0x1a, 0xdb, 0x94, 0xb1, 0xda, 0xcb, 0x6e, 0x62, 0x6b, 0xc1, 0x9d, 0x98, + 0x1b, 0x3a, 0x1f, 0x95, 0xae, 0x16, 0xa5, 0x24, 0x6f, 0xc3, 0x88, 0xff, 0x01, 0x18, 0x79, 0x5c, + 0x8a, 0x98, 0x1d, 0x2c, 0x88, 0xb1, 0x18, 0xdd, 0x91, 0x02, 0xe4, 0x65, 0x18, 0x42, 0x73, 0xa8, + 0xa3, 0x37, 0xfd, 0xb0, 0xf4, 0x3c, 0x8e, 0xbd, 0x0f, 0x94, 0xa3, 0xe5, 0x05, 0x94, 0xe4, 0x73, + 0x50, 0x12, 0xb9, 0x59, 0x4a, 0xb8, 0x44, 0x5f, 0xdb, 0xc5, 0xb3, 0x88, 0x29, 0x39, 0x2f, 0x0b, + 0xdf, 0xe0, 0x20, 0x20, 0xb2, 0xc1, 0xe1, 0x29, 0x59, 0x56, 0xe0, 0x74, 0xcd, 0xa1, 0x06, 0x3e, + 0xc0, 0x99, 0x7d, 0xd4, 0x71, 0x44, 0xd6, 0x1c, 0x3e, 0x41, 0xe0, 0xfa, 0xd6, 0xf1, 0xd1, 0x6c, + 0xe5, 0x15, 0x78, 0x39, 0x36, 0x76, 0x4a, 0x71, 0x66, 0xf4, 0xf0, 0x96, 0xdc, 0xa6, 0x9b, 0x1b, + 0xb6, 0x63, 0xf0, 0xc4, 0x32, 0x62, 0xea, 0x17, 0x82, 0x7e, 0x20, 0x50, 0xb2, 0xd1, 0x13, 0x2d, + 0x74, 0xf1, 0x35, 0x18, 0xde, 0x6f, 0x6e, 0x93, 0x5f, 0xce, 0x67, 0xbc, 0x97, 0x7b, 0x7c, 0xd3, + 0x4b, 0x06, 0x39, 0xcf, 0xfb, 0x33, 0x72, 0x9e, 0xff, 0x49, 0x3e, 0xe3, 0x31, 0xe0, 0x63, 0x9d, + 0x9b, 0x38, 0x10, 0x46, 0x34, 0x37, 0x71, 0x98, 0x16, 0xda, 0x34, 0x34, 0x99, 0x28, 0x96, 0xc5, + 0xbc, 0xb4, 0x63, 0x16, 0xf3, 0x9f, 0x2f, 0xf4, 0x7a, 0x2c, 0x79, 0x22, 0xfb, 0xbd, 0xc8, 0xfe, + 0x3a, 0x0c, 0x07, 0x92, 0xad, 0x56, 0xd0, 0x5e, 0x1a, 0x0d, 0x32, 0x29, 0x71, 0x30, 0x96, 0x91, + 0x88, 0xc8, 0x55, 0xde, 0xd6, 0xba, 0xf9, 0x3e, 0xcf, 0xe9, 0x31, 0x2a, 0xb2, 0x35, 0xe8, 0x9e, + 0xde, 0x70, 0xcd, 0xf7, 0xa9, 0x16, 0xa0, 0xd5, 0xbf, 0x9b, 0x4f, 0x7d, 0x71, 0x7a, 0xd2, 0x47, + 0x7b, 0xe8, 0xa3, 0x14, 0x21, 0xf2, 0xb7, 0xb2, 0x27, 0x42, 0xdc, 0x83, 0x10, 0xff, 0x38, 0x9f, + 0xfa, 0xb2, 0xf8, 0x44, 0x88, 0x7b, 0x99, 0x2d, 0x9e, 0x87, 0x21, 0xcd, 0xde, 0x70, 0x67, 0x70, + 0x4f, 0xc4, 0xe7, 0x0a, 0x9c, 0xa8, 0x1d, 0x7b, 0xc3, 0x6d, 0xe0, 0x6e, 0x47, 0x0b, 0x09, 0xd4, + 0xef, 0xe6, 0x7b, 0xbc, 0xbd, 0x3e, 0x11, 0xfc, 0x07, 0xb9, 0x44, 0xfe, 0x5a, 0x3e, 0xf2, 0xb6, + 0xfb, 0xf1, 0x15, 0xf6, 0x35, 0x80, 0x7a, 0x73, 0x9d, 0xb6, 0x75, 0x29, 0x2f, 0x1a, 0x1e, 0x59, + 0xb8, 0x08, 0x15, 0xf9, 0xb4, 0x43, 0x12, 0xf5, 0x1b, 0xf9, 0xd8, 0xe3, 0xf6, 0x13, 0xd9, 0xed, + 0x5a, 0x76, 0x81, 0xd6, 0x89, 0xf7, 0xfa, 0x27, 0x92, 0xdb, 0xad, 0xe4, 0x7e, 0x34, 0x1f, 0x0b, + 0x6d, 0xf0, 0xd8, 0xca, 0x8e, 0x0d, 0xc0, 0x64, 0xc8, 0x85, 0xc7, 0x56, 0x93, 0x9e, 0x87, 0x21, + 0x21, 0x87, 0x60, 0xa9, 0xe0, 0xf3, 0x3e, 0x07, 0xe2, 0x01, 0x6d, 0x40, 0xa0, 0xfe, 0xb9, 0x3c, + 0x44, 0x43, 0x4e, 0x3c, 0xa6, 0x3a, 0xf4, 0x6b, 0xf9, 0x68, 0xb0, 0x8d, 0xc7, 0x57, 0x7f, 0xa6, + 0x00, 0xea, 0xdd, 0xd5, 0xa6, 0x88, 0xd5, 0xdc, 0x2f, 0x9d, 0xf0, 0x07, 0x50, 0x4d, 0xa2, 0x50, + 0xff, 0x55, 0x3e, 0x35, 0x02, 0xc8, 0xe3, 0x2b, 0xc0, 0x97, 0xf0, 0x54, 0xbc, 0x69, 0x85, 0x13, + 0x39, 0x1e, 0x42, 0xb2, 0xf1, 0x97, 0x48, 0xa6, 0xe9, 0x13, 0x92, 0x4f, 0xa6, 0x98, 0x6b, 0x98, + 0xea, 0x23, 0x34, 0xd7, 0xe4, 0xc3, 0x7c, 0xc9, 0x70, 0xfb, 0xdd, 0xfc, 0x4e, 0x01, 0x53, 0x1e, + 0xe7, 0x55, 0x75, 0xa0, 0xa6, 0x6f, 0x62, 0x60, 0x4f, 0xd6, 0x13, 0x23, 0x3c, 0xd5, 0x63, 0x87, + 0x83, 0xe4, 0x6b, 0x3b, 0x41, 0xa5, 0xfe, 0xf3, 0xfe, 0xf4, 0x68, 0x1d, 0x8f, 0xaf, 0x08, 0x2f, + 0x41, 0xb1, 0xa6, 0x7b, 0xeb, 0x42, 0x93, 0xf1, 0x36, 0xb0, 0xa3, 0x7b, 0xeb, 0x1a, 0x42, 0xc9, + 0x55, 0x18, 0xd4, 0xf4, 0x0d, 0x7e, 0xe6, 0x59, 0x0a, 0xd3, 0x70, 0x3a, 0xfa, 0x46, 0x83, 0x9f, + 0x7b, 0x06, 0x68, 0xa2, 0x06, 0x69, 0x60, 0xf9, 0xc9, 0x37, 0xe6, 0x20, 0xe4, 0x69, 0x60, 0x83, + 0xe4, 0xaf, 0x97, 0xa0, 0x38, 0x6d, 0x1b, 0x9b, 0x78, 0xf3, 0x35, 0xc2, 0x2b, 0x5b, 0xb5, 0x8d, + 0x4d, 0x0d, 0xa1, 0xe4, 0xc7, 0x72, 0x30, 0x30, 0x4f, 0x75, 0x83, 0x8d, 0x90, 0xa1, 0x5e, 0x0e, + 0x2b, 0x9f, 0x39, 0x1c, 0x87, 0x95, 0x53, 0xeb, 0xbc, 0x32, 0x59, 0x51, 0x44, 0xfd, 0xe4, 0x26, + 0x0c, 0xce, 0xe8, 0x1e, 0x5d, 0xb3, 0x9d, 0x4d, 0x74, 0xc1, 0x19, 0x0b, 0x5f, 0x7c, 0x44, 0xf4, + 0xc7, 0x27, 0xe2, 0x37, 0x63, 0x4d, 0xf1, 0x4b, 0x0b, 0x0a, 0x33, 0xb1, 0xf0, 0x9b, 0x39, 0x91, + 0xf2, 0x1c, 0xc5, 0xc2, 0xaf, 0xf0, 0x34, 0x81, 0x09, 0x8f, 0x95, 0x47, 0xd2, 0x8f, 0x95, 0xd1, + 0x7a, 0x44, 0x37, 0x3d, 0x4c, 0xbe, 0x3a, 0x8a, 0x8b, 0x3e, 0xb7, 0x1e, 0x11, 0x8a, 0xb9, 0x57, + 0x35, 0x89, 0x44, 0xfd, 0x4e, 0x3f, 0xa4, 0xbe, 0xed, 0x3f, 0x51, 0xf2, 0x13, 0x25, 0x0f, 0x95, + 0xbc, 0x92, 0x50, 0xf2, 0x8b, 0xc9, 0x68, 0x11, 0x1f, 0x52, 0x0d, 0xff, 0x6a, 0x31, 0x11, 0x6b, + 0xe6, 0xf1, 0xde, 0x5d, 0x86, 0xd2, 0xeb, 0xdf, 0x51, 0x7a, 0xc1, 0x80, 0x28, 0xed, 0x38, 0x20, + 0x06, 0x76, 0x3b, 0x20, 0x06, 0x33, 0x07, 0x44, 0xa8, 0x20, 0x43, 0x99, 0x0a, 0x52, 0x15, 0x83, + 0x06, 0x7a, 0xa7, 0xbc, 0xb9, 0xb4, 0xbd, 0x35, 0x39, 0xc6, 0x46, 0x53, 0x6a, 0xae, 0x1b, 0x64, + 0xa1, 0xfe, 0x5e, 0xb1, 0x47, 0x80, 0xa8, 0x23, 0xd1, 0x91, 0x97, 0xa0, 0x50, 0xee, 0x74, 0x84, + 0x7e, 0x9c, 0x96, 0x62, 0x53, 0x65, 0x94, 0x62, 0xd4, 0xe4, 0x75, 0x28, 0x94, 0xef, 0xd5, 0xe3, + 0x69, 0x6e, 0xca, 0xf7, 0xea, 0xe2, 0x4b, 0x32, 0xcb, 0xde, 0xab, 0x93, 0x37, 0xc3, 0x78, 0xb3, + 0xeb, 0x5d, 0xeb, 0x81, 0xd8, 0x28, 0x0a, 0x4f, 0x5d, 0xdf, 0x93, 0xa7, 0xc9, 0x50, 0x6c, 0xbb, + 0x18, 0xa3, 0x8d, 0x69, 0x53, 0x69, 0xf7, 0xda, 0x34, 0xb0, 0xa3, 0x36, 0x0d, 0xee, 0x56, 0x9b, + 0x86, 0x76, 0xa1, 0x4d, 0xb0, 0xa3, 0x36, 0x0d, 0x1f, 0x5c, 0x9b, 0x3a, 0x70, 0x31, 0x19, 0xd4, + 0x2f, 0xd0, 0x08, 0x0d, 0x48, 0x12, 0x2b, 0x1c, 0x4b, 0xf0, 0xea, 0xbf, 0xcb, 0xb1, 0x8d, 0x0d, + 0x44, 0x37, 0x5c, 0x86, 0x97, 0x5d, 0xdb, 0x92, 0xa5, 0xd5, 0x5f, 0xce, 0x67, 0xc7, 0x22, 0x3c, + 0x9e, 0x53, 0xdc, 0x0f, 0xa4, 0x4a, 0xa9, 0x18, 0x7b, 0x3c, 0x91, 0x29, 0xe5, 0x18, 0xdb, 0x34, + 0x99, 0x7d, 0x3d, 0x9f, 0x15, 0x20, 0xf1, 0x40, 0x12, 0xfb, 0x68, 0xd2, 0x19, 0x0e, 0x5d, 0xfc, + 0xdd, 0xa8, 0x17, 0xdc, 0x1c, 0x8c, 0xc8, 0x42, 0x14, 0x52, 0xda, 0x8d, 0x80, 0x23, 0xe5, 0xc8, + 0x9b, 0x41, 0x36, 0x22, 0xc9, 0x3f, 0x06, 0x3d, 0xdd, 0xfc, 0x31, 0x1b, 0x73, 0x8f, 0x91, 0xc9, + 0xc9, 0xf3, 0x50, 0x9a, 0xc3, 0xf0, 0xfe, 0xf2, 0x60, 0xe7, 0x01, 0xff, 0x65, 0xaf, 0x15, 0x4e, + 0xa3, 0xfe, 0x76, 0x0e, 0x4e, 0xdf, 0xee, 0xae, 0x52, 0xe1, 0x68, 0x17, 0xb4, 0xe1, 0x3d, 0x00, + 0x06, 0x16, 0x0e, 0x33, 0x39, 0x74, 0x98, 0xf9, 0xb8, 0x1c, 0x48, 0x31, 0x56, 0x60, 0x2a, 0xa4, + 0xe6, 0xce, 0x32, 0x4f, 0xfa, 0x3e, 0xa7, 0x0f, 0xba, 0xab, 0xb4, 0x91, 0xf0, 0x9a, 0x91, 0xb8, + 0x5f, 0x7c, 0x8b, 0x7b, 0xf3, 0xef, 0xd7, 0x41, 0xe5, 0x97, 0xf2, 0x99, 0xb1, 0x2b, 0x8f, 0x6d, + 0x92, 0xd5, 0xef, 0x4b, 0xed, 0x95, 0x78, 0xb2, 0xd5, 0x14, 0x92, 0x18, 0xc7, 0x34, 0x2e, 0xe9, + 0x02, 0x3b, 0xe6, 0xa9, 0x7f, 0x3f, 0x50, 0x81, 0xfd, 0x61, 0x2e, 0x33, 0xc6, 0xe8, 0x71, 0x15, + 0x98, 0xfa, 0xbf, 0x14, 0xfc, 0xd0, 0xa6, 0x07, 0xfa, 0x84, 0xe7, 0x61, 0x48, 0x44, 0x78, 0x88, + 0xfa, 0x09, 0x8b, 0x63, 0x43, 0x3c, 0x86, 0x0e, 0x08, 0x98, 0x49, 0x21, 0x39, 0x31, 0x4b, 0x7e, + 0xc2, 0x92, 0x03, 0xb3, 0x26, 0x91, 0x30, 0xa3, 0x61, 0xf6, 0x91, 0xe9, 0xa1, 0x05, 0xc2, 0xfa, + 0xb2, 0xc0, 0x8d, 0x06, 0xfa, 0xc8, 0xf4, 0xb8, 0xfd, 0x11, 0xa0, 0x99, 0x41, 0xc0, 0x6d, 0x11, + 0x31, 0xef, 0xa1, 0x41, 0xc0, 0x4d, 0x15, 0x4d, 0x60, 0x58, 0x6b, 0x85, 0xf3, 0xad, 0x70, 0x69, + 0x11, 0xad, 0x15, 0xee, 0xba, 0xd8, 0xda, 0x80, 0x80, 0x71, 0xd4, 0xe8, 0x5a, 0xe8, 0xc4, 0x87, + 0x1c, 0x1d, 0x84, 0x68, 0x02, 0x43, 0xae, 0xc3, 0x58, 0xdd, 0xd3, 0x2d, 0x43, 0x77, 0x8c, 0xe5, + 0xae, 0xd7, 0xe9, 0x7a, 0xb2, 0x01, 0xec, 0x7a, 0x86, 0xdd, 0xf5, 0xb4, 0x18, 0x05, 0xf9, 0x04, + 0x8c, 0xfa, 0x90, 0x59, 0xc7, 0xb1, 0x1d, 0xd9, 0xca, 0x71, 0x3d, 0x83, 0x3a, 0x8e, 0x16, 0x25, + 0x20, 0x9f, 0x84, 0xd1, 0xaa, 0xf5, 0xd0, 0x6e, 0xf2, 0x28, 0x07, 0xda, 0x82, 0xb0, 0x79, 0xf0, + 0xc5, 0x98, 0x19, 0x20, 0x1a, 0x5d, 0xa7, 0xa5, 0x45, 0x09, 0xd5, 0xed, 0x7c, 0x32, 0x02, 0xec, + 0xe3, 0xbb, 0x41, 0xba, 0x1a, 0x75, 0xdc, 0x43, 0x6f, 0x55, 0x34, 0x3e, 0x65, 0xbf, 0x61, 0x6e, + 0x83, 0x5e, 0x87, 0xc1, 0xdb, 0x74, 0x93, 0xfb, 0x98, 0x96, 0x42, 0xb7, 0xe4, 0x07, 0x02, 0x26, + 0x9f, 0xee, 0xfa, 0x74, 0xea, 0x37, 0xf3, 0xc9, 0xd8, 0xb6, 0x8f, 0xaf, 0xb0, 0x3f, 0x01, 0x03, + 0x28, 0xca, 0xaa, 0x7f, 0xbd, 0x80, 0x02, 0x44, 0x71, 0x47, 0xbd, 0x9d, 0x7d, 0x32, 0xf5, 0xe7, + 0x4a, 0xf1, 0x80, 0xc7, 0x8f, 0xaf, 0xf4, 0xde, 0x80, 0xe1, 0x19, 0xdb, 0x72, 0x4d, 0xd7, 0xa3, + 0x56, 0xd3, 0x57, 0x58, 0x74, 0xfc, 0x6f, 0x86, 0x60, 0xd9, 0x06, 0x94, 0xa8, 0xf7, 0xa3, 0xbc, + 0xe4, 0x15, 0x18, 0x42, 0x91, 0xa3, 0xcd, 0xc9, 0x27, 0x3c, 0xbc, 0x99, 0x58, 0x65, 0xc0, 0xb8, + 0xc5, 0x19, 0x92, 0x92, 0x3b, 0x30, 0x38, 0xb3, 0x6e, 0xb6, 0x0c, 0x87, 0x5a, 0xe8, 0x9b, 0x2c, + 0xc5, 0x95, 0x89, 0xf6, 0xe5, 0x14, 0xfe, 0x8b, 0xb4, 0xbc, 0x39, 0x4d, 0x51, 0x2c, 0xf2, 0x58, + 0x4c, 0xc0, 0x2e, 0xfe, 0x54, 0x1e, 0x20, 0x2c, 0x40, 0x9e, 0x86, 0x7c, 0x90, 0x33, 0x1c, 0x5d, + 0x62, 0x22, 0x1a, 0x94, 0xc7, 0xa5, 0x42, 0x8c, 0xed, 0xfc, 0x8e, 0x63, 0xfb, 0x0e, 0x94, 0xf8, + 0xe9, 0x1a, 0x7a, 0xad, 0x4b, 0x31, 0x58, 0x33, 0x1b, 0x3c, 0x85, 0xf4, 0xdc, 0x96, 0x46, 0xcb, + 0x33, 0xe2, 0x01, 0xce, 0x99, 0x5d, 0x6c, 0x42, 0x3f, 0xfe, 0x45, 0x2e, 0x43, 0x71, 0xc5, 0xcf, + 0x37, 0x3c, 0xca, 0x67, 0xe9, 0x98, 0xfc, 0x10, 0xcf, 0xba, 0x69, 0xc6, 0xb6, 0x3c, 0x56, 0x35, + 0xb6, 0x7a, 0x44, 0xc8, 0x45, 0xc0, 0x22, 0x72, 0x11, 0x30, 0xf5, 0xbf, 0xca, 0xa7, 0x84, 0xe2, + 0x7e, 0x7c, 0x87, 0xc9, 0x6b, 0x00, 0xf8, 0xf2, 0x9c, 0xc9, 0xd3, 0x7f, 0x0e, 0x82, 0xa3, 0x04, + 0x19, 0xa1, 0xda, 0x46, 0xb6, 0x1d, 0x21, 0xb1, 0xfa, 0x0f, 0x72, 0x89, 0xf8, 0xcd, 0x07, 0x92, + 0xa3, 0x6c, 0x95, 0xe5, 0xf7, 0x69, 0xc6, 0xfa, 0x7d, 0x51, 0xd8, 0x5b, 0x5f, 0x44, 0xbf, 0xe5, + 0x10, 0x2c, 0xd3, 0xa3, 0xfc, 0x96, 0xef, 0xe4, 0xd3, 0xa2, 0x59, 0x1f, 0x4f, 0x15, 0xbf, 0x11, + 0x18, 0xa5, 0xc5, 0x58, 0xfe, 0x00, 0x84, 0xc6, 0x73, 0xa2, 0x0b, 0x33, 0xf5, 0xf3, 0x30, 0x1e, + 0x8b, 0xf1, 0x2c, 0xd2, 0x53, 0x5f, 0xee, 0x1d, 0x2c, 0x3a, 0x3b, 0x66, 0x41, 0x84, 0x4c, 0xfd, + 0xff, 0x72, 0xbd, 0x23, 0x7c, 0x1f, 0xb9, 0xea, 0xa4, 0x08, 0xa0, 0xf0, 0xaf, 0x47, 0x00, 0x87, + 0xb0, 0x0d, 0x3e, 0xde, 0x02, 0xf8, 0x90, 0x4c, 0x1e, 0x1f, 0xb4, 0x00, 0x7e, 0x2e, 0xb7, 0x63, + 0x80, 0xf6, 0xa3, 0x96, 0x81, 0xfa, 0x3f, 0xe6, 0x52, 0x03, 0xa9, 0x1f, 0xa8, 0x5d, 0x6f, 0x42, + 0x89, 0xbb, 0xf0, 0x88, 0x56, 0x49, 0xa9, 0xe7, 0x18, 0x34, 0xa3, 0xbc, 0x28, 0x43, 0x16, 0x60, + 0x80, 0xb7, 0xc1, 0x10, 0xbd, 0xf1, 0x91, 0x1e, 0xd1, 0xdc, 0x8d, 0xac, 0xc9, 0x51, 0xa0, 0xd5, + 0xdf, 0xc9, 0x25, 0xe2, 0xba, 0x1f, 0xe1, 0xb7, 0x85, 0x53, 0x75, 0x61, 0xf7, 0x53, 0xb5, 0xfa, + 0xcf, 0xf2, 0xe9, 0x61, 0xe5, 0x8f, 0xf0, 0x43, 0x0e, 0xe3, 0x38, 0x6d, 0x7f, 0xeb, 0xd6, 0x0a, + 0x8c, 0x45, 0x65, 0x21, 0x96, 0xad, 0xa7, 0xd2, 0x83, 0xeb, 0x67, 0xb4, 0x22, 0xc6, 0x43, 0xfd, + 0x76, 0x2e, 0x19, 0x11, 0xff, 0xc8, 0xe7, 0xa7, 0xfd, 0x69, 0x4b, 0xf4, 0x53, 0x3e, 0x24, 0x6b, + 0xcd, 0x61, 0x7c, 0xca, 0x87, 0x64, 0xd5, 0xd8, 0xdf, 0xa7, 0xfc, 0x42, 0x3e, 0x2b, 0xa1, 0xc0, + 0x91, 0x7f, 0xd0, 0x67, 0x65, 0x21, 0xf3, 0x96, 0x89, 0x4f, 0x7b, 0x3a, 0x2b, 0x82, 0x7f, 0x06, + 0xcf, 0x04, 0x9f, 0xfd, 0x8d, 0xf1, 0x54, 0x61, 0x7d, 0x48, 0x14, 0xf9, 0x78, 0x08, 0xeb, 0x43, + 0x32, 0x54, 0x3e, 0x7c, 0xc2, 0xfa, 0x8d, 0xfc, 0x6e, 0xb3, 0x58, 0x9c, 0x08, 0x2f, 0x21, 0xbc, + 0x2f, 0xe7, 0x93, 0xd9, 0x55, 0x8e, 0x5c, 0x4c, 0x73, 0x50, 0x12, 0x79, 0x5e, 0x32, 0x85, 0xc3, + 0xf1, 0x59, 0x16, 0x8d, 0xf8, 0x8e, 0x1b, 0x20, 0x2e, 0x72, 0x76, 0x27, 0x12, 0x4e, 0xab, 0x7e, + 0x37, 0x17, 0x4b, 0x45, 0x72, 0x24, 0x47, 0x08, 0xfb, 0x5a, 0x92, 0xc8, 0x5b, 0xfe, 0x61, 0x66, + 0x31, 0x16, 0x0a, 0x3e, 0xf8, 0x9e, 0x0a, 0xf5, 0x74, 0xb3, 0x15, 0x2f, 0x2f, 0xe2, 0x0f, 0x7c, + 0x33, 0x0f, 0xa7, 0x12, 0xa4, 0xe4, 0x72, 0x24, 0xe2, 0x0f, 0x1e, 0x4b, 0xc6, 0x1c, 0xd5, 0x79, + 0xec, 0x9f, 0x3d, 0x9c, 0xa4, 0x5e, 0x86, 0x62, 0x45, 0xdf, 0xe4, 0xdf, 0xd6, 0xcf, 0x59, 0x1a, + 0xfa, 0xa6, 0x7c, 0xe2, 0x86, 0x78, 0xb2, 0x0a, 0x67, 0xf9, 0x7d, 0x88, 0x69, 0x5b, 0x2b, 0x66, + 0x9b, 0x56, 0xad, 0x45, 0xb3, 0xd5, 0x32, 0x5d, 0x71, 0xa9, 0xf7, 0xfc, 0xf6, 0xd6, 0xe4, 0x15, + 0xcf, 0xf6, 0xf4, 0x56, 0x83, 0xfa, 0x64, 0x0d, 0xcf, 0x6c, 0xd3, 0x86, 0x69, 0x35, 0xda, 0x48, + 0x29, 0xb1, 0x4c, 0x67, 0x45, 0xaa, 0x3c, 0xea, 0x7f, 0xbd, 0xa9, 0x5b, 0x16, 0x35, 0xaa, 0xd6, + 0xf4, 0xa6, 0x47, 0xf9, 0x65, 0x60, 0x81, 0x1f, 0x09, 0xf2, 0x77, 0xe8, 0x1c, 0xcd, 0x18, 0xaf, + 0x32, 0x02, 0x2d, 0xa5, 0x90, 0xfa, 0x9b, 0xc5, 0x94, 0x2c, 0x34, 0xc7, 0x48, 0x7d, 0xfc, 0x9e, + 0x2e, 0xee, 0xd0, 0xd3, 0xd7, 0x60, 0x40, 0x84, 0x55, 0x16, 0x17, 0x0c, 0xe8, 0x38, 0xff, 0x90, + 0x83, 0xe4, 0x1b, 0x1a, 0x41, 0x45, 0x5a, 0x70, 0x71, 0x85, 0x75, 0x53, 0x7a, 0x67, 0x96, 0xf6, + 0xd1, 0x99, 0x3d, 0xf8, 0x91, 0x77, 0xe1, 0x3c, 0x62, 0x53, 0xba, 0x75, 0x00, 0xab, 0xc2, 0x50, + 0x5a, 0xbc, 0xaa, 0xf4, 0xce, 0xcd, 0x2a, 0x4f, 0x3e, 0x0b, 0x23, 0xc1, 0x00, 0x31, 0xa9, 0x2b, + 0x6e, 0x2e, 0x7a, 0x8c, 0x33, 0x1e, 0xa7, 0x8e, 0x81, 0xd1, 0x5d, 0x2d, 0x1a, 0xeb, 0x2c, 0xc2, + 0x4b, 0xfd, 0x1f, 0x72, 0xbd, 0xb2, 0xe1, 0x1c, 0xf9, 0xac, 0xfc, 0x16, 0x0c, 0x18, 0xfc, 0xa3, + 0x84, 0x4e, 0xf5, 0xce, 0x97, 0xc3, 0x49, 0x35, 0xbf, 0x8c, 0xfa, 0x4f, 0x73, 0x3d, 0x93, 0xf0, + 0x1c, 0xf7, 0xcf, 0xfb, 0x72, 0x21, 0xe3, 0xf3, 0xc4, 0x24, 0x7a, 0x15, 0x26, 0xcc, 0x30, 0x4b, + 0x40, 0x23, 0x0c, 0x75, 0xa5, 0x8d, 0x4b, 0x70, 0x1c, 0x5d, 0x37, 0x20, 0x70, 0xd8, 0x72, 0x7c, + 0x6f, 0x34, 0xb7, 0xd1, 0x75, 0x4c, 0x3e, 0x2e, 0xb5, 0x33, 0x6e, 0xcc, 0x55, 0xcd, 0xbd, 0xe3, + 0x98, 0xac, 0x02, 0xdd, 0x5b, 0xa7, 0x96, 0xde, 0xd8, 0xb0, 0x9d, 0x07, 0x18, 0x0c, 0x95, 0x0f, + 0x4e, 0x6d, 0x9c, 0xc3, 0xef, 0xf9, 0x60, 0xf2, 0x2c, 0x8c, 0xae, 0xb5, 0xba, 0x34, 0x08, 0x3f, + 0xc9, 0xef, 0xfa, 0xb4, 0x11, 0x06, 0x0c, 0x6e, 0x48, 0x9e, 0x04, 0x40, 0x22, 0x0f, 0x53, 0x24, + 0xe1, 0xc5, 0x9e, 0x36, 0xc4, 0x20, 0x2b, 0xa2, 0xbb, 0x2e, 0x72, 0xad, 0xe6, 0x42, 0x6a, 0xb4, + 0x6c, 0x6b, 0xad, 0xe1, 0x51, 0xa7, 0x8d, 0x0d, 0x45, 0x67, 0x06, 0xed, 0x1c, 0x52, 0xe0, 0xd5, + 0x89, 0xbb, 0x60, 0x5b, 0x6b, 0x2b, 0xd4, 0x69, 0xb3, 0xa6, 0x3e, 0x0f, 0x44, 0x34, 0xd5, 0xc1, + 0x43, 0x0f, 0xfe, 0x71, 0xe8, 0xcd, 0xa0, 0x89, 0x8f, 0xe0, 0xa7, 0x21, 0xf8, 0x61, 0x93, 0x30, + 0xcc, 0x63, 0xf0, 0x71, 0xa1, 0xa1, 0x0b, 0x83, 0x06, 0x1c, 0x84, 0xf2, 0x3a, 0x07, 0xc2, 0xbb, + 0x82, 0x7b, 0x90, 0x6b, 0xe2, 0x97, 0xfa, 0xc5, 0x42, 0x5a, 0xde, 0xa0, 0x03, 0x29, 0x5a, 0x38, + 0xad, 0xe6, 0xf7, 0x34, 0xad, 0x8e, 0x5b, 0xdd, 0x76, 0x43, 0xef, 0x74, 0x1a, 0xf7, 0xcd, 0x16, + 0x3e, 0xe1, 0xc2, 0x85, 0x4f, 0x1b, 0xb5, 0xba, 0xed, 0x72, 0xa7, 0x33, 0xc7, 0x81, 0xe4, 0x39, + 0x38, 0xc5, 0xe8, 0xb0, 0x93, 0x02, 0xca, 0x22, 0x52, 0x32, 0x06, 0x18, 0xc4, 0xd6, 0xa7, 0xbd, + 0x00, 0x83, 0x82, 0x27, 0x5f, 0xab, 0xfa, 0xb5, 0x01, 0xce, 0xcc, 0x65, 0x3d, 0x17, 0xb0, 0xe1, + 0x93, 0x6b, 0xbf, 0x36, 0xe4, 0x97, 0xc7, 0x50, 0xcd, 0x56, 0xb7, 0xcd, 0xa3, 0x6f, 0x0d, 0x20, + 0x32, 0xf8, 0x4d, 0x2e, 0xc3, 0x18, 0xe3, 0x12, 0x08, 0x8c, 0x47, 0xb7, 0xed, 0xd7, 0x62, 0x50, + 0x72, 0x1d, 0xce, 0x44, 0x20, 0xdc, 0x06, 0xe5, 0x4f, 0x12, 0xfa, 0xb5, 0x54, 0x9c, 0xfa, 0x8d, + 0x42, 0x34, 0x9b, 0xd1, 0x11, 0x74, 0xc4, 0x79, 0x18, 0xb0, 0x9d, 0xb5, 0x46, 0xd7, 0x69, 0x89, + 0xb1, 0x57, 0xb2, 0x9d, 0xb5, 0x3b, 0x4e, 0x8b, 0x9c, 0x85, 0x12, 0xeb, 0x1d, 0xd3, 0x10, 0x43, + 0xac, 0x5f, 0xef, 0x74, 0xaa, 0x06, 0x29, 0xf3, 0x0e, 0xc1, 0xc8, 0xa8, 0x8d, 0x26, 0x6e, 0xed, + 0xb9, 0x53, 0x42, 0x3f, 0x5f, 0xf1, 0x12, 0x48, 0xec, 0x27, 0x8c, 0x97, 0xca, 0x0f, 0x02, 0x62, + 0x2c, 0x0c, 0xdc, 0x96, 0x18, 0xbc, 0x4f, 0xe2, 0x2c, 0x04, 0x32, 0x64, 0xc1, 0x37, 0x31, 0x06, + 0xa9, 0x00, 0x09, 0xa9, 0xda, 0xb6, 0x61, 0xde, 0x37, 0x29, 0x7f, 0x41, 0xd2, 0xcf, 0x2f, 0x7e, + 0x93, 0x58, 0x6d, 0xc2, 0x67, 0xb2, 0x28, 0x20, 0xe4, 0x0d, 0xae, 0x84, 0x9c, 0x0e, 0xd7, 0x3e, + 0xde, 0xb7, 0xdc, 0x4e, 0x8b, 0xa1, 0x50, 0x33, 0xb1, 0x3c, 0x2e, 0x84, 0xea, 0x7f, 0xd7, 0x9f, + 0x4c, 0x69, 0x75, 0x24, 0x76, 0xcd, 0x3c, 0x80, 0xc8, 0x58, 0x17, 0x5e, 0xae, 0x5d, 0x94, 0xc2, + 0xd3, 0x0b, 0x4c, 0x06, 0x0f, 0xa9, 0x2c, 0xb9, 0x0a, 0x83, 0xfc, 0x8b, 0xaa, 0x15, 0x61, 0xef, + 0xa0, 0x8b, 0x98, 0xdb, 0x31, 0xef, 0xdf, 0x47, 0x7f, 0xb2, 0x00, 0x4d, 0x2e, 0xc3, 0x40, 0x65, + 0xa9, 0x5e, 0x2f, 0x2f, 0xf9, 0x37, 0xc5, 0xf8, 0x96, 0xc5, 0xb0, 0xdc, 0x86, 0xab, 0x5b, 0xae, + 0xe6, 0x23, 0xc9, 0xb3, 0x50, 0xaa, 0xd6, 0x90, 0x8c, 0xbf, 0xd0, 0x1c, 0xde, 0xde, 0x9a, 0x1c, + 0x30, 0x3b, 0x9c, 0x4a, 0xa0, 0xb0, 0xde, 0xbb, 0xd5, 0x8a, 0xe4, 0x2e, 0xc1, 0xeb, 0x7d, 0x68, + 0x1a, 0x78, 0xed, 0xac, 0x05, 0x68, 0xf2, 0x32, 0x8c, 0xd4, 0xa9, 0x63, 0xea, 0xad, 0xa5, 0x2e, + 0x6e, 0x15, 0xa5, 0x88, 0x8f, 0x2e, 0xc2, 0x1b, 0x16, 0x22, 0xb4, 0x08, 0x19, 0xb9, 0x04, 0xc5, + 0x79, 0xd3, 0xf2, 0x9f, 0x4b, 0xa0, 0x3f, 0xfd, 0xba, 0x69, 0x79, 0x1a, 0x42, 0xc9, 0xb3, 0x50, + 0xb8, 0xb5, 0x52, 0x15, 0x9e, 0x60, 0xc8, 0xeb, 0x3d, 0x2f, 0x12, 0x3d, 0xf2, 0xd6, 0x4a, 0x95, + 0xbc, 0x0c, 0x43, 0x6c, 0x11, 0xa3, 0x56, 0x93, 0xba, 0xca, 0x30, 0x7e, 0x0c, 0x0f, 0x59, 0xe8, + 0x03, 0x65, 0x9f, 0x8e, 0x80, 0x92, 0xdc, 0x86, 0x89, 0x78, 0x24, 0x7c, 0xf1, 0x64, 0x07, 0x2d, + 0xae, 0x0d, 0x81, 0x4b, 0x0b, 0x9a, 0x99, 0x28, 0x48, 0x0c, 0x50, 0xe2, 0x30, 0xb6, 0xaf, 0x43, + 0xab, 0x93, 0xc7, 0x6b, 0xbe, 0xb2, 0xbd, 0x35, 0xf9, 0x91, 0x04, 0xd3, 0x86, 0x23, 0xa8, 0x24, + 0xee, 0x99, 0x9c, 0xd4, 0xff, 0x33, 0x9f, 0x9e, 0x26, 0xed, 0x08, 0x66, 0xa7, 0x7d, 0x5e, 0x7c, + 0xc7, 0xc6, 0x44, 0xf1, 0x00, 0x63, 0xe2, 0x3e, 0x8c, 0x97, 0x8d, 0xb6, 0x69, 0x95, 0xf1, 0xa7, + 0xbb, 0x38, 0x57, 0xc6, 0xd9, 0x4e, 0x7a, 0xbd, 0x18, 0x43, 0x8b, 0xef, 0xe1, 0xa1, 0x94, 0x19, + 0xaa, 0xa1, 0x73, 0x5c, 0xa3, 0x7d, 0x5f, 0x6f, 0x34, 0x79, 0x86, 0x31, 0x2d, 0xce, 0x54, 0xfd, + 0xc9, 0xfc, 0x0e, 0x99, 0xdd, 0x1e, 0x47, 0xe9, 0xab, 0x5f, 0xc9, 0xf7, 0x4e, 0xae, 0xf7, 0x58, + 0x0a, 0xe5, 0x8f, 0xf3, 0x29, 0xa9, 0xee, 0x0e, 0x24, 0x89, 0xab, 0x30, 0xc8, 0xd9, 0x04, 0x9e, + 0xc7, 0x38, 0x01, 0x73, 0x65, 0xc5, 0x89, 0xdf, 0x47, 0x93, 0x25, 0x38, 0x53, 0xbe, 0x7f, 0x9f, + 0x36, 0xbd, 0x30, 0xa8, 0xf6, 0x52, 0x18, 0xa3, 0x96, 0x07, 0x11, 0x16, 0xf8, 0x30, 0x28, 0x37, + 0xc6, 0x62, 0x49, 0x2d, 0x47, 0x56, 0xe0, 0x5c, 0x1c, 0x5e, 0xe7, 0xbb, 0x96, 0xa2, 0x14, 0x57, + 0x38, 0xc1, 0x91, 0xff, 0xa7, 0x65, 0x94, 0x4d, 0x6b, 0x25, 0xae, 0x2e, 0xfd, 0xbd, 0x5a, 0x89, + 0x4b, 0x4d, 0x6a, 0x39, 0xf5, 0x9b, 0x05, 0x39, 0x23, 0xe0, 0xe3, 0xeb, 0x23, 0x76, 0x23, 0xe2, + 0x19, 0xbe, 0xdb, 0x21, 0xf3, 0xb2, 0x08, 0xb0, 0x62, 0x74, 0x1d, 0xdf, 0x89, 0x32, 0x08, 0xf0, + 0x80, 0x40, 0x79, 0xe9, 0x0c, 0x28, 0x49, 0x15, 0x8a, 0x65, 0x67, 0x8d, 0x5b, 0xe4, 0x3b, 0xbd, + 0x39, 0xd3, 0x9d, 0x35, 0x37, 0xfd, 0xcd, 0x19, 0x63, 0xa1, 0xfe, 0xc5, 0x7c, 0x8f, 0x24, 0x7e, + 0x8f, 0xe5, 0x24, 0xf2, 0xb3, 0xf9, 0xac, 0x74, 0x7c, 0xc7, 0xd5, 0xdb, 0xed, 0x03, 0x16, 0xce, + 0xf1, 0x76, 0x05, 0x3c, 0x44, 0xe1, 0xfc, 0x41, 0x3e, 0x2b, 0xb7, 0xe0, 0x89, 0x70, 0xf6, 0x37, + 0x41, 0xa6, 0x8a, 0xf4, 0x31, 0xb6, 0xb9, 0x65, 0x55, 0xe8, 0xdf, 0xa7, 0xc7, 0x57, 0x9a, 0x48, + 0x4f, 0x86, 0xf0, 0x81, 0xb4, 0xf4, 0x0f, 0xf3, 0x99, 0x39, 0x34, 0x4f, 0x64, 0x7a, 0x98, 0x32, + 0x3d, 0x19, 0xfa, 0x07, 0x1a, 0xfa, 0xa9, 0x32, 0x3d, 0x19, 0xfb, 0x07, 0xd2, 0xd3, 0xdf, 0xcf, + 0xa7, 0x67, 0x89, 0x3d, 0x02, 0x25, 0x3d, 0x0c, 0xa7, 0x4c, 0xbf, 0x1b, 0x8a, 0x07, 0xea, 0x86, + 0xfe, 0x03, 0x58, 0x51, 0x49, 0x81, 0x1e, 0xd9, 0xa8, 0xff, 0x5e, 0x15, 0xe8, 0x21, 0x0c, 0xf9, + 0xc7, 0x59, 0xa0, 0x3f, 0x5e, 0x48, 0x66, 0x46, 0x7e, 0x5c, 0xd7, 0x24, 0x67, 0x9f, 0x6b, 0x92, + 0x5f, 0x8e, 0xbc, 0x0d, 0xe3, 0xa1, 0x2c, 0xe5, 0xc0, 0x68, 0x78, 0xe3, 0xd5, 0x64, 0xa8, 0xc6, + 0x7b, 0x0c, 0x27, 0x22, 0xf8, 0xc4, 0xa9, 0xd5, 0xef, 0x16, 0x92, 0xe9, 0xa5, 0x4f, 0x7a, 0x63, + 0x9f, 0xbd, 0x71, 0x07, 0xce, 0xcd, 0x74, 0x1d, 0x87, 0x5a, 0x5e, 0x7a, 0xa7, 0xe0, 0xe1, 0x7d, + 0x93, 0x53, 0x34, 0x92, 0x9d, 0x93, 0x51, 0x98, 0xb1, 0x15, 0x0f, 0x32, 0xe2, 0x6c, 0x07, 0x42, + 0xb6, 0x5d, 0x4e, 0x91, 0xc6, 0x36, 0xbd, 0xb0, 0xfa, 0xbb, 0xf9, 0x64, 0x42, 0xf0, 0x93, 0xae, + 0xdf, 0x5f, 0xd7, 0xab, 0x5f, 0x2c, 0xc4, 0x93, 0xa2, 0x9f, 0x2c, 0x10, 0xfb, 0xef, 0x0e, 0x5f, + 0x92, 0x38, 0x6e, 0xa4, 0xaf, 0xf0, 0xe1, 0x59, 0x5f, 0xe1, 0xe3, 0xd5, 0x5f, 0x2a, 0xc6, 0x13, + 0xcc, 0x9f, 0x74, 0xc7, 0xd1, 0x75, 0x07, 0x59, 0x86, 0x33, 0x62, 0x6e, 0xf3, 0x41, 0x98, 0x21, + 0x43, 0xcc, 0x5f, 0x3c, 0xd1, 0x9e, 0x98, 0x16, 0xbb, 0x2e, 0x75, 0x1a, 0x9e, 0xee, 0x3e, 0x68, + 0x60, 0x4a, 0x0d, 0x2d, 0xb5, 0x20, 0x63, 0x28, 0x66, 0xb5, 0x28, 0xc3, 0xc1, 0x90, 0xa1, 0x3f, + 0x21, 0x26, 0x18, 0xa6, 0x15, 0x54, 0x7f, 0x2d, 0x07, 0x13, 0xf1, 0xcf, 0x21, 0x53, 0x30, 0xc8, + 0x7e, 0x07, 0x91, 0x02, 0xa4, 0x0c, 0xe0, 0x9c, 0x23, 0xf7, 0x22, 0xf0, 0x69, 0xc8, 0x2b, 0x30, + 0x84, 0x0e, 0x1b, 0x58, 0x20, 0x1f, 0x06, 0x68, 0x08, 0x0b, 0x60, 0x5a, 0x5a, 0x5e, 0x2c, 0x24, + 0x25, 0x6f, 0xc0, 0x70, 0x35, 0xf4, 0x4c, 0x13, 0x77, 0x5e, 0xe8, 0x10, 0x2b, 0x95, 0x0c, 0x09, + 0x34, 0x99, 0x5a, 0xfd, 0x76, 0x3e, 0x54, 0xf5, 0x13, 0xd3, 0xf4, 0x40, 0xa6, 0xe9, 0x57, 0x0b, + 0x70, 0x2e, 0xee, 0xbe, 0x70, 0x72, 0x10, 0x25, 0xe6, 0x81, 0x3f, 0x03, 0x67, 0xe2, 0xb2, 0xa9, + 0x30, 0x69, 0xf4, 0xf7, 0xbe, 0x46, 0x9b, 0xda, 0xde, 0x9a, 0x7c, 0x3a, 0xe9, 0x39, 0xc2, 0x2a, + 0x4b, 0xbd, 0x58, 0x4b, 0xad, 0x24, 0xb5, 0x67, 0x3e, 0x24, 0x6f, 0x9a, 0x1e, 0xf3, 0x9e, 0xf9, + 0xd9, 0x7c, 0xb2, 0x67, 0x4e, 0x0e, 0xc5, 0xc4, 0x84, 0xf2, 0x0f, 0x73, 0xfe, 0xfd, 0xf0, 0x82, + 0xe9, 0x7a, 0x55, 0xeb, 0xa1, 0xde, 0x32, 0x83, 0x47, 0xd7, 0xe4, 0xa6, 0x9f, 0x2e, 0x9d, 0x21, + 0xa5, 0x77, 0x1f, 0xe8, 0xc1, 0x25, 0xd2, 0xa5, 0xb7, 0x4c, 0x57, 0xe4, 0xb6, 0x7e, 0x3a, 0x91, + 0x30, 0xdd, 0x2f, 0x46, 0x2e, 0x4b, 0x77, 0xff, 0xd2, 0x1a, 0x25, 0x3f, 0x26, 0x10, 0x77, 0xfd, + 0x23, 0x8b, 0xa6, 0xeb, 0x9a, 0xd6, 0x9a, 0x9c, 0x11, 0x16, 0x57, 0xcb, 0x36, 0x87, 0x37, 0xe2, + 0x39, 0x7a, 0x23, 0x05, 0xd4, 0x7f, 0x95, 0x83, 0x8b, 0x8c, 0x13, 0x06, 0x32, 0x49, 0x7c, 0xd8, + 0x81, 0x3a, 0xbc, 0xdd, 0x43, 0x52, 0x42, 0x03, 0x9e, 0x49, 0x3e, 0x4e, 0x8a, 0x11, 0xc6, 0xb8, + 0xf7, 0x90, 0xfd, 0xfe, 0xde, 0xa1, 0xfe, 0x72, 0x01, 0x46, 0x67, 0x6c, 0xcb, 0xd3, 0x9b, 0xde, + 0xc9, 0xba, 0x70, 0x90, 0x83, 0x5f, 0x32, 0x09, 0xfd, 0xb3, 0x6d, 0xdd, 0x6c, 0x89, 0x9d, 0x31, + 0xc6, 0x19, 0xa7, 0x0c, 0xa0, 0x71, 0x38, 0xb9, 0x89, 0xd1, 0xb5, 0x98, 0xa4, 0x03, 0xff, 0xcd, + 0xb1, 0x30, 0x24, 0xb3, 0x84, 0x12, 0x49, 0xc9, 0x39, 0x80, 0x9b, 0x56, 0x72, 0x49, 0xb9, 0xcf, + 0x4e, 0xe6, 0xa5, 0xe3, 0xd1, 0x67, 0xcf, 0x2d, 0xf2, 0x3d, 0xc7, 0x6d, 0xd3, 0x32, 0xc8, 0x05, + 0x38, 0x7b, 0xa7, 0x3e, 0xab, 0x35, 0x6e, 0x57, 0x97, 0x2a, 0x8d, 0x3b, 0x4b, 0xf5, 0xda, 0xec, + 0x4c, 0x75, 0xae, 0x3a, 0x5b, 0x99, 0xe8, 0x23, 0xa7, 0x61, 0x3c, 0x44, 0xcd, 0xdf, 0x59, 0x2c, + 0x2f, 0x4d, 0xe4, 0xc8, 0x29, 0x18, 0x0d, 0x81, 0xd3, 0xcb, 0x2b, 0x13, 0xf9, 0xe7, 0x3e, 0x06, + 0xc3, 0xf8, 0x34, 0x82, 0xfb, 0x45, 0x92, 0x11, 0x18, 0x5c, 0x9e, 0xae, 0xcf, 0x6a, 0x77, 0x91, + 0x09, 0x40, 0xa9, 0x32, 0xbb, 0xc4, 0x18, 0xe6, 0x9e, 0xfb, 0x7f, 0x72, 0x00, 0xf5, 0xb9, 0x95, + 0x9a, 0x20, 0x1c, 0x86, 0x81, 0xea, 0xd2, 0xdd, 0xf2, 0x42, 0x95, 0xd1, 0x0d, 0x42, 0x71, 0xb9, + 0x36, 0xcb, 0x6a, 0x18, 0x82, 0xfe, 0x99, 0x85, 0xe5, 0xfa, 0xec, 0x44, 0x9e, 0x01, 0xb5, 0xd9, + 0x72, 0x65, 0xa2, 0xc0, 0x80, 0xf7, 0xb4, 0xea, 0xca, 0xec, 0x44, 0x91, 0xfd, 0xb9, 0x50, 0x5f, + 0x29, 0xaf, 0x4c, 0xf4, 0xb3, 0x3f, 0xe7, 0xf0, 0xcf, 0x12, 0x63, 0x56, 0x9f, 0x5d, 0xc1, 0x1f, + 0x03, 0xac, 0x09, 0x73, 0xfe, 0xaf, 0x41, 0x86, 0x62, 0xac, 0x2b, 0x55, 0x6d, 0x62, 0x88, 0xfd, + 0x60, 0x2c, 0xd9, 0x0f, 0x60, 0x8d, 0xd3, 0x66, 0x17, 0x97, 0xef, 0xce, 0x4e, 0x0c, 0x33, 0x5e, + 0x8b, 0xb7, 0x19, 0x78, 0x84, 0xfd, 0xa9, 0x2d, 0xb2, 0x3f, 0x47, 0x19, 0x27, 0x6d, 0xb6, 0xbc, + 0x50, 0x2b, 0xaf, 0xcc, 0x4f, 0x8c, 0xb1, 0xf6, 0x20, 0xcf, 0x71, 0x5e, 0x72, 0xa9, 0xbc, 0x38, + 0x3b, 0x31, 0x21, 0x68, 0x2a, 0x0b, 0xd5, 0xa5, 0xdb, 0x13, 0xa7, 0xb0, 0x21, 0xef, 0x2e, 0xe2, + 0x0f, 0xc2, 0x0a, 0xe0, 0x5f, 0xa7, 0x9f, 0xfb, 0x7e, 0x28, 0x2d, 0xd7, 0x71, 0x3b, 0x72, 0x1e, + 0x4e, 0x2f, 0xd7, 0x1b, 0x2b, 0xef, 0xd6, 0x66, 0x63, 0xf2, 0x3e, 0x05, 0xa3, 0x3e, 0x62, 0xa1, + 0xba, 0x74, 0xe7, 0x33, 0x5c, 0xda, 0x3e, 0x68, 0xb1, 0x3c, 0xb3, 0x5c, 0x9f, 0xc8, 0xb3, 0x5e, + 0xf1, 0x41, 0xf7, 0xaa, 0x4b, 0x95, 0xe5, 0x7b, 0xf5, 0x89, 0xc2, 0x73, 0x0f, 0x61, 0x84, 0xe7, + 0x78, 0x5f, 0x76, 0xcc, 0x35, 0xd3, 0x22, 0x4f, 0xc2, 0x85, 0xca, 0xec, 0xdd, 0xea, 0xcc, 0x6c, + 0x63, 0x59, 0xab, 0xde, 0xac, 0x2e, 0xc5, 0x6a, 0x3a, 0x0b, 0xa7, 0xa2, 0xe8, 0x72, 0xad, 0x3a, + 0x91, 0x23, 0xe7, 0x80, 0x44, 0xc1, 0xb7, 0xca, 0x8b, 0x73, 0x13, 0x79, 0xa2, 0xc0, 0x99, 0x28, + 0xbc, 0xba, 0xb4, 0x72, 0x67, 0x69, 0x76, 0xa2, 0xf0, 0xdc, 0x5f, 0xcb, 0xc1, 0xd9, 0xd4, 0x3c, + 0x20, 0x44, 0x85, 0xa7, 0x66, 0x17, 0xca, 0xf5, 0x95, 0xea, 0x4c, 0x7d, 0xb6, 0xac, 0xcd, 0xcc, + 0x37, 0x66, 0xca, 0x2b, 0xb3, 0x37, 0x97, 0xb5, 0x77, 0x1b, 0x37, 0x67, 0x97, 0x66, 0xb5, 0xf2, + 0xc2, 0x44, 0x1f, 0x79, 0x16, 0x26, 0x33, 0x68, 0xea, 0xb3, 0x33, 0x77, 0xb4, 0xea, 0xca, 0xbb, + 0x13, 0x39, 0xf2, 0x0c, 0x3c, 0x99, 0x49, 0xc4, 0x7e, 0x4f, 0xe4, 0xc9, 0x53, 0x70, 0x31, 0x8b, + 0xe4, 0x9d, 0x85, 0x89, 0xc2, 0x73, 0x3f, 0x93, 0x03, 0x92, 0x4c, 0xe4, 0x40, 0x9e, 0x86, 0x4b, + 0x4c, 0x2f, 0x1a, 0xd9, 0x0d, 0x7c, 0x06, 0x9e, 0x4c, 0xa5, 0x90, 0x9a, 0x37, 0x09, 0x4f, 0x64, + 0x90, 0x88, 0xc6, 0x5d, 0x02, 0x25, 0x9d, 0x00, 0x9b, 0xf6, 0xab, 0x39, 0x38, 0x9b, 0xea, 0x8a, + 0x4c, 0xae, 0xc0, 0x47, 0xca, 0x95, 0x45, 0xd6, 0x37, 0x33, 0x2b, 0xd5, 0xe5, 0xa5, 0x7a, 0x63, + 0x71, 0xae, 0xdc, 0x60, 0xda, 0x77, 0xa7, 0x1e, 0xeb, 0xcd, 0xcb, 0xa0, 0xf6, 0xa0, 0x9c, 0x99, + 0x2f, 0x2f, 0xdd, 0x64, 0xc3, 0x8f, 0x7c, 0x04, 0x9e, 0xce, 0xa4, 0x9b, 0x5d, 0x2a, 0x4f, 0x2f, + 0xcc, 0x56, 0x26, 0xf2, 0xe4, 0xa3, 0xf0, 0x4c, 0x26, 0x55, 0xa5, 0x5a, 0xe7, 0x64, 0x85, 0xe7, + 0xf4, 0xc8, 0x64, 0xc4, 0xbe, 0x72, 0x66, 0x79, 0x69, 0xa5, 0x3c, 0xb3, 0x92, 0xa6, 0xd9, 0x17, + 0xe0, 0x6c, 0x04, 0x3b, 0x7d, 0xa7, 0x5e, 0x5d, 0x9a, 0xad, 0xd7, 0x27, 0x72, 0x09, 0x54, 0x20, + 0xda, 0xfc, 0x74, 0xe5, 0xdb, 0xff, 0xd3, 0x53, 0x7d, 0xdf, 0xfe, 0xa3, 0xa7, 0x72, 0x7f, 0xf0, + 0x47, 0x4f, 0xe5, 0xfe, 0xd9, 0x1f, 0x3d, 0x95, 0xfb, 0xec, 0xf5, 0xbd, 0xe4, 0x00, 0xe1, 0xf3, + 0xe2, 0x6a, 0x09, 0x0d, 0xf3, 0x97, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x64, 0x30, + 0x8e, 0xf5, 0x8e, 0x01, 0x00, } func (m *Metadata) Marshal() (dAtA []byte, err error) { @@ -31527,6 +31702,52 @@ func (m *OneOf_SFTPSummary) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *OneOf_ContactCreate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OneOf_ContactCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ContactCreate != nil { + { + size, err := m.ContactCreate.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xc + i-- + dAtA[i] = 0x82 + } + return len(dAtA) - i, nil +} +func (m *OneOf_ContactDelete) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OneOf_ContactDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ContactDelete != nil { + { + size, err := m.ContactDelete.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xc + i-- + dAtA[i] = 0x8a + } + return len(dAtA) - i, nil +} func (m *OneOf_WorkloadIdentityCreate) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) @@ -31643,12 +31864,12 @@ func (m *StreamStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - n661, err661 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastUploadTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastUploadTime):]) - if err661 != nil { - return 0, err661 + n663, err663 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastUploadTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastUploadTime):]) + if err663 != nil { + return 0, err663 } - i -= n661 - i = encodeVarintEvents(dAtA, i, uint64(n661)) + i -= n663 + i = encodeVarintEvents(dAtA, i, uint64(n663)) i-- dAtA[i] = 0x1a if m.LastEventIndex != 0 { @@ -31807,12 +32028,12 @@ func (m *Identity) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xc2 } } - n665, err665 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.PreviousIdentityExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.PreviousIdentityExpires):]) - if err665 != nil { - return 0, err665 + n667, err667 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.PreviousIdentityExpires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.PreviousIdentityExpires):]) + if err667 != nil { + return 0, err667 } - i -= n665 - i = encodeVarintEvents(dAtA, i, uint64(n665)) + i -= n667 + i = encodeVarintEvents(dAtA, i, uint64(n667)) i-- dAtA[i] = 0x1 i-- @@ -31960,12 +32181,12 @@ func (m *Identity) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x4a } - n669, err669 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) - if err669 != nil { - return 0, err669 + n671, err671 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expires):]) + if err671 != nil { + return 0, err671 } - i -= n669 - i = encodeVarintEvents(dAtA, i, uint64(n669)) + i -= n671 + i = encodeVarintEvents(dAtA, i, uint64(n671)) i-- dAtA[i] = 0x42 if len(m.KubernetesUsers) > 0 { @@ -38939,6 +39160,184 @@ func (m *UserLoginAccessListInvalid) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } +func (m *ContactCreate) 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 *ContactCreate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContactCreate) 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 m.ContactType != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.ContactType)) + i-- + dAtA[i] = 0x38 + } + if len(m.Email) > 0 { + i -= len(m.Email) + copy(dAtA[i:], m.Email) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Email))) + i-- + dAtA[i] = 0x32 + } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.UserMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.ResourceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ContactDelete) 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 *ContactDelete) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContactDelete) 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 m.ContactType != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.ContactType)) + i-- + dAtA[i] = 0x38 + } + if len(m.Email) > 0 { + i -= len(m.Email) + copy(dAtA[i:], m.Email) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Email))) + i-- + dAtA[i] = 0x32 + } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.UserMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.ResourceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { offset -= sovEvents(v) base := offset @@ -45077,6 +45476,30 @@ func (m *OneOf_SFTPSummary) Size() (n int) { } return n } +func (m *OneOf_ContactCreate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ContactCreate != nil { + l = m.ContactCreate.Size() + n += 2 + l + sovEvents(uint64(l)) + } + return n +} +func (m *OneOf_ContactDelete) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ContactDelete != nil { + l = m.ContactDelete.Size() + n += 2 + l + sovEvents(uint64(l)) + } + return n +} func (m *OneOf_WorkloadIdentityCreate) Size() (n int) { if m == nil { return 0 @@ -47731,6 +48154,64 @@ func (m *UserLoginAccessListInvalid) Size() (n int) { return n } +func (m *ContactCreate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Metadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.ResourceMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.UserMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.ConnectionMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) + l = len(m.Email) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.ContactType != 0 { + n += 1 + sovEvents(uint64(m.ContactType)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ContactDelete) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Metadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.ResourceMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.UserMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.ConnectionMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) + l = len(m.Email) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.ContactType != 0 { + n += 1 + sovEvents(uint64(m.ContactType)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovEvents(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -86395,9 +86876,9 @@ func (m *OneOf) Unmarshal(dAtA []byte) error { } m.Event = &OneOf_SFTPSummary{v} iNdEx = postIndex - case 194: + case 192: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityCreate", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ContactCreate", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -86424,15 +86905,15 @@ func (m *OneOf) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &WorkloadIdentityCreate{} + v := &ContactCreate{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Event = &OneOf_WorkloadIdentityCreate{v} + m.Event = &OneOf_ContactCreate{v} iNdEx = postIndex - case 195: + case 193: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityUpdate", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ContactDelete", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -86459,15 +86940,15 @@ func (m *OneOf) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &WorkloadIdentityUpdate{} + v := &ContactDelete{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Event = &OneOf_WorkloadIdentityUpdate{v} + m.Event = &OneOf_ContactDelete{v} iNdEx = postIndex - case 196: + case 194: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityDelete", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityCreate", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -86494,15 +86975,15 @@ func (m *OneOf) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &WorkloadIdentityDelete{} + v := &WorkloadIdentityCreate{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Event = &OneOf_WorkloadIdentityDelete{v} + m.Event = &OneOf_WorkloadIdentityCreate{v} iNdEx = postIndex - case 198: + case 195: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserLoginAccessListInvalid", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityUpdate", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -86529,117 +87010,15 @@ func (m *OneOf) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &UserLoginAccessListInvalid{} + v := &WorkloadIdentityUpdate{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Event = &OneOf_UserLoginAccessListInvalid{v} - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipEvents(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthEvents - } - 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 *StreamStatus) 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 ErrIntOverflowEvents - } - 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: StreamStatus: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: StreamStatus: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UploadID", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - 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 ErrInvalidLengthEvents - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthEvents - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.UploadID = string(dAtA[iNdEx:postIndex]) + m.Event = &OneOf_WorkloadIdentityUpdate{v} iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field LastEventIndex", wireType) - } - m.LastEventIndex = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.LastEventIndex |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: + case 196: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LastUploadTime", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityDelete", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -86666,64 +87045,15 @@ func (m *StreamStatus) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.LastUploadTime, dAtA[iNdEx:postIndex]); err != nil { + v := &WorkloadIdentityDelete{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } + m.Event = &OneOf_WorkloadIdentityDelete{v} iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipEvents(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthEvents - } - 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 *SessionUpload) 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 ErrIntOverflowEvents - } - 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: SessionUpload: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SessionUpload: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 198: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UserLoginAccessListInvalid", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -86750,15 +87080,68 @@ func (m *SessionUpload) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + v := &UserLoginAccessListInvalid{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } + m.Event = &OneOf_UserLoginAccessListInvalid{v} iNdEx = postIndex - case 2: + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + 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 *StreamStatus) 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 ErrIntOverflowEvents + } + 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: StreamStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StreamStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SessionMetadata", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UploadID", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -86768,30 +87151,48 @@ func (m *SessionUpload) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.SessionMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.UploadID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LastEventIndex", wireType) + } + m.LastEventIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LastEventIndex |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SessionURL", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field LastUploadTime", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -86801,23 +87202,24 @@ func (m *SessionUpload) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - m.SessionURL = string(dAtA[iNdEx:postIndex]) + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.LastUploadTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -86841,7 +87243,7 @@ func (m *SessionUpload) Unmarshal(dAtA []byte) error { } return nil } -func (m *Identity) Unmarshal(dAtA []byte) error { +func (m *SessionUpload) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -86864,17 +87266,17 @@ func (m *Identity) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Identity: wiretype end group for non-group") + return fmt.Errorf("proto: SessionUpload: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: Identity: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SessionUpload: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -86884,29 +87286,30 @@ func (m *Identity) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - m.User = string(dAtA[iNdEx:postIndex]) + if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Impersonator", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SessionMetadata", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -86916,27 +87319,28 @@ func (m *Identity) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - m.Impersonator = string(dAtA[iNdEx:postIndex]) + if err := m.SessionMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 3: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SessionURL", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -86964,11 +87368,62 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex])) + m.SessionURL = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + 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 *Identity) 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 ErrIntOverflowEvents + } + 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: Identity: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Identity: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -86996,11 +87451,11 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Usage = append(m.Usage, string(dAtA[iNdEx:postIndex])) + m.User = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Logins", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Impersonator", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -87028,11 +87483,11 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Logins = append(m.Logins, string(dAtA[iNdEx:postIndex])) + m.Impersonator = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KubernetesGroups", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -87060,11 +87515,11 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.KubernetesGroups = append(m.KubernetesGroups, string(dAtA[iNdEx:postIndex])) + m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 7: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KubernetesUsers", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -87092,13 +87547,13 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.KubernetesUsers = append(m.KubernetesUsers, string(dAtA[iNdEx:postIndex])) + m.Usage = append(m.Usage, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 8: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Logins", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -87108,28 +87563,27 @@ func (m *Identity) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Expires, dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Logins = append(m.Logins, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 9: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RouteToCluster", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field KubernetesGroups", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -87157,11 +87611,11 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.RouteToCluster = string(dAtA[iNdEx:postIndex]) + m.KubernetesGroups = append(m.KubernetesGroups, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 10: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KubernetesCluster", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field KubernetesUsers", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -87189,11 +87643,11 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.KubernetesCluster = string(dAtA[iNdEx:postIndex]) + m.KubernetesUsers = append(m.KubernetesUsers, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 11: + case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Traits", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -87220,15 +87674,15 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Traits.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Expires, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 12: + case 9: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RouteToApp", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RouteToCluster", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -87238,31 +87692,27 @@ func (m *Identity) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - if m.RouteToApp == nil { - m.RouteToApp = &RouteToApp{} - } - if err := m.RouteToApp.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.RouteToCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 13: + case 10: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TeleportCluster", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field KubernetesCluster", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -87290,11 +87740,11 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.TeleportCluster = string(dAtA[iNdEx:postIndex]) + m.KubernetesCluster = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 14: + case 11: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RouteToDatabase", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Traits", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -87321,18 +87771,15 @@ func (m *Identity) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.RouteToDatabase == nil { - m.RouteToDatabase = &RouteToDatabase{} - } - if err := m.RouteToDatabase.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Traits.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 15: + case 12: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DatabaseNames", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RouteToApp", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -87342,27 +87789,131 @@ func (m *Identity) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - m.DatabaseNames = append(m.DatabaseNames, string(dAtA[iNdEx:postIndex])) + if m.RouteToApp == nil { + m.RouteToApp = &RouteToApp{} + } + if err := m.RouteToApp.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 16: + case 13: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DatabaseUsers", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TeleportCluster", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TeleportCluster = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RouteToDatabase", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RouteToDatabase == nil { + m.RouteToDatabase = &RouteToDatabase{} + } + if err := m.RouteToDatabase.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DatabaseNames", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DatabaseNames = append(m.DatabaseNames, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 16: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DatabaseUsers", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -107551,46 +108102,529 @@ func (m *WorkloadIdentityUpdate) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserMetadata", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvents - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthEvents - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthEvents - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.UserMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UserMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ConnectionMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityData", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.WorkloadIdentityData == nil { + m.WorkloadIdentityData = &Struct{} + } + if err := m.WorkloadIdentityData.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + 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 *WorkloadIdentityDelete) 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 ErrIntOverflowEvents + } + 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: WorkloadIdentityDelete: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WorkloadIdentityDelete: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UserMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ConnectionMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + 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 *AccessListInvalidMetadata) 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 ErrIntOverflowEvents + } + 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: AccessListInvalidMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccessListInvalidMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccessListName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccessListName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.User = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MissingRoles", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MissingRoles = append(m.MissingRoles, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + 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 *UserLoginAccessListInvalid) 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 ErrIntOverflowEvents + } + 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: UserLoginAccessListInvalid: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserLoginAccessListInvalid: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 4: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConnectionMetadata", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AccessListInvalidMetadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -107617,13 +108651,13 @@ func (m *WorkloadIdentityUpdate) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.ConnectionMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.AccessListInvalidMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 5: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkloadIdentityData", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -107650,10 +108684,7 @@ func (m *WorkloadIdentityUpdate) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.WorkloadIdentityData == nil { - m.WorkloadIdentityData = &Struct{} - } - if err := m.WorkloadIdentityData.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -107679,7 +108710,7 @@ func (m *WorkloadIdentityUpdate) Unmarshal(dAtA []byte) error { } return nil } -func (m *WorkloadIdentityDelete) Unmarshal(dAtA []byte) error { +func (m *ContactCreate) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -107702,10 +108733,10 @@ func (m *WorkloadIdentityDelete) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: WorkloadIdentityDelete: wiretype end group for non-group") + return fmt.Errorf("proto: ContactCreate: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: WorkloadIdentityDelete: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ContactCreate: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -107840,62 +108871,11 @@ func (m *WorkloadIdentityDelete) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipEvents(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthEvents - } - 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 *AccessListInvalidMetadata) 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 ErrIntOverflowEvents - } - 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: AccessListInvalidMetadata: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AccessListInvalidMetadata: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AccessListName", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -107905,27 +108885,28 @@ func (m *AccessListInvalidMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthEvents } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthEvents } if postIndex > l { return io.ErrUnexpectedEOF } - m.AccessListName = string(dAtA[iNdEx:postIndex]) + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 2: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Email", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -107953,13 +108934,13 @@ func (m *AccessListInvalidMetadata) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.User = string(dAtA[iNdEx:postIndex]) + m.Email = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MissingRoles", wireType) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ContactType", wireType) } - var stringLen uint64 + m.ContactType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvents @@ -107969,24 +108950,11 @@ func (m *AccessListInvalidMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + m.ContactType |= ContactType(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthEvents - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthEvents - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.MissingRoles = append(m.MissingRoles, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -108009,7 +108977,7 @@ func (m *AccessListInvalidMetadata) Unmarshal(dAtA []byte) error { } return nil } -func (m *UserLoginAccessListInvalid) Unmarshal(dAtA []byte) error { +func (m *ContactDelete) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -108032,10 +109000,10 @@ func (m *UserLoginAccessListInvalid) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UserLoginAccessListInvalid: wiretype end group for non-group") + return fmt.Errorf("proto: ContactDelete: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UserLoginAccessListInvalid: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ContactDelete: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -108073,7 +109041,7 @@ func (m *UserLoginAccessListInvalid) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AccessListInvalidMetadata", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ResourceMetadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -108100,11 +109068,77 @@ func (m *UserLoginAccessListInvalid) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.AccessListInvalidMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UserMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ConnectionMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } @@ -108137,6 +109171,57 @@ func (m *UserLoginAccessListInvalid) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Email", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Email = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ContactType", wireType) + } + m.ContactType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ContactType |= ContactType(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) diff --git a/api/types/events/oneof.go b/api/types/events/oneof.go index 8aab26ac1d4bb..01a491cdbd593 100644 --- a/api/types/events/oneof.go +++ b/api/types/events/oneof.go @@ -806,6 +806,14 @@ func ToOneOf(in AuditEvent) (*OneOf, error) { out.Event = &OneOf_WorkloadIdentityDelete{ WorkloadIdentityDelete: e, } + case *ContactCreate: + out.Event = &OneOf_ContactCreate{ + ContactCreate: e, + } + case *ContactDelete: + out.Event = &OneOf_ContactDelete{ + ContactDelete: e, + } default: slog.ErrorContext(context.Background(), "Attempted to convert dynamic event of unknown type into protobuf event.", "event_type", in.GetType()) unknown := &Unknown{} diff --git a/api/types/session_tracker.go b/api/types/session_tracker.go index db07ea2578db5..2892db170085c 100644 --- a/api/types/session_tracker.go +++ b/api/types/session_tracker.go @@ -39,6 +39,7 @@ const ( DatabaseSessionKind SessionKind = "db" AppSessionKind SessionKind = "app" WindowsDesktopSessionKind SessionKind = "desktop" + UnknownSessionKind SessionKind = "" ) // SessionParticipantMode is the mode that determines what you can do when you join a session. diff --git a/api/version.go b/api/version.go index c1aa81f8bd305..caf6935ac7702 100644 --- a/api/version.go +++ b/api/version.go @@ -3,6 +3,6 @@ package api import "github.com/coreos/go-semver/semver" -const Version = "17.1.1" +const Version = "17.1.2" var SemVersion = semver.New(Version) diff --git a/build.assets/macos/tsh/tsh.app/Contents/Info.plist b/build.assets/macos/tsh/tsh.app/Contents/Info.plist index 53874cf22124e..5185e5c8c3f9c 100644 --- a/build.assets/macos/tsh/tsh.app/Contents/Info.plist +++ b/build.assets/macos/tsh/tsh.app/Contents/Info.plist @@ -19,13 +19,13 @@ CFBundlePackageType APPL CFBundleShortVersionString - 17.1.1 + 17.1.2 CFBundleSupportedPlatforms MacOSX CFBundleVersion - 17.1.1 + 17.1.2 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild diff --git a/build.assets/macos/tshdev/tsh.app/Contents/Info.plist b/build.assets/macos/tshdev/tsh.app/Contents/Info.plist index 037752de74f48..f57cbff316910 100644 --- a/build.assets/macos/tshdev/tsh.app/Contents/Info.plist +++ b/build.assets/macos/tshdev/tsh.app/Contents/Info.plist @@ -17,13 +17,13 @@ CFBundlePackageType APPL CFBundleShortVersionString - 17.1.1 + 17.1.2 CFBundleSupportedPlatforms MacOSX CFBundleVersion - 17.1.1 + 17.1.2 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild diff --git a/docs/config.json b/docs/config.json index ebc0f3b033a10..8dc4c113415a8 100644 --- a/docs/config.json +++ b/docs/config.json @@ -135,7 +135,7 @@ "aws_secret_access_key": "zyxw9876-this-is-an-example" }, "cloud": { - "version": "16.4.9", + "version": "16.4.11", "major_version": "16", "sla": { "monthly_percentage": "99.9%", @@ -185,18 +185,18 @@ "teleport": { "git": "api/14.0.0-gd1e081e", "major_version": "17", - "version": "17.1.1", + "version": "17.1.2", "url": "teleport.example.com", "golang": "1.23.4", "plugin": { - "version": "17.1.1" + "version": "17.1.2" }, "helm_repo_url": "https://charts.releases.teleport.dev", - "latest_oss_docker_image": "public.ecr.aws/gravitational/teleport-distroless:17.1.1", - "latest_oss_debug_docker_image": "public.ecr.aws/gravitational/teleport-distroless-debug:17.1.1", - "latest_ent_docker_image": "public.ecr.aws/gravitational/teleport-ent-distroless:17.1.1", - "latest_ent_debug_docker_image": "public.ecr.aws/gravitational/teleport-ent-distroless-debug:17.1.1", - "teleport_install_script_url": "https://cdn.teleport.dev/install-v17.1.1.sh" + "latest_oss_docker_image": "public.ecr.aws/gravitational/teleport-distroless:17.1.2", + "latest_oss_debug_docker_image": "public.ecr.aws/gravitational/teleport-distroless-debug:17.1.2", + "latest_ent_docker_image": "public.ecr.aws/gravitational/teleport-ent-distroless:17.1.2", + "latest_ent_debug_docker_image": "public.ecr.aws/gravitational/teleport-ent-distroless-debug:17.1.2", + "teleport_install_script_url": "https://cdn.teleport.dev/install-v17.1.2.sh" }, "terraform": { "version": "1.0.0" diff --git a/docs/img/connect-your-client/webui-db-sessions/connect-dialog-custom.png b/docs/img/connect-your-client/webui-db-sessions/connect-dialog-custom.png new file mode 100644 index 0000000000000..1ed7f0e494230 Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/connect-dialog-custom.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/connect-dialog.png b/docs/img/connect-your-client/webui-db-sessions/connect-dialog.png new file mode 100644 index 0000000000000..f1614f0a2826e Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/connect-dialog.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/resources-connect-dialog.png b/docs/img/connect-your-client/webui-db-sessions/resources-connect-dialog.png new file mode 100644 index 0000000000000..51137d8769469 Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/resources-connect-dialog.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/resources-list.png b/docs/img/connect-your-client/webui-db-sessions/resources-list.png new file mode 100644 index 0000000000000..447fe1a42a13a Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/resources-list.png differ diff --git a/docs/img/connect-your-client/webui-db-sessions/session-terminal.png b/docs/img/connect-your-client/webui-db-sessions/session-terminal.png new file mode 100644 index 0000000000000..c062ec00e2ace Binary files /dev/null and b/docs/img/connect-your-client/webui-db-sessions/session-terminal.png differ diff --git a/docs/pages/connect-your-client/web-ui.mdx b/docs/pages/connect-your-client/web-ui.mdx index be05318f09d57..9bce88e3eac52 100644 --- a/docs/pages/connect-your-client/web-ui.mdx +++ b/docs/pages/connect-your-client/web-ui.mdx @@ -65,3 +65,57 @@ After you save and exit the editor, `tctl` will update the resource: cluster networking configuration has been updated ``` +## Starting a database session + +Starting from version `17.1`, users can establish database sessions using the +Teleport Web UI. Currently, it is supported in PostgreSQL databases. + +To start a new session, locate your database in the resources list and click +"Connect". + +![Resources list](../../img/connect-your-client/webui-db-sessions/resources-list.png) + +For supported databases, the dialog will present the option to start +the session in your browser. + +![Resources list database connect dialog](../../img/connect-your-client/webui-db-sessions/resources-connect-dialog.png) + +After clicking on the "Connect in the browser" button, a new tab will open with +a form. Teleport will pre-fill this form based on your permissions, but you can +adjust the options as needed. + +![New database session connect dialog](../../img/connect-your-client/webui-db-sessions/connect-dialog.png) + +If your user has wildcard permissions (*), you can type custom values into the +form fields. This allows flexibility in selecting specific databases or +credentials. + +![New database session connect dialog with custom values](../../img/connect-your-client/webui-db-sessions/connect-dialog-custom.png) + +Once you've filled in the session details, click the "Connect" button. Your +session will start, and a terminal interface will appear in the browser. + +The browser-based terminal allows you to execute queries and interact with your +database. Follow the on-screen instructions to see available commands and +limitations. + +![Database session terminal](../../img/connect-your-client/webui-db-sessions/session-terminal.png) + + + While the terminal interface provided in the Teleport Web UI is designed to + resemble popular database CLIs such as `psql`, it is a custom implementation + with some differences and limitations: + - **Feature Set:** Not all features available in `psql` are implemented. + For instance, scripting capabilities, query cancellation, or informational + commands like `\d` or `\dt` are currently unsupported. + - **Error Handling:** Error messages and diagnostics might differ from what + users are accustomed to in `psql`. + + These distinctions are designed to maintain a lightweight and secure interface + directly in your browser. For more complex operations, you may prefer + accessing your database from your terminal using `tsh` and your preferred + tool. + + Future updates may expand functionality or address feedback based on user + needs and supported databases. + diff --git a/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx b/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx index 77e487d14bb3b..87be741c8513d 100644 --- a/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-aws-databases/postgres-redshift.mdx @@ -205,6 +205,8 @@ $ tsh db connect --db-user=role/example-iam-role --db-name=dev my-redshift +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx b/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx index 4837cb6e7a4e4..a07c4b6e4c73d 100644 --- a/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-aws-databases/rds.mdx @@ -295,6 +295,8 @@ Retrieve credentials for the database and connect to it as the `alice` user: $ tsh db connect --db-user=alice --db-name=postgres rds-example ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + The appropriate database command-line client (`psql`, `mysql`, `mariadb`) should be available in `PATH` in order to be able to connect. diff --git a/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx b/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx index 8de82a3035a91..7b01cb3a2a959 100644 --- a/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-aws-databases/redshift-serverless.mdx @@ -235,6 +235,8 @@ Type "help" for help. dev=> ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx b/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx index b639ab2a0673a..e18afa60ca7e9 100644 --- a/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-azure-databases/azure-postgres-mysql.mdx @@ -428,6 +428,8 @@ $ tsh db connect --db-user=teleport --db-name=postgres azure-db +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + The appropriate database command-line client (`psql`, `mysql`) should be available in the `PATH` of the machine you're running `tsh db connect` from. diff --git a/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx b/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx index 15a3d9ece0f22..05f58d2e21788 100644 --- a/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-google-cloud-databases/postgres-cloudsql.mdx @@ -149,6 +149,8 @@ Retrieve credentials for the "cloudsql" example database and connect to it: $ tsh db connect --db-user=cloudsql-user@.iam --db-name=postgres cloudsql ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx b/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx index 1cbb3d8a0f427..02acd22cd3fdc 100644 --- a/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx +++ b/docs/pages/enroll-resources/database-access/enroll-self-hosted-databases/postgres-self-hosted.mdx @@ -142,6 +142,8 @@ To retrieve credentials for a database and connect to it: $ tsh db connect --db-user=postgres --db-name=postgres example-postgres ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx b/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx index 2d55518cc26ec..891efcc664187 100644 --- a/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx +++ b/docs/pages/includes/database-access/auto-user-provisioning/connect.mdx @@ -2,6 +2,8 @@ Now, log into your Teleport cluster and connect to the database: +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + ```code $ tsh login --proxy=teleport.example.com $ tsh db connect --db-name example diff --git a/docs/pages/includes/database-access/pg-access-webui.mdx b/docs/pages/includes/database-access/pg-access-webui.mdx new file mode 100644 index 0000000000000..5f6f7cddb4b33 --- /dev/null +++ b/docs/pages/includes/database-access/pg-access-webui.mdx @@ -0,0 +1,3 @@ + + Starting from version `17.1`, you can now [access your PostgreSQL databases using the Web UI.](../../connect-your-client/web-ui.mdx#starting-a-database-session) + diff --git a/docs/pages/includes/database-access/rds-proxy.mdx b/docs/pages/includes/database-access/rds-proxy.mdx index 348708e348aa9..dccbc48e02bc9 100644 --- a/docs/pages/includes/database-access/rds-proxy.mdx +++ b/docs/pages/includes/database-access/rds-proxy.mdx @@ -184,6 +184,8 @@ Retrieve credentials for the database and connect to it as the `alice` user: $ tsh db connect --db-user=alice --db-name=dev rds-proxy ``` +(!docs/pages/includes/database-access/pg-access-webui.mdx!) + To log out of the database and remove credentials: ```code diff --git a/docs/pages/includes/database-access/tctl-auth-sign-3-files.mdx b/docs/pages/includes/database-access/tctl-auth-sign-3-files.mdx index 253c54b2121b4..a84fab0c7d9ad 100644 --- a/docs/pages/includes/database-access/tctl-auth-sign-3-files.mdx +++ b/docs/pages/includes/database-access/tctl-auth-sign-3-files.mdx @@ -30,12 +30,12 @@ the database, follow these instructions on your workstation: ``` 1. Export Teleport's certificate authority and a generate certificate/key pair. - This example generates a certificate with a 1-year validity period. + This example generates a certificate with a 90-day validity period. `db.example.com` is the hostname where the Teleport Database Service can reach the {{ dbname }} server. ```code - $ tctl auth sign --format={{ format }} --host=db.example.com --out=server --ttl=2190h + $ tctl auth sign --format={{ format }} --host=db.example.com --out=server --ttl=2160h ``` (!docs/pages/includes/database-access/ttl-note.mdx!) diff --git a/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx b/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx index 92e37199d2dd6..0e6dfabb1b4d4 100644 --- a/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/data-sources.mdx @@ -34,3 +34,4 @@ The Teleport Terraform provider supports the following data-sources: - [`teleport_trusted_cluster`](./trusted_cluster.mdx) - [`teleport_trusted_device`](./trusted_device.mdx) - [`teleport_user`](./user.mdx) + - [`teleport_workload_identity`](./workload_identity.mdx) diff --git a/docs/pages/reference/terraform-provider/data-sources/workload_identity.mdx b/docs/pages/reference/terraform-provider/data-sources/workload_identity.mdx new file mode 100644 index 0000000000000..2298d5363d77c --- /dev/null +++ b/docs/pages/reference/terraform-provider/data-sources/workload_identity.mdx @@ -0,0 +1,69 @@ +--- +title: Reference for the teleport_workload_identity Terraform data-source +sidebar_label: workload_identity +description: This page describes the supported values of the teleport_workload_identity data-source of the Teleport Terraform provider. +--- + +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + + + + + +{/* schema generated by tfplugindocs */} +## Schema + +### Optional + +- `metadata` (Attributes) Common metadata that all resources share. (see [below for nested schema](#nested-schema-for-metadata)) +- `spec` (Attributes) The configured properties of the WorkloadIdentity (see [below for nested schema](#nested-schema-for-spec)) +- `sub_kind` (String) Differentiates variations of the same kind. All resources should contain one, even if it is never populated. +- `version` (String) The version of the resource being represented. + +### Nested Schema for `metadata` + +Optional: + +- `description` (String) description is object description. +- `expires` (String) expires is a global expiry time header can be set on any resource in the system. +- `labels` (Map of String) labels is a set of labels. +- `name` (String) name is an object name. + + +### Nested Schema for `spec` + +Optional: + +- `rules` (Attributes) The rules which are evaluated before the WorkloadIdentity can be issued. (see [below for nested schema](#nested-schema-for-specrules)) +- `spiffe` (Attributes) Configuration pertaining to the issuance of SPIFFE-compatible workload identity credentials. (see [below for nested schema](#nested-schema-for-specspiffe)) + +### Nested Schema for `spec.rules` + +Optional: + +- `allow` (Attributes List) A list of rules used to determine if a WorkloadIdentity can be issued. If none are provided, it will be considered a pass. If any are provided, then at least one must pass for the rules to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallow)) + +### Nested Schema for `spec.rules.allow` + +Optional: + +- `conditions` (Attributes List) The conditions that must be met for this rule to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallowconditions)) + +### Nested Schema for `spec.rules.allow.conditions` + +Optional: + +- `attribute` (String) The name of the attribute to evaluate the condition against. +- `equals` (String) An exact string that the attribute must match. + + + + +### Nested Schema for `spec.spiffe` + +Optional: + +- `hint` (String) A freeform text field which is provided to workloads along with a credential produced by this WorkloadIdentity. This can be used to provide additional context that can be used to select between multiple credentials. +- `id` (String) The path of the SPIFFE ID that will be issued to the workload. This should be prefixed with a forward-slash ("/"). This field supports templating using attributes. + diff --git a/docs/pages/reference/terraform-provider/resources/resources.mdx b/docs/pages/reference/terraform-provider/resources/resources.mdx index 51d7bb8d2e3b3..e962f85c38abb 100644 --- a/docs/pages/reference/terraform-provider/resources/resources.mdx +++ b/docs/pages/reference/terraform-provider/resources/resources.mdx @@ -36,3 +36,4 @@ The Teleport Terraform provider supports the following resources: - [`teleport_trusted_cluster`](./trusted_cluster.mdx) - [`teleport_trusted_device`](./trusted_device.mdx) - [`teleport_user`](./user.mdx) + - [`teleport_workload_identity`](./workload_identity.mdx) diff --git a/docs/pages/reference/terraform-provider/resources/workload_identity.mdx b/docs/pages/reference/terraform-provider/resources/workload_identity.mdx new file mode 100644 index 0000000000000..a9d3da4bc7a73 --- /dev/null +++ b/docs/pages/reference/terraform-provider/resources/workload_identity.mdx @@ -0,0 +1,94 @@ +--- +title: Reference for the teleport_workload_identity Terraform resource +sidebar_label: workload_identity +description: This page describes the supported values of the teleport_workload_identity resource of the Teleport Terraform provider. +--- + +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + + + +## Example Usage + +```hcl +resource "teleport_workload_identity" "example" { + version = "v1" + metadata = { + name = "example" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "noah" + }] + } + ] + } + spiffe = { + id = "/my/spiffe/id/path" + hint = "my-hint" + } + } +} +``` + +{/* schema generated by tfplugindocs */} +## Schema + +### Optional + +- `metadata` (Attributes) Common metadata that all resources share. (see [below for nested schema](#nested-schema-for-metadata)) +- `spec` (Attributes) The configured properties of the WorkloadIdentity (see [below for nested schema](#nested-schema-for-spec)) +- `sub_kind` (String) Differentiates variations of the same kind. All resources should contain one, even if it is never populated. +- `version` (String) The version of the resource being represented. + +### Nested Schema for `metadata` + +Optional: + +- `description` (String) description is object description. +- `expires` (String) expires is a global expiry time header can be set on any resource in the system. +- `labels` (Map of String) labels is a set of labels. +- `name` (String) name is an object name. + + +### Nested Schema for `spec` + +Optional: + +- `rules` (Attributes) The rules which are evaluated before the WorkloadIdentity can be issued. (see [below for nested schema](#nested-schema-for-specrules)) +- `spiffe` (Attributes) Configuration pertaining to the issuance of SPIFFE-compatible workload identity credentials. (see [below for nested schema](#nested-schema-for-specspiffe)) + +### Nested Schema for `spec.rules` + +Optional: + +- `allow` (Attributes List) A list of rules used to determine if a WorkloadIdentity can be issued. If none are provided, it will be considered a pass. If any are provided, then at least one must pass for the rules to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallow)) + +### Nested Schema for `spec.rules.allow` + +Optional: + +- `conditions` (Attributes List) The conditions that must be met for this rule to be considered passed. (see [below for nested schema](#nested-schema-for-specrulesallowconditions)) + +### Nested Schema for `spec.rules.allow.conditions` + +Optional: + +- `attribute` (String) The name of the attribute to evaluate the condition against. +- `equals` (String) An exact string that the attribute must match. + + + + +### Nested Schema for `spec.spiffe` + +Optional: + +- `hint` (String) A freeform text field which is provided to workloads along with a credential produced by this WorkloadIdentity. This can be used to provide additional context that can be used to select between multiple credentials. +- `id` (String) The path of the SPIFFE ID that will be issued to the workload. This should be prefixed with a forward-slash ("/"). This field supports templating using attributes. + diff --git a/e b/e index fb6c1019bfc0b..0bfbd98dde0d6 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit fb6c1019bfc0b6887ff3cbbcc2367360810fa8a1 +Subproject commit 0bfbd98dde0d6d20e7139234889d5df574da531c diff --git a/examples/chart/access/datadog/Chart.yaml b/examples/chart/access/datadog/Chart.yaml index d8b05d6f6e8a3..af6d9934ce347 100644 --- a/examples/chart/access/datadog/Chart.yaml +++ b/examples/chart/access/datadog/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-datadog diff --git a/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap index 2e62fe34629f9..bb778d31c7594 100644 --- a/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/datadog/tests/__snapshot__/configmap_test.yaml.snap @@ -26,6 +26,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-datadog - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-datadog-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-datadog-17.1.2 name: RELEASE-NAME-teleport-plugin-datadog diff --git a/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap index ef7070fec6a91..e9a3fbd5cccf0 100644 --- a/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/datadog/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-datadog - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-datadog-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-datadog-17.1.2 name: RELEASE-NAME-teleport-plugin-datadog spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-datadog - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-datadog-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-datadog-17.1.2 spec: containers: - command: diff --git a/examples/chart/access/discord/Chart.yaml b/examples/chart/access/discord/Chart.yaml index 0a4e380e401c5..3f2d04cf8d01d 100644 --- a/examples/chart/access/discord/Chart.yaml +++ b/examples/chart/access/discord/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-discord diff --git a/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap index 101486bdbd696..eccbf15241262 100644 --- a/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/discord/tests/__snapshot__/configmap_test.yaml.snap @@ -24,6 +24,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-discord - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-discord-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-discord-17.1.2 name: RELEASE-NAME-teleport-plugin-discord diff --git a/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap index fc2195ea55124..c22cdfe3b993d 100644 --- a/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/discord/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-discord - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-discord-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-discord-17.1.2 name: RELEASE-NAME-teleport-plugin-discord spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-discord - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-discord-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-discord-17.1.2 spec: containers: - command: diff --git a/examples/chart/access/email/Chart.yaml b/examples/chart/access/email/Chart.yaml index 0e799a4f3699d..e377e7cffaf7f 100644 --- a/examples/chart/access/email/Chart.yaml +++ b/examples/chart/access/email/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-email diff --git a/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap index 089cfcbcc5633..2674924f7c3d9 100644 --- a/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/email/tests/__snapshot__/configmap_test.yaml.snap @@ -26,8 +26,8 @@ should match the snapshot (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on): 1: | @@ -59,8 +59,8 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, no starttls): 1: | @@ -92,8 +92,8 @@ should match the snapshot (smtp on, no starttls): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, password file): 1: | @@ -125,8 +125,8 @@ should match the snapshot (smtp on, password file): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, roleToRecipients set): 1: | @@ -161,8 +161,8 @@ should match the snapshot (smtp on, roleToRecipients set): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email should match the snapshot (smtp on, starttls disabled): 1: | @@ -194,6 +194,6 @@ should match the snapshot (smtp on, starttls disabled): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email diff --git a/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap index 593aead992053..1b5a9b78de452 100644 --- a/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/email/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should be possible to override volume name (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -22,8 +22,8 @@ should be possible to override volume name (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 spec: containers: - command: @@ -34,7 +34,7 @@ should be possible to override volume name (smtp on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -75,8 +75,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -90,8 +90,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 spec: containers: - command: @@ -136,8 +136,8 @@ should match the snapshot (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -151,8 +151,8 @@ should match the snapshot (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 spec: containers: - command: @@ -163,7 +163,7 @@ should match the snapshot (mailgun on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -204,8 +204,8 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -219,8 +219,8 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 spec: containers: - command: @@ -231,7 +231,7 @@ should match the snapshot (smtp on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -272,8 +272,8 @@ should mount external secret (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -287,8 +287,8 @@ should mount external secret (mailgun on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 spec: containers: - command: @@ -299,7 +299,7 @@ should mount external secret (mailgun on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: @@ -340,8 +340,8 @@ should mount external secret (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 name: RELEASE-NAME-teleport-plugin-email spec: replicas: 1 @@ -355,8 +355,8 @@ should mount external secret (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-email - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-email-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-email-17.1.2 spec: containers: - command: @@ -367,7 +367,7 @@ should mount external secret (smtp on): env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-email:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-email ports: diff --git a/examples/chart/access/jira/Chart.yaml b/examples/chart/access/jira/Chart.yaml index ed5a736b67d0f..6ca8dbd150f0f 100644 --- a/examples/chart/access/jira/Chart.yaml +++ b/examples/chart/access/jira/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-jira diff --git a/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap index 2a86bfa1c8f11..92eaa22d5cf2b 100644 --- a/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/jira/tests/__snapshot__/configmap_test.yaml.snap @@ -32,6 +32,6 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-jira - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-jira-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-jira-17.1.2 name: RELEASE-NAME-teleport-plugin-jira diff --git a/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap index 3ad0b2966319e..856b0f5ddf1c8 100644 --- a/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/jira/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-jira - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-jira-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-jira-17.1.2 name: RELEASE-NAME-teleport-plugin-jira spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-jira - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-jira-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-jira-17.1.2 spec: containers: - command: diff --git a/examples/chart/access/mattermost/Chart.yaml b/examples/chart/access/mattermost/Chart.yaml index 668019b392ee5..67d7ea9172b2d 100644 --- a/examples/chart/access/mattermost/Chart.yaml +++ b/examples/chart/access/mattermost/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-mattermost diff --git a/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap index 927c8362c0568..10564216a75aa 100644 --- a/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/mattermost/tests/__snapshot__/configmap_test.yaml.snap @@ -22,6 +22,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-mattermost-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-mattermost-17.1.2 name: RELEASE-NAME-teleport-plugin-mattermost diff --git a/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap index cb11fe2f0f6ad..c7530391033a3 100644 --- a/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/mattermost/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-mattermost-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-mattermost-17.1.2 name: RELEASE-NAME-teleport-plugin-mattermost spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-mattermost-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-mattermost-17.1.2 spec: containers: - command: @@ -75,8 +75,8 @@ should mount external secret: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-mattermost-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-mattermost-17.1.2 name: RELEASE-NAME-teleport-plugin-mattermost spec: replicas: 1 @@ -90,8 +90,8 @@ should mount external secret: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-mattermost-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-mattermost-17.1.2 spec: containers: - command: @@ -102,7 +102,7 @@ should mount external secret: env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-mattermost:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-mattermost:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-mattermost ports: @@ -143,8 +143,8 @@ should override volume name: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-mattermost-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-mattermost-17.1.2 name: RELEASE-NAME-teleport-plugin-mattermost spec: replicas: 1 @@ -158,8 +158,8 @@ should override volume name: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-mattermost - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-mattermost-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-mattermost-17.1.2 spec: containers: - command: @@ -170,7 +170,7 @@ should override volume name: env: - name: TELEPORT_PLUGIN_FAIL_FAST value: "true" - image: public.ecr.aws/gravitational/teleport-plugin-mattermost:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-mattermost:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-mattermost ports: diff --git a/examples/chart/access/msteams/Chart.yaml b/examples/chart/access/msteams/Chart.yaml index 7d619ecaa796d..0e69391eb41e7 100644 --- a/examples/chart/access/msteams/Chart.yaml +++ b/examples/chart/access/msteams/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-msteams diff --git a/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap index 5d58bcf85cf9b..87d635f1df779 100644 --- a/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/msteams/tests/__snapshot__/configmap_test.yaml.snap @@ -29,6 +29,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-msteams - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-msteams-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-msteams-17.1.2 name: RELEASE-NAME-teleport-plugin-msteams diff --git a/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap index 86e0735d6208d..a6f05e09b8487 100644 --- a/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/msteams/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-msteams - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-msteams-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-msteams-17.1.2 name: RELEASE-NAME-teleport-plugin-msteams spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-msteams - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-msteams-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-msteams-17.1.2 spec: containers: - command: diff --git a/examples/chart/access/pagerduty/Chart.yaml b/examples/chart/access/pagerduty/Chart.yaml index 7125821ebdec8..9fa63eeebfae3 100644 --- a/examples/chart/access/pagerduty/Chart.yaml +++ b/examples/chart/access/pagerduty/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-pagerduty diff --git a/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap index 2bb96e917c515..9d5a6e9b6594a 100644 --- a/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/pagerduty/tests/__snapshot__/configmap_test.yaml.snap @@ -21,6 +21,6 @@ should match the snapshot (smtp on): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-pagerduty - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-pagerduty-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-pagerduty-17.1.2 name: RELEASE-NAME-teleport-plugin-pagerduty diff --git a/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap index 7172a804538c9..2c12e7939ac91 100644 --- a/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/pagerduty/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-pagerduty - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-pagerduty-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-pagerduty-17.1.2 name: RELEASE-NAME-teleport-plugin-pagerduty spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-pagerduty - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-pagerduty-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-pagerduty-17.1.2 spec: containers: - command: diff --git a/examples/chart/access/slack/Chart.yaml b/examples/chart/access/slack/Chart.yaml index b355632c95e8a..5a1a09786f2b8 100644 --- a/examples/chart/access/slack/Chart.yaml +++ b/examples/chart/access/slack/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-slack diff --git a/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap index 87b0400101a8a..b4f17d6aa8049 100644 --- a/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/access/slack/tests/__snapshot__/configmap_test.yaml.snap @@ -24,6 +24,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-slack - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-slack-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-slack-17.1.2 name: RELEASE-NAME-teleport-plugin-slack diff --git a/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap index 1a72ab63e3523..a5111c834e4bd 100644 --- a/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/access/slack/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-slack - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-slack-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-slack-17.1.2 name: RELEASE-NAME-teleport-plugin-slack spec: replicas: 1 @@ -22,8 +22,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-slack - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-slack-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-slack-17.1.2 spec: containers: - command: diff --git a/examples/chart/event-handler/Chart.yaml b/examples/chart/event-handler/Chart.yaml index a79de9a00ce4a..1a22e926fea6c 100644 --- a/examples/chart/event-handler/Chart.yaml +++ b/examples/chart/event-handler/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" apiVersion: v2 name: teleport-plugin-event-handler diff --git a/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap b/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap index 9f5583967f3c4..5cb74b7e4ecb3 100644 --- a/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap +++ b/examples/chart/event-handler/tests/__snapshot__/configmap_test.yaml.snap @@ -26,6 +26,6 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-event-handler - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-event-handler-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-event-handler-17.1.2 name: RELEASE-NAME-teleport-plugin-event-handler diff --git a/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap index b173ed4735bba..ed9900ba91222 100644 --- a/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap @@ -7,8 +7,8 @@ should match the snapshot: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-plugin-event-handler - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-plugin-event-handler-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-plugin-event-handler-17.1.2 name: RELEASE-NAME-teleport-plugin-event-handler spec: replicas: 1 @@ -82,7 +82,7 @@ should mount tls.existingCASecretName and set environment when set in values: value: "true" - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-plugin-event-handler:17.1.1 + image: public.ecr.aws/gravitational/teleport-plugin-event-handler:17.1.2 imagePullPolicy: IfNotPresent name: teleport-plugin-event-handler ports: diff --git a/examples/chart/tbot/.lint/full.yaml b/examples/chart/tbot/.lint/full.yaml index 706b3d207ee5c..9d076782b506e 100644 --- a/examples/chart/tbot/.lint/full.yaml +++ b/examples/chart/tbot/.lint/full.yaml @@ -1,7 +1,7 @@ clusterName: "test.teleport.sh" teleportAuthAddress: "my-auth:3024" defaultOutput: - enabled: false + enabled: true token: "my-token" joinMethod: "modified-join-method" diff --git a/examples/chart/tbot/Chart.yaml b/examples/chart/tbot/Chart.yaml index 659070fe0bb83..4435abf990d60 100644 --- a/examples/chart/tbot/Chart.yaml +++ b/examples/chart/tbot/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" name: tbot apiVersion: v2 diff --git a/examples/chart/tbot/templates/_config.tpl b/examples/chart/tbot/templates/_config.tpl index 344b25e403fc5..d8c9aff491862 100644 --- a/examples/chart/tbot/templates/_config.tpl +++ b/examples/chart/tbot/templates/_config.tpl @@ -40,10 +40,10 @@ outputs: name: {{ include "tbot.defaultOutputName" . }} {{- end }} {{- if .Values.outputs }} -{{- toYaml .Values.outputs | nindent 6}} +{{- toYaml .Values.outputs | nindent 2}} {{- end }} {{- end }} {{- if .Values.services }} -services: {{- toYaml .Values.services | nindent 6}} +services: {{- toYaml .Values.services | nindent 2}} {{- end }} {{- end -}} diff --git a/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap b/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap index a1e58c58b4643..2dbfb81532f58 100644 --- a/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap +++ b/examples/chart/tbot/tests/__snapshot__/config_test.yaml.snap @@ -35,6 +35,10 @@ should match the snapshot (full): join_method: modified-join-method token: my-token outputs: + - destination: + name: RELEASE-NAME-tbot-out + type: kubernetes_secret + type: identity - app_name: foo destination: path: /bar diff --git a/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap index d8353e0a90436..db353cabc7134 100644 --- a/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/tbot/tests/__snapshot__/deployment_test.yaml.snap @@ -22,14 +22,14 @@ should match the snapshot (full): template: metadata: annotations: - checksum/config: 094cdbfc4e4fe3824a33426d8eea4e9e8a4b2711823d4fbb4102e11caa7f62c0 + checksum/config: 010d3421120a26bed12d1b9df8443e0eeafa362e88bd830e4a81688d13689483 test-key: test-annotation-pod labels: app.kubernetes.io/component: tbot app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: tbot - helm.sh/chart: tbot-17.1.1 + helm.sh/chart: tbot-17.1.2 test-key: test-label-pod spec: affinity: @@ -68,7 +68,7 @@ should match the snapshot (full): value: "1" - name: TEST_ENV value: test-value - image: public.ecr.aws/gravitational/tbot-distroless:17.1.1 + image: public.ecr.aws/gravitational/tbot-distroless:17.1.2 imagePullPolicy: Always livenessProbe: failureThreshold: 6 @@ -154,7 +154,7 @@ should match the snapshot (simple): app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: tbot - helm.sh/chart: tbot-17.1.1 + helm.sh/chart: tbot-17.1.2 spec: containers: - args: @@ -176,7 +176,7 @@ should match the snapshot (simple): fieldPath: spec.nodeName - name: KUBERNETES_TOKEN_PATH value: /var/run/secrets/tokens/join-sa-token - image: public.ecr.aws/gravitational/tbot-distroless:17.1.1 + image: public.ecr.aws/gravitational/tbot-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/examples/chart/teleport-cluster/Chart.yaml b/examples/chart/teleport-cluster/Chart.yaml index 0821369f96635..537648cfb7138 100644 --- a/examples/chart/teleport-cluster/Chart.yaml +++ b/examples/chart/teleport-cluster/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" name: teleport-cluster apiVersion: v2 diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml index 4a94fd4afb9b5..977e2e6b9792d 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" name: teleport-operator apiVersion: v2 diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap index 8682779208346..7f12c8497ddaf 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap @@ -8,8 +8,8 @@ adds operator permissions to ClusterRole: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-cluster-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-cluster-17.1.2 teleport.dev/majorVersion: "17" name: RELEASE-NAME rules: diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap index b729b1b439d52..e91a45014f311 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_config_test.yaml.snap @@ -1848,8 +1848,8 @@ sets clusterDomain on Configmap: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-cluster-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-cluster-17.1.2 teleport.dev/majorVersion: "17" name: RELEASE-NAME-auth namespace: NAMESPACE diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap index 4c13d015c4572..240366c3a79d3 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap @@ -8,7 +8,7 @@ - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -141,7 +141,7 @@ should set nodeSelector when set in values: - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -238,7 +238,7 @@ should set resources when set in values: - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -324,7 +324,7 @@ should set securityContext when set in values: - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap index 2567287d235b3..720b2144ab303 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_config_test.yaml.snap @@ -567,8 +567,8 @@ sets clusterDomain on Configmap: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-cluster-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-cluster-17.1.2 teleport.dev/majorVersion: "17" name: RELEASE-NAME-proxy namespace: NAMESPACE diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap index 526abf2578815..23929e5f23810 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap @@ -11,8 +11,8 @@ sets clusterDomain on Deployment Pods: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-cluster-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-cluster-17.1.2 teleport.dev/majorVersion: "17" name: RELEASE-NAME-proxy namespace: NAMESPACE @@ -26,7 +26,7 @@ sets clusterDomain on Deployment Pods: template: metadata: annotations: - checksum/config: f8c36fbfefbeb49c341d941eed325ab6be97bbee82ae23e797b7a6ced382d593 + checksum/config: c573a6b130ea20b773a595c566510c7a5017fa844498efad2fd235f45b2e2311 kubernetes.io/pod: test-annotation kubernetes.io/pod-different: 4 labels: @@ -34,8 +34,8 @@ sets clusterDomain on Deployment Pods: app.kubernetes.io/instance: RELEASE-NAME app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: teleport-cluster - app.kubernetes.io/version: 17.1.1 - helm.sh/chart: teleport-cluster-17.1.1 + app.kubernetes.io/version: 17.1.2 + helm.sh/chart: teleport-cluster-17.1.2 teleport.dev/majorVersion: "17" spec: affinity: @@ -44,7 +44,7 @@ sets clusterDomain on Deployment Pods: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -105,7 +105,7 @@ sets clusterDomain on Deployment Pods: - wait - no-resolve - RELEASE-NAME-auth-v16.NAMESPACE.svc.test.com - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 name: wait-auth-update serviceAccountName: RELEASE-NAME-proxy terminationGracePeriodSeconds: 60 @@ -137,7 +137,7 @@ should provision initContainer correctly when set in values: - wait - no-resolve - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 name: wait-auth-update resources: limits: @@ -201,7 +201,7 @@ should set nodeSelector when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -262,7 +262,7 @@ should set nodeSelector when set in values: - wait - no-resolve - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 name: wait-auth-update nodeSelector: environment: security @@ -313,7 +313,7 @@ should set resources for wait-auth-update initContainer when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -381,7 +381,7 @@ should set resources for wait-auth-update initContainer when set in values: - wait - no-resolve - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 name: wait-auth-update resources: limits: @@ -421,7 +421,7 @@ should set resources when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -489,7 +489,7 @@ should set resources when set in values: - wait - no-resolve - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 name: wait-auth-update resources: limits: @@ -529,7 +529,7 @@ should set securityContext for initContainers when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -597,7 +597,7 @@ should set securityContext for initContainers when set in values: - wait - no-resolve - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 name: wait-auth-update securityContext: allowPrivilegeEscalation: false @@ -637,7 +637,7 @@ should set securityContext when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -705,7 +705,7 @@ should set securityContext when set in values: - wait - no-resolve - RELEASE-NAME-auth-v16.NAMESPACE.svc.cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 name: wait-auth-update securityContext: allowPrivilegeEscalation: false diff --git a/examples/chart/teleport-kube-agent/Chart.yaml b/examples/chart/teleport-kube-agent/Chart.yaml index 7385f1f394a80..eedb75037d1d5 100644 --- a/examples/chart/teleport-kube-agent/Chart.yaml +++ b/examples/chart/teleport-kube-agent/Chart.yaml @@ -1,4 +1,4 @@ -.version: &version "17.1.1" +.version: &version "17.1.2" name: teleport-kube-agent apiVersion: v2 diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap index a6582820ca5e8..e2efec0c5c57b 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap @@ -32,7 +32,7 @@ sets Deployment annotations when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -109,7 +109,7 @@ sets Deployment labels when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -173,7 +173,7 @@ sets Pod annotations when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -237,7 +237,7 @@ sets Pod labels when specified if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -322,7 +322,7 @@ should add emptyDir for data when existingDataVolume is not set if action is Upg value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -387,7 +387,7 @@ should add insecureSkipProxyTLSVerify to args when set in values if action is Up value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -451,7 +451,7 @@ should correctly configure existingDataVolume when set if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -513,7 +513,7 @@ should expose diag port if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -589,7 +589,7 @@ should have multiple replicas when replicaCount is set (using .replicaCount, dep value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -665,7 +665,7 @@ should have multiple replicas when replicaCount is set (using highAvailability.r value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -729,7 +729,7 @@ should have one replica when replicaCount is not set if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -793,7 +793,7 @@ should mount extraVolumes and extraVolumeMounts if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -862,7 +862,7 @@ should mount jamfCredentialsSecret if it already exists and when role is jamf an value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -932,7 +932,7 @@ should mount jamfCredentialsSecret.name when role is jamf and action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1004,7 +1004,7 @@ should mount tls.existingCASecretName and set environment when set in values if value: cluster.local - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1078,7 +1078,7 @@ should mount tls.existingCASecretName and set extra environment when set in valu value: http://username:password@my.proxy.host:3128 - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1148,7 +1148,7 @@ should provision initContainer correctly when set in values if action is Upgrade value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1270,7 +1270,7 @@ should set affinity when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1334,7 +1334,7 @@ should set default serviceAccountName when not set in values if action is Upgrad value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1411,7 +1411,7 @@ should set environment when extraEnv set in values if action is Upgrade: value: cluster.local - name: HTTPS_PROXY value: http://username:password@my.proxy.host:3128 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1539,7 +1539,7 @@ should set imagePullPolicy when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: Always livenessProbe: failureThreshold: 6 @@ -1603,7 +1603,7 @@ should set nodeSelector if set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1669,7 +1669,7 @@ should set not set priorityClassName when not set in values if action is Upgrade value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1745,7 +1745,7 @@ should set preferred affinity when more than one replica is used if action is Up value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1809,7 +1809,7 @@ should set priorityClassName when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1874,7 +1874,7 @@ should set probeTimeoutSeconds when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1948,7 +1948,7 @@ should set required affinity when highAvailability.requireAntiAffinity is set if value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2012,7 +2012,7 @@ should set resources when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2083,7 +2083,7 @@ should set serviceAccountName when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2147,7 +2147,7 @@ should set tolerations when set in values if action is Upgrade: value: "true" - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap index c51a84a12c1b7..03a6ee1c48405 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/job_test.yaml.snap @@ -25,7 +25,7 @@ should create ServiceAccount for post-delete hook by default: fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -108,7 +108,7 @@ should not create ServiceAccount for post-delete hook if serviceAccount.create i fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -138,7 +138,7 @@ should not create ServiceAccount, Role or RoleBinding for post-delete hook if se fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -168,7 +168,7 @@ should set nodeSelector in post-delete hook: fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent name: post-delete-job securityContext: @@ -200,7 +200,7 @@ should set resources in the Job's pod spec if resources is set in values: fieldPath: metadata.namespace - name: RELEASE_NAME value: RELEASE-NAME - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent name: post-delete-job resources: diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap index 7ebf952de87bb..26808dae97bac 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap @@ -18,7 +18,7 @@ sets Pod annotations when specified: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -91,7 +91,7 @@ sets Pod labels when specified: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -188,7 +188,7 @@ sets StatefulSet labels when specified: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -293,7 +293,7 @@ should add insecureSkipProxyTLSVerify to args when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -366,7 +366,7 @@ should add volumeClaimTemplate for data volume when using StatefulSet and action value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -459,7 +459,7 @@ should add volumeClaimTemplate for data volume when using StatefulSet and is Fre value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -542,7 +542,7 @@ should add volumeMount for data volume when using StatefulSet: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -615,7 +615,7 @@ should expose diag port: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -688,7 +688,7 @@ should generate Statefulset when storage is disabled and mode is a Upgrade: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -775,7 +775,7 @@ should have multiple replicas when replicaCount is set (using .replicaCount, dep value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -860,7 +860,7 @@ should have multiple replicas when replicaCount is set (using highAvailability.r value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -933,7 +933,7 @@ should have one replica when replicaCount is not set: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1006,7 +1006,7 @@ should install Statefulset when storage is disabled and mode is a Fresh Install: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1081,7 +1081,7 @@ should mount extraVolumes and extraVolumeMounts: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1159,7 +1159,7 @@ should mount jamfCredentialsSecret if it already exists and when role is jamf: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1240,7 +1240,7 @@ should mount jamfCredentialsSecret.name when role is jamf: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1323,7 +1323,7 @@ should mount tls.existingCASecretName and set environment when set in values: value: cluster.local - name: SSL_CERT_FILE value: /etc/teleport-tls-ca/ca.pem - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1408,7 +1408,7 @@ should mount tls.existingCASecretName and set extra environment when set in valu value: /etc/teleport-tls-ca/ca.pem - name: HTTPS_PROXY value: http://username:password@my.proxy.host:3128 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1489,7 +1489,7 @@ should not add emptyDir for data when using StatefulSet: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1562,7 +1562,7 @@ should provision initContainer correctly when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1693,7 +1693,7 @@ should set affinity when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1766,7 +1766,7 @@ should set default serviceAccountName when not set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1852,7 +1852,7 @@ should set environment when extraEnv set in values: value: cluster.local - name: HTTPS_PROXY value: http://username:password@my.proxy.host:3128 - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -1998,7 +1998,7 @@ should set imagePullPolicy when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: Always livenessProbe: failureThreshold: 6 @@ -2071,7 +2071,7 @@ should set nodeSelector if set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2158,7 +2158,7 @@ should set preferred affinity when more than one replica is used: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2231,7 +2231,7 @@ should set probeTimeoutSeconds when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2314,7 +2314,7 @@ should set required affinity when highAvailability.requireAntiAffinity is set: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2387,7 +2387,7 @@ should set resources when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2467,7 +2467,7 @@ should set serviceAccountName when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2540,7 +2540,7 @@ should set storage.requests when set in values and action is an Upgrade: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2613,7 +2613,7 @@ should set storage.storageClassName when set in values and action is an Upgrade: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -2686,7 +2686,7 @@ should set tolerations when set in values: value: RELEASE-NAME - name: TELEPORT_KUBE_CLUSTER_DOMAIN value: cluster.local - image: public.ecr.aws/gravitational/teleport-distroless:17.1.1 + image: public.ecr.aws/gravitational/teleport-distroless:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap index ecf0d0aa8c317..c196e173b9ec5 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/updater_deployment_test.yaml.snap @@ -27,7 +27,7 @@ sets the affinity: - --base-image=public.ecr.aws/gravitational/teleport-distroless - --version-server=https://my-custom-version-server/v1 - --version-channel=custom/preview - image: public.ecr.aws/gravitational/teleport-kube-agent-updater:17.1.1 + image: public.ecr.aws/gravitational/teleport-kube-agent-updater:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 @@ -73,7 +73,7 @@ sets the tolerations: - --base-image=public.ecr.aws/gravitational/teleport-distroless - --version-server=https://my-custom-version-server/v1 - --version-channel=custom/preview - image: public.ecr.aws/gravitational/teleport-kube-agent-updater:17.1.1 + image: public.ecr.aws/gravitational/teleport-kube-agent-updater:17.1.2 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 6 diff --git a/integrations/access/slack/bot.go b/integrations/access/slack/bot.go index dff6c17bbad27..9c58093cb9897 100644 --- a/integrations/access/slack/bot.go +++ b/integrations/access/slack/bot.go @@ -291,6 +291,7 @@ func (b Bot) slackAccessListReminderMsgSection(accessList *accesslist.AccessList if b.webProxyURL != nil { reqURL := *b.webProxyURL reqURL.Path = lib.BuildURLPath("web", "accesslists", accessList.Metadata.Name) + reqURL.Fragment = "review" link = fmt.Sprintf("*Link*: %s", reqURL.String()) } diff --git a/integrations/terraform/Makefile b/integrations/terraform/Makefile index f14246132fd55..68ec5fe4150d5 100644 --- a/integrations/terraform/Makefile +++ b/integrations/terraform/Makefile @@ -115,10 +115,18 @@ endif --terraform_out=config=protoc-gen-terraform-statichostuser.yaml:./tfschema \ teleport/userprovisioning/v2/statichostuser.proto + @protoc \ + -I=../../api/proto \ + -I=$(PROTOBUF_MOD_PATH) \ + --plugin=$(PROTOC_GEN_TERRAFORM) \ + --terraform_out=config=protoc-gen-terraform-workloadidentity.yaml:./tfschema \ + teleport/workloadidentity/v1/resource.proto + mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1/loginrule_terraform.go ./tfschema/loginrule/v1/ mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1/accesslist_terraform.go ./tfschema/accesslist/v1/ mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_terraform.go ./tfschema/accessmonitoringrules/v1/ mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/userprovisioning/v2/statichostuser_terraform.go ./tfschema/userprovisioning/v2/ + mv ./tfschema/github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1/resource_terraform.go ./tfschema/workloadidentity/v1/ mv ./tfschema/github.com/gravitational/teleport/api/types/device_terraform.go ./tfschema/devicetrust/v1/ rm -r ./tfschema/github.com/ @go run ./gen/main.go diff --git a/integrations/terraform/examples/resources/teleport_workload_identity/resource.tf b/integrations/terraform/examples/resources/teleport_workload_identity/resource.tf new file mode 100644 index 0000000000000..e48ab1e5d0dd2 --- /dev/null +++ b/integrations/terraform/examples/resources/teleport_workload_identity/resource.tf @@ -0,0 +1,22 @@ +resource "teleport_workload_identity" "example" { + version = "v1" + metadata = { + name = "example" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "noah" + }] + } + ] + } + spiffe = { + id = "/my/spiffe/id/path" + hint = "my-hint" + } + } +} \ No newline at end of file diff --git a/integrations/terraform/gen/main.go b/integrations/terraform/gen/main.go index 3053e7b56098a..ca639ac758ac2 100644 --- a/integrations/terraform/gen/main.go +++ b/integrations/terraform/gen/main.go @@ -519,6 +519,31 @@ var ( ExtraImports: []string{"apitypes \"github.com/gravitational/teleport/api/types\""}, ForceSetKind: "apitypes.KindStaticHostUser", } + + workloadIdentity = payload{ + Name: "WorkloadIdentity", + TypeName: "WorkloadIdentity", + VarName: "workloadIdentity", + GetMethod: "GetWorkloadIdentity", + CreateMethod: "CreateWorkloadIdentity", + UpsertMethodArity: 2, + UpdateMethod: "UpsertWorkloadIdentity", + DeleteMethod: "DeleteWorkloadIdentity", + ID: "workloadIdentity.Metadata.Name", + Kind: "workload_identity", + HasStaticID: false, + ProtoPackage: "workloadidentityv1", + ProtoPackagePath: "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1", + SchemaPackage: "schemav1", + SchemaPackagePath: "github.com/gravitational/teleport/integrations/terraform/tfschema/workloadidentity/v1", + TerraformResourceType: "teleport_workload_identity", + // Since [RFD 153](https://github.com/gravitational/teleport/blob/master/rfd/0153-resource-guidelines.md) + // resources are plain structs + IsPlainStruct: true, + // As 153-style resources don't have CheckAndSetDefaults, we must set the Kind manually. + // We import the package containing kinds, then use ForceSetKind. + ForceSetKind: `"workload_identity"`, + } ) func main() { @@ -570,6 +595,8 @@ func genTFSchema() { generateDataSource(accessMonitoringRule, pluralDataSource) generateResource(staticHostUser, pluralResource) generateDataSource(staticHostUser, pluralDataSource) + generateResource(workloadIdentity, pluralResource) + generateDataSource(workloadIdentity, pluralDataSource) } func generateResource(p payload, tpl string) { diff --git a/integrations/terraform/protoc-gen-terraform-workloadidentity.yaml b/integrations/terraform/protoc-gen-terraform-workloadidentity.yaml new file mode 100644 index 0000000000000..016f3209037ba --- /dev/null +++ b/integrations/terraform/protoc-gen-terraform-workloadidentity.yaml @@ -0,0 +1,68 @@ +--- +target_package_name: "v1" +default_package_name: "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" +duration_custom_type: Duration +use_state_for_unknown_by_default: true + +# Top-level type names to export +types: + - "WorkloadIdentity" + +# These import paths were not being automatically picked up by +# protoc-gen-terraform without these overrides +import_path_overrides: + "types": "github.com/gravitational/teleport/api/types" + "wrappers": "github.com/gravitational/teleport/api/types/wrappers" + "durationpb": "google.golang.org/protobuf/types/known/durationpb" + "timestamppb": "google.golang.org/protobuf/types/known/timestamppb" + "structpb": "google.golang.org/protobuf/types/known/structpb" + "v1": "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + "v11": "github.com/gravitational/teleport/api/gen/proto/go/teleport/label/v1" + "github_com_gravitational_teleport_integrations_terraform_tfschema": "github.com/gravitational/teleport/integrations/terraform/tfschema" + + +# id field is required for integration tests. It is not used by provider. +# We have to add it manually (might be removed in the future versions). +injected_fields: + WorkloadIdentity: + - name: id + type: github.com/hashicorp/terraform-plugin-framework/types.StringType + computed: true + plan_modifiers: + - "github.com/hashicorp/terraform-plugin-framework/tfsdk.UseStateForUnknown()" + +# These fields will be excluded +exclude_fields: + # Metadata (we id resources by name on our side) + - "WorkloadIdentity.metadata.id" + +# These fields will be marked as Computed: true +computed_fields: + # Metadata + - "WorkloadIdentity.metadata.namespace" + - "WorkloadIdentity.kind" + +# These fields will be marked as Required: true +required_fields: [] + + +plan_modifiers: + # Force to recreate resource if it's name changes + Metadata.name: + - "github.com/hashicorp/terraform-plugin-framework/tfsdk.RequiresReplace()" + +# This must be defined for the generator to be happy, but in reality all time +# fields are overridden (because the protobuf timestamps contain locks and the +# linter gets mad if we use raw structs instead of pointers). +time_type: + type: "PlaceholderType" +duration_type: + type: "PlaceholderType" + +validators: + # Expires must be in the future + Metadata.expires: + - github_com_gravitational_teleport_integrations_terraform_tfschema.MustTimeBeInFuture() + +custom_types: + "WorkloadIdentity.metadata.expires": Timestamp \ No newline at end of file diff --git a/integrations/terraform/provider/data_source_teleport_workload_identity.go b/integrations/terraform/provider/data_source_teleport_workload_identity.go new file mode 100755 index 0000000000000..1b1d15fb99dcd --- /dev/null +++ b/integrations/terraform/provider/data_source_teleport_workload_identity.go @@ -0,0 +1,82 @@ +// Code generated by _gen/main.go DO NOT EDIT +/* +Copyright 2015-2024 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 provider + +import ( + "context" + + + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + + schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/workloadidentity/v1" +) + +// dataSourceTeleportWorkloadIdentityType is the data source metadata type +type dataSourceTeleportWorkloadIdentityType struct{} + +// dataSourceTeleportWorkloadIdentity is the resource +type dataSourceTeleportWorkloadIdentity struct { + p Provider +} + +// GetSchema returns the data source schema +func (r dataSourceTeleportWorkloadIdentityType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return schemav1.GenSchemaWorkloadIdentity(ctx) +} + +// NewDataSource creates the empty data source +func (r dataSourceTeleportWorkloadIdentityType) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return dataSourceTeleportWorkloadIdentity{ + p: *(p.(*Provider)), + }, nil +} + +// Read reads teleport WorkloadIdentity +func (r dataSourceTeleportWorkloadIdentity) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var id types.String + diags := req.Config.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentityI, err := r.p.Client.GetWorkloadIdentity(ctx, id.Value) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + var state types.Object + workloadIdentity := workloadIdentityI + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/integrations/terraform/provider/provider.go b/integrations/terraform/provider/provider.go index ec2648df1b274..13b20d20c434f 100644 --- a/integrations/terraform/provider/provider.go +++ b/integrations/terraform/provider/provider.go @@ -504,6 +504,7 @@ func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceTyp "teleport_installer": resourceTeleportInstallerType{}, "teleport_access_monitoring_rule": resourceTeleportAccessMonitoringRuleType{}, "teleport_static_host_user": resourceTeleportStaticHostUserType{}, + "teleport_workload_identity": resourceTeleportWorkloadIdentityType{}, }, nil } @@ -531,6 +532,7 @@ func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc "teleport_installer": dataSourceTeleportInstallerType{}, "teleport_access_monitoring_rule": dataSourceTeleportAccessMonitoringRuleType{}, "teleport_static_host_user": dataSourceTeleportStaticHostUserType{}, + "teleport_workload_identity": dataSourceTeleportWorkloadIdentityType{}, }, nil } diff --git a/integrations/terraform/provider/resource_teleport_workload_identity.go b/integrations/terraform/provider/resource_teleport_workload_identity.go new file mode 100755 index 0000000000000..e5c59e0993b44 --- /dev/null +++ b/integrations/terraform/provider/resource_teleport_workload_identity.go @@ -0,0 +1,317 @@ +// Code generated by _gen/main.go DO NOT EDIT +/* +Copyright 2015-2024 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 provider + +import ( + "context" + "fmt" + + workloadidentityv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" + + "github.com/gravitational/teleport/integrations/lib/backoff" + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/jonboulle/clockwork" + + schemav1 "github.com/gravitational/teleport/integrations/terraform/tfschema/workloadidentity/v1" +) + +// resourceTeleportWorkloadIdentityType is the resource metadata type +type resourceTeleportWorkloadIdentityType struct{} + +// resourceTeleportWorkloadIdentity is the resource +type resourceTeleportWorkloadIdentity struct { + p Provider +} + +// GetSchema returns the resource schema +func (r resourceTeleportWorkloadIdentityType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { + return schemav1.GenSchemaWorkloadIdentity(ctx) +} + +// NewResource creates the empty resource +func (r resourceTeleportWorkloadIdentityType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) { + return resourceTeleportWorkloadIdentity{ + p: *(p.(*Provider)), + }, nil +} + +// Create creates the WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { + var err error + if !r.p.IsConfigured(resp.Diagnostics) { + return + } + + var plan types.Object + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentity := &workloadidentityv1.WorkloadIdentity{} + diags = schemav1.CopyWorkloadIdentityFromTerraform(ctx, plan, workloadIdentity) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + + workloadIdentityResource := workloadIdentity + + workloadIdentityResource.Kind = "workload_identity" + + id := workloadIdentityResource.Metadata.Name + + _, err = r.p.Client.GetWorkloadIdentity(ctx, id) + if !trace.IsNotFound(err) { + if err == nil { + existErr := fmt.Sprintf("WorkloadIdentity exists in Teleport. Either remove it (tctl rm workload_identity/%v)"+ + " or import it to the existing state (terraform import teleport_workload_identity.%v %v)", id, id, id) + + resp.Diagnostics.Append(diagFromErr("WorkloadIdentity exists in Teleport", trace.Errorf(existErr))) + return + } + + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + _, err = r.p.Client.CreateWorkloadIdentity(ctx, workloadIdentityResource) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error creating WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + var workloadIdentityI *workloadidentityv1.WorkloadIdentity + tries := 0 + backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) + for { + tries = tries + 1 + workloadIdentityI, err = r.p.Client.GetWorkloadIdentity(ctx, id) + if trace.IsNotFound(err) { + if bErr := backoff.Do(ctx); bErr != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(bErr), "workload_identity")) + return + } + if tries >= r.p.RetryConfig.MaxTries { + diagMessage := fmt.Sprintf("Error reading WorkloadIdentity (tried %d times) - state outdated, please import resource", tries) + resp.Diagnostics.AddError(diagMessage, "workload_identity") + } + continue + } + break + } + + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + workloadIdentityResource = workloadIdentityI + + workloadIdentity = workloadIdentityResource + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + plan.Attrs["id"] = types.String{Value: workloadIdentity.Metadata.Name} + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Read reads teleport WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) { + var state types.Object + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + var id types.String + diags = req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentityI, err := r.p.Client.GetWorkloadIdentity(ctx, id.Value) + if trace.IsNotFound(err) { + resp.State.RemoveResource(ctx) + return + } + + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + workloadIdentity := workloadIdentityI + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update updates teleport WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) { + if !r.p.IsConfigured(resp.Diagnostics) { + return + } + + var plan types.Object + diags := req.Plan.Get(ctx, &plan) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + workloadIdentity := &workloadidentityv1.WorkloadIdentity{} + diags = schemav1.CopyWorkloadIdentityFromTerraform(ctx, plan, workloadIdentity) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + workloadIdentityResource := workloadIdentity + + + + name := workloadIdentityResource.Metadata.Name + + workloadIdentityBefore, err := r.p.Client.GetWorkloadIdentity(ctx, name) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", err, "workload_identity")) + return + } + + _, err = r.p.Client.UpsertWorkloadIdentity(ctx, workloadIdentityResource) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error updating WorkloadIdentity", err, "workload_identity")) + return + } + var workloadIdentityI *workloadidentityv1.WorkloadIdentity + + tries := 0 + backoff := backoff.NewDecorr(r.p.RetryConfig.Base, r.p.RetryConfig.Cap, clockwork.NewRealClock()) + for { + tries = tries + 1 + workloadIdentityI, err = r.p.Client.GetWorkloadIdentity(ctx, name) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", err, "workload_identity")) + return + } + if workloadIdentityBefore.GetMetadata().Revision != workloadIdentityI.GetMetadata().Revision || false { + break + } + + if err := backoff.Do(ctx); err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + if tries >= r.p.RetryConfig.MaxTries { + diagMessage := fmt.Sprintf("Error reading WorkloadIdentity (tried %d times) - state outdated, please import resource", tries) + resp.Diagnostics.AddError(diagMessage, "workload_identity") + return + } + } + + workloadIdentityResource = workloadIdentityI + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentity, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete deletes Teleport WorkloadIdentity +func (r resourceTeleportWorkloadIdentity) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) { + var id types.String + diags := req.State.GetAttribute(ctx, path.Root("metadata").AtName("name"), &id) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + err := r.p.Client.DeleteWorkloadIdentity(ctx, id.Value) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error deleting WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + resp.State.RemoveResource(ctx) +} + +// ImportState imports WorkloadIdentity state +func (r resourceTeleportWorkloadIdentity) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { + workloadIdentity, err := r.p.Client.GetWorkloadIdentity(ctx, req.ID) + if err != nil { + resp.Diagnostics.Append(diagFromWrappedErr("Error reading WorkloadIdentity", trace.Wrap(err), "workload_identity")) + return + } + + workloadIdentityResource := workloadIdentity + + + var state types.Object + + diags := resp.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = schemav1.CopyWorkloadIdentityToTerraform(ctx, workloadIdentityResource, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + id := workloadIdentity.Metadata.Name + + state.Attrs["id"] = types.String{Value: id} + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/integrations/terraform/testlib/fixtures/workload_identity_0_create.tf b/integrations/terraform/testlib/fixtures/workload_identity_0_create.tf new file mode 100644 index 0000000000000..b5d0ebe8aae08 --- /dev/null +++ b/integrations/terraform/testlib/fixtures/workload_identity_0_create.tf @@ -0,0 +1,21 @@ +resource "teleport_workload_identity" "test" { + version = "v1" + metadata = { + name = "test" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "foo" + }] + } + ] + } + spiffe = { + id = "/test" + } + } +} \ No newline at end of file diff --git a/integrations/terraform/testlib/fixtures/workload_identity_1_update.tf b/integrations/terraform/testlib/fixtures/workload_identity_1_update.tf new file mode 100644 index 0000000000000..cced0a4f8ecdd --- /dev/null +++ b/integrations/terraform/testlib/fixtures/workload_identity_1_update.tf @@ -0,0 +1,21 @@ +resource "teleport_workload_identity" "test" { + version = "v1" + metadata = { + name = "test" + } + spec = { + rules = { + allow = [ + { + conditions = [{ + attribute = "user.name" + equals = "foo" + }] + } + ] + } + spiffe = { + id = "/test/updated" + } + } +} \ No newline at end of file diff --git a/integrations/terraform/testlib/workload_identity_test.go b/integrations/terraform/testlib/workload_identity_test.go new file mode 100644 index 0000000000000..1e6d84cf6feb9 --- /dev/null +++ b/integrations/terraform/testlib/workload_identity_test.go @@ -0,0 +1,143 @@ +// Copyright 2024 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 testlib + +import ( + "context" + "fmt" + "time" + + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/stretchr/testify/require" + + v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" + "github.com/gravitational/teleport/api/types" +) + +func (s *TerraformSuiteOSS) TestWorkloadIdentity() { + t := s.T() + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + checkDestroyed := func(state *terraform.State) error { + _, err := s.client.GetWorkloadIdentity(ctx, "test") + if trace.IsNotFound(err) { + return nil + } + return trace.Errorf("expected not found, actual: %v", err) + } + + name := "teleport_workload_identity.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: s.terraformProviders, + CheckDestroy: checkDestroyed, + IsUnitTest: true, + Steps: []resource.TestStep{ + { + Config: s.getFixture("workload_identity_0_create.tf"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "kind", "workload_identity"), + resource.TestCheckResourceAttr(name, "spec.spiffe.id", "/test"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.attribute", "user.name"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.equals", "foo"), + ), + }, + { + Config: s.getFixture("workload_identity_0_create.tf"), + PlanOnly: true, + }, + { + Config: s.getFixture("workload_identity_1_update.tf"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "kind", "workload_identity"), + resource.TestCheckResourceAttr(name, "spec.spiffe.id", "/test/updated"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.attribute", "user.name"), + resource.TestCheckResourceAttr(name, "spec.rules.allow.0.conditions.0.equals", "foo"), + ), + }, + { + Config: s.getFixture("workload_identity_1_update.tf"), + PlanOnly: true, + }, + }, + }) +} + +func (s *TerraformSuiteOSS) TestImportWorkloadIdentity() { + t := s.T() + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + r := "teleport_workload_identity" + id := "test_import" + name := r + "." + id + + shu := &workloadidentityv1pb.WorkloadIdentity{ + Metadata: &v1.Metadata{ + Name: id, + }, + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{ + Allow: []*workloadidentityv1pb.WorkloadIdentityRule{ + { + Conditions: []*workloadidentityv1pb.WorkloadIdentityCondition{ + { + Attribute: "user.name", + Equals: "foo", + }, + }, + }, + }, + }, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/test", + }, + }, + } + shu, err := s.client.CreateWorkloadIdentity(ctx, shu) + require.NoError(t, err) + + require.Eventually(t, func() bool { + _, err := s.client.GetWorkloadIdentity(ctx, shu.GetMetadata().Name) + return err == nil + }, 5*time.Second, time.Second) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: s.terraformProviders, + IsUnitTest: true, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf("%s\nresource %q %q { }", s.terraformConfig, r, id), + ResourceName: name, + ImportState: true, + ImportStateId: id, + ImportStateCheck: func(state []*terraform.InstanceState) error { + require.Equal(t, types.KindWorkloadIdentity, state[0].Attributes["kind"]) + require.Equal(t, "/test", state[0].Attributes["spec.spiffe.id"]) + require.Equal(t, "user.name", state[0].Attributes["spec.rules.allow.0.conditions.0.attribute"]) + require.Equal(t, "foo", state[0].Attributes["spec.rules.allow.0.conditions.0.equals"]) + + return nil + }, + }, + }, + }) +} diff --git a/integrations/terraform/tfschema/workloadidentity/v1/custom_types.go b/integrations/terraform/tfschema/workloadidentity/v1/custom_types.go new file mode 100644 index 0000000000000..eb615d5b1888b --- /dev/null +++ b/integrations/terraform/tfschema/workloadidentity/v1/custom_types.go @@ -0,0 +1,25 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package v1 + +import "github.com/gravitational/teleport/integrations/terraform/tfschema/resource153" + +var ( + GenSchemaTimestamp = resource153.GenSchemaTimestamp + CopyToTimestamp = resource153.CopyToTimestamp + CopyFromTimestamp = resource153.CopyFromTimestamp +) diff --git a/integrations/terraform/tfschema/workloadidentity/v1/resource_terraform.go b/integrations/terraform/tfschema/workloadidentity/v1/resource_terraform.go new file mode 100644 index 0000000000000..f6510d28f7294 --- /dev/null +++ b/integrations/terraform/tfschema/workloadidentity/v1/resource_terraform.go @@ -0,0 +1,1177 @@ +/* +Copyright 2015-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. +*/ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: teleport/workloadidentity/v1/resource.proto + +package v1 + +import ( + context "context" + fmt "fmt" + math "math" + + proto "github.com/gogo/protobuf/proto" + _ "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" + github_com_gravitational_teleport_integrations_terraform_tfschema "github.com/gravitational/teleport/integrations/terraform/tfschema" + github_com_hashicorp_terraform_plugin_framework_attr "github.com/hashicorp/terraform-plugin-framework/attr" + github_com_hashicorp_terraform_plugin_framework_diag "github.com/hashicorp/terraform-plugin-framework/diag" + github_com_hashicorp_terraform_plugin_framework_tfsdk "github.com/hashicorp/terraform-plugin-framework/tfsdk" + github_com_hashicorp_terraform_plugin_framework_types "github.com/hashicorp/terraform-plugin-framework/types" + github_com_hashicorp_terraform_plugin_go_tftypes "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// GenSchemaWorkloadIdentity returns tfsdk.Schema definition for WorkloadIdentity +func GenSchemaWorkloadIdentity(ctx context.Context) (github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema, github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics) { + return github_com_hashicorp_terraform_plugin_framework_tfsdk.Schema{Attributes: map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "id": { + Computed: true, + Optional: false, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Required: false, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "kind": { + Computed: true, + Description: "The kind of resource represented.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "metadata": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "description": { + Description: "description is object description.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "expires": GenSchemaTimestamp(ctx, github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + Description: "expires is a global expiry time header can be set on any resource in the system.", + Optional: true, + Validators: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributeValidator{github_com_gravitational_teleport_integrations_terraform_tfschema.MustTimeBeInFuture()}, + }), + "labels": { + Description: "labels is a set of labels.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.MapType{ElemType: github_com_hashicorp_terraform_plugin_framework_types.StringType}, + }, + "name": { + Description: "name is an object name.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.RequiresReplace()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "namespace": { + Computed: true, + Description: "namespace is object namespace. The field should be called \"namespace\" when it returns in Teleport 2.4.", + Optional: true, + PlanModifiers: []github_com_hashicorp_terraform_plugin_framework_tfsdk.AttributePlanModifier{github_com_hashicorp_terraform_plugin_framework_tfsdk.UseStateForUnknown()}, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "revision": { + Description: "revision is an opaque identifier which tracks the versions of a resource over time. Clients should ignore and not alter its value but must return the revision in any updates of a resource.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "Common metadata that all resources share.", + Optional: true, + }, + "spec": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "rules": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{"allow": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{"conditions": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.ListNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "attribute": { + Description: "The name of the attribute to evaluate the condition against.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "equals": { + Description: "An exact string that the attribute must match.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "The conditions that must be met for this rule to be considered passed.", + Optional: true, + }}), + Description: "A list of rules used to determine if a WorkloadIdentity can be issued. If none are provided, it will be considered a pass. If any are provided, then at least one must pass for the rules to be considered passed.", + Optional: true, + }}), + Description: "The rules which are evaluated before the WorkloadIdentity can be issued.", + Optional: true, + }, + "spiffe": { + Attributes: github_com_hashicorp_terraform_plugin_framework_tfsdk.SingleNestedAttributes(map[string]github_com_hashicorp_terraform_plugin_framework_tfsdk.Attribute{ + "hint": { + Description: "A freeform text field which is provided to workloads along with a credential produced by this WorkloadIdentity. This can be used to provide additional context that can be used to select between multiple credentials.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "id": { + Description: "The path of the SPIFFE ID that will be issued to the workload. This should be prefixed with a forward-slash (\"/\"). This field supports templating using attributes.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }), + Description: "Configuration pertaining to the issuance of SPIFFE-compatible workload identity credentials.", + Optional: true, + }, + }), + Description: "The configured properties of the WorkloadIdentity", + Optional: true, + }, + "sub_kind": { + Description: "Differentiates variations of the same kind. All resources should contain one, even if it is never populated.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + "version": { + Description: "The version of the resource being represented.", + Optional: true, + Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, + }, + }}, nil +} + +// CopyWorkloadIdentityFromTerraform copies contents of the source Terraform object into a target struct +func CopyWorkloadIdentityFromTerraform(_ context.Context, tf github_com_hashicorp_terraform_plugin_framework_types.Object, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentity) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + { + a, ok := tf.Attrs["kind"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Kind = t + } + } + } + { + a, ok := tf.Attrs["sub_kind"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.sub_kind"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.SubKind = t + } + } + } + { + a, ok := tf.Attrs["version"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.version"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Version = t + } + } + } + { + a, ok := tf.Attrs["metadata"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Metadata = nil + if !v.Null && !v.Unknown { + tf := v + obj.Metadata = &github_com_gravitational_teleport_api_gen_proto_go_teleport_header_v1.Metadata{} + obj := obj.Metadata + { + a, ok := tf.Attrs["name"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.name"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Name = t + } + } + } + { + a, ok := tf.Attrs["namespace"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.namespace"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Namespace = t + } + } + } + { + a, ok := tf.Attrs["description"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.description"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Description = t + } + } + } + { + a, ok := tf.Attrs["labels"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.labels"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.Map"}) + } else { + obj.Labels = make(map[string]string, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github_com_hashicorp_terraform_plugin_framework_types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Labels[k] = t + } + } + } + } + } + } + { + a, ok := tf.Attrs["expires"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.expires"}) + } + CopyFromTimestamp(diags, a, &obj.Expires) + } + { + a, ok := tf.Attrs["revision"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.metadata.revision"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Revision = t + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["spec"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Spec = nil + if !v.Null && !v.Unknown { + tf := v + obj.Spec = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentitySpec{} + obj := obj.Spec + { + a, ok := tf.Attrs["rules"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Rules = nil + if !v.Null && !v.Unknown { + tf := v + obj.Rules = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRules{} + obj := obj.Rules + { + a, ok := tf.Attrs["allow"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Allow = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRule, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRule + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityRule{} + obj := t + { + a, ok := tf.Attrs["conditions"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions", "github.com/hashicorp/terraform-plugin-framework/types.List"}) + } else { + obj.Conditions = make([]*github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityCondition, len(v.Elems)) + if !v.Null && !v.Unknown { + for k, a := range v.Elems { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions", "github_com_hashicorp_terraform_plugin_framework_types.Object"}) + } else { + var t *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityCondition + if !v.Null && !v.Unknown { + tf := v + t = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentityCondition{} + obj := t + { + a, ok := tf.Attrs["attribute"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Attribute = t + } + } + } + { + a, ok := tf.Attrs["equals"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Equals = t + } + } + } + } + obj.Conditions[k] = t + } + } + } + } + } + } + } + obj.Allow[k] = t + } + } + } + } + } + } + } + } + } + } + { + a, ok := tf.Attrs["spiffe"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe", "github.com/hashicorp/terraform-plugin-framework/types.Object"}) + } else { + obj.Spiffe = nil + if !v.Null && !v.Unknown { + tf := v + obj.Spiffe = &github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentitySPIFFE{} + obj := obj.Spiffe + { + a, ok := tf.Attrs["id"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe.id"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe.id", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Id = t + } + } + } + { + a, ok := tf.Attrs["hint"] + if !ok { + diags.Append(attrReadMissingDiag{"WorkloadIdentity.spec.spiffe.hint"}) + } else { + v, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrReadConversionFailureDiag{"WorkloadIdentity.spec.spiffe.hint", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } else { + var t string + if !v.Null && !v.Unknown { + t = string(v.Value) + } + obj.Hint = t + } + } + } + } + } + } + } + } + } + } + } + return diags +} + +// CopyWorkloadIdentityToTerraform copies contents of the source Terraform object into a target struct +func CopyWorkloadIdentityToTerraform(ctx context.Context, obj *github_com_gravitational_teleport_api_gen_proto_go_teleport_workloadidentity_v1.WorkloadIdentity, tf *github_com_hashicorp_terraform_plugin_framework_types.Object) github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics { + var diags github_com_hashicorp_terraform_plugin_framework_diag.Diagnostics + tf.Null = false + tf.Unknown = false + if tf.Attrs == nil { + tf.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value) + } + { + t, ok := tf.AttrTypes["kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.kind"}) + } else { + v, ok := tf.Attrs["kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Kind) == "" + } + v.Value = string(obj.Kind) + v.Unknown = false + tf.Attrs["kind"] = v + } + } + { + t, ok := tf.AttrTypes["sub_kind"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.sub_kind"}) + } else { + v, ok := tf.Attrs["sub_kind"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.sub_kind", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.sub_kind", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.SubKind) == "" + } + v.Value = string(obj.SubKind) + v.Unknown = false + tf.Attrs["sub_kind"] = v + } + } + { + t, ok := tf.AttrTypes["version"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.version"}) + } else { + v, ok := tf.Attrs["version"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.version", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.version", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Version) == "" + } + v.Value = string(obj.Version) + v.Unknown = false + tf.Attrs["version"] = v + } + } + { + a, ok := tf.AttrTypes["metadata"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["metadata"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Metadata == nil { + v.Null = true + } else { + obj := obj.Metadata + tf := &v + { + t, ok := tf.AttrTypes["name"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.name"}) + } else { + v, ok := tf.Attrs["name"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.name", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.name", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Name) == "" + } + v.Value = string(obj.Name) + v.Unknown = false + tf.Attrs["name"] = v + } + } + { + t, ok := tf.AttrTypes["namespace"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.namespace"}) + } else { + v, ok := tf.Attrs["namespace"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.namespace", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.namespace", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Namespace) == "" + } + v.Value = string(obj.Namespace) + v.Unknown = false + tf.Attrs["namespace"] = v + } + } + { + t, ok := tf.AttrTypes["description"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.description"}) + } else { + v, ok := tf.Attrs["description"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.description", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.description", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Description) == "" + } + v.Value = string(obj.Description) + v.Unknown = false + tf.Attrs["description"] = v + } + } + { + a, ok := tf.AttrTypes["labels"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.labels"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.MapType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.MapType"}) + } else { + c, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.Map) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.Map{ + + ElemType: o.ElemType, + Elems: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Labels)) + } + } + if obj.Labels != nil { + t := o.ElemType + for k, a := range obj.Labels { + v, ok := tf.Attrs["labels"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.labels", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.labels", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = false + } + v.Value = string(a) + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Labels) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["labels"] = c + } + } + } + { + t, ok := tf.AttrTypes["expires"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.expires"}) + } else { + v := CopyToTimestamp(diags, obj.Expires, t, tf.Attrs["expires"]) + tf.Attrs["expires"] = v + } + } + { + t, ok := tf.AttrTypes["revision"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.metadata.revision"}) + } else { + v, ok := tf.Attrs["revision"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.metadata.revision", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.metadata.revision", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Revision) == "" + } + v.Value = string(obj.Revision) + v.Unknown = false + tf.Attrs["revision"] = v + } + } + } + v.Unknown = false + tf.Attrs["metadata"] = v + } + } + } + { + a, ok := tf.AttrTypes["spec"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["spec"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Spec == nil { + v.Null = true + } else { + obj := obj.Spec + tf := &v + { + a, ok := tf.AttrTypes["rules"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["rules"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Rules == nil { + v.Null = true + } else { + obj := obj.Rules + tf := &v + { + a, ok := tf.AttrTypes["allow"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["allow"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Allow)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Allow)) + } + } + if obj.Allow != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Allow) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Allow)) + } + for k, a := range obj.Allow { + v, ok := tf.Attrs["allow"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + a, ok := tf.AttrTypes["conditions"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ListType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions", "github.com/hashicorp/terraform-plugin-framework/types.ListType"}) + } else { + c, ok := tf.Attrs["conditions"].(github_com_hashicorp_terraform_plugin_framework_types.List) + if !ok { + c = github_com_hashicorp_terraform_plugin_framework_types.List{ + + ElemType: o.ElemType, + Elems: make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Conditions)), + Null: true, + } + } else { + if c.Elems == nil { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Conditions)) + } + } + if obj.Conditions != nil { + o := o.ElemType.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if len(obj.Conditions) != len(c.Elems) { + c.Elems = make([]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(obj.Conditions)) + } + for k, a := range obj.Conditions { + v, ok := tf.Attrs["conditions"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if a == nil { + v.Null = true + } else { + obj := a + tf := &v + { + t, ok := tf.AttrTypes["attribute"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute"}) + } else { + v, ok := tf.Attrs["attribute"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.rules.allow.conditions.attribute", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.attribute", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Attribute) == "" + } + v.Value = string(obj.Attribute) + v.Unknown = false + tf.Attrs["attribute"] = v + } + } + { + t, ok := tf.AttrTypes["equals"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals"}) + } else { + v, ok := tf.Attrs["equals"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.rules.allow.conditions.equals", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.rules.allow.conditions.equals", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Equals) == "" + } + v.Value = string(obj.Equals) + v.Unknown = false + tf.Attrs["equals"] = v + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Conditions) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["conditions"] = c + } + } + } + } + v.Unknown = false + c.Elems[k] = v + } + if len(obj.Allow) > 0 { + c.Null = false + } + } + c.Unknown = false + tf.Attrs["allow"] = c + } + } + } + } + v.Unknown = false + tf.Attrs["rules"] = v + } + } + } + { + a, ok := tf.AttrTypes["spiffe"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe"}) + } else { + o, ok := a.(github_com_hashicorp_terraform_plugin_framework_types.ObjectType) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe", "github.com/hashicorp/terraform-plugin-framework/types.ObjectType"}) + } else { + v, ok := tf.Attrs["spiffe"].(github_com_hashicorp_terraform_plugin_framework_types.Object) + if !ok { + v = github_com_hashicorp_terraform_plugin_framework_types.Object{ + + AttrTypes: o.AttrTypes, + Attrs: make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(o.AttrTypes)), + } + } else { + if v.Attrs == nil { + v.Attrs = make(map[string]github_com_hashicorp_terraform_plugin_framework_attr.Value, len(tf.AttrTypes)) + } + } + if obj.Spiffe == nil { + v.Null = true + } else { + obj := obj.Spiffe + tf := &v + { + t, ok := tf.AttrTypes["id"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe.id"}) + } else { + v, ok := tf.Attrs["id"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.spiffe.id", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe.id", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Id) == "" + } + v.Value = string(obj.Id) + v.Unknown = false + tf.Attrs["id"] = v + } + } + { + t, ok := tf.AttrTypes["hint"] + if !ok { + diags.Append(attrWriteMissingDiag{"WorkloadIdentity.spec.spiffe.hint"}) + } else { + v, ok := tf.Attrs["hint"].(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + i, err := t.ValueFromTerraform(ctx, github_com_hashicorp_terraform_plugin_go_tftypes.NewValue(t.TerraformType(ctx), nil)) + if err != nil { + diags.Append(attrWriteGeneralError{"WorkloadIdentity.spec.spiffe.hint", err}) + } + v, ok = i.(github_com_hashicorp_terraform_plugin_framework_types.String) + if !ok { + diags.Append(attrWriteConversionFailureDiag{"WorkloadIdentity.spec.spiffe.hint", "github.com/hashicorp/terraform-plugin-framework/types.String"}) + } + v.Null = string(obj.Hint) == "" + } + v.Value = string(obj.Hint) + v.Unknown = false + tf.Attrs["hint"] = v + } + } + } + v.Unknown = false + tf.Attrs["spiffe"] = v + } + } + } + } + v.Unknown = false + tf.Attrs["spec"] = v + } + } + } + return diags +} + +// attrReadMissingDiag represents diagnostic message on an attribute missing in the source object +type attrReadMissingDiag struct { + Path string +} + +func (d attrReadMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadMissingDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object Attrs", d.Path) +} + +func (d attrReadMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrReadConversionFailureDiag represents diagnostic message on a failed type conversion on read +type attrReadConversionFailureDiag struct { + Path string + Type string +} + +func (d attrReadConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrReadConversionFailureDiag) Summary() string { + return "Error reading from Terraform object" +} + +func (d attrReadConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrReadConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteMissingDiag represents diagnostic message on an attribute missing in the target object +type attrWriteMissingDiag struct { + Path string +} + +func (d attrWriteMissingDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteMissingDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteMissingDiag) Detail() string { + return fmt.Sprintf("A value for %v is missing in the source Terraform object AttrTypes", d.Path) +} + +func (d attrWriteMissingDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteConversionFailureDiag represents diagnostic message on a failed type conversion on write +type attrWriteConversionFailureDiag struct { + Path string + Type string +} + +func (d attrWriteConversionFailureDiag) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteConversionFailureDiag) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteConversionFailureDiag) Detail() string { + return fmt.Sprintf("A value for %v can not be converted to %v", d.Path, d.Type) +} + +func (d attrWriteConversionFailureDiag) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} + +// attrWriteGeneralError represents diagnostic message on a generic error on write +type attrWriteGeneralError struct { + Path string + Err error +} + +func (d attrWriteGeneralError) Severity() github_com_hashicorp_terraform_plugin_framework_diag.Severity { + return github_com_hashicorp_terraform_plugin_framework_diag.SeverityError +} + +func (d attrWriteGeneralError) Summary() string { + return "Error writing to Terraform object" +} + +func (d attrWriteGeneralError) Detail() string { + return fmt.Sprintf("%s: %s", d.Path, d.Err.Error()) +} + +func (d attrWriteGeneralError) Equal(o github_com_hashicorp_terraform_plugin_framework_diag.Diagnostic) bool { + return (d.Severity() == o.Severity()) && (d.Summary() == o.Summary()) && (d.Detail() == o.Detail()) +} diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 70cce69fe8260..58c6c89e88fda 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -211,30 +211,14 @@ func (a *ServerWithRoles) actionWithExtendedContext(namespace, kind, verb string // actionForKindSession is a special checker that grants access to session // recordings. It can allow access to a specific recording based on the // `where` section of the user's access rule for kind `session`. -func (a *ServerWithRoles) actionForKindSession(ctx context.Context, namespace string, sid session.ID) (types.SessionKind, error) { - sessionEnd, err := a.findSessionEndEvent(ctx, sid) - - extendContext := func(ctx *services.Context) error { - ctx.Session = sessionEnd +func (a *ServerWithRoles) actionForKindSession(ctx context.Context, namespace string, sid session.ID) error { + extendContext := func(servicesCtx *services.Context) error { + sessionEnd, err := a.findSessionEndEvent(ctx, sid) + servicesCtx.Session = sessionEnd return trace.Wrap(err) } - var sessionKind types.SessionKind - switch e := sessionEnd.(type) { - case *apievents.SessionEnd: - sessionKind = types.SSHSessionKind - if e.KubernetesCluster != "" { - sessionKind = types.KubernetesSessionKind - } - case *apievents.DatabaseSessionEnd: - sessionKind = types.DatabaseSessionKind - case *apievents.AppSessionEnd: - sessionKind = types.AppSessionKind - case *apievents.WindowsDesktopSessionEnd: - sessionKind = types.WindowsDesktopSessionKind - } - - return sessionKind, trace.Wrap(a.actionWithExtendedContext(namespace, types.KindSession, types.VerbRead, extendContext)) + return trace.Wrap(a.actionWithExtendedContext(namespace, types.KindSession, types.VerbRead, extendContext)) } // localServerAction returns an access denied error if the role is not one of the builtin server roles. @@ -6063,29 +6047,25 @@ func (a *ServerWithRoles) ReplaceRemoteLocks(ctx context.Context, clusterName st // channel if one is encountered. Otherwise the event channel is closed when the stream ends. // The event channel is not closed on error to prevent race conditions in downstream select statements. func (a *ServerWithRoles) StreamSessionEvents(ctx context.Context, sessionID session.ID, startIndex int64) (chan apievents.AuditEvent, chan error) { - createErrorChannel := func(err error) (chan apievents.AuditEvent, chan error) { - e := make(chan error, 1) - e <- trace.Wrap(err) - return nil, e - } - err := a.localServerAction() isTeleportServer := err == nil - var sessionType types.SessionKind - if !isTeleportServer { - var err error - sessionType, err = a.actionForKindSession(ctx, apidefaults.Namespace, sessionID) - if err != nil { - c, e := make(chan apievents.AuditEvent), make(chan error, 1) - e <- trace.Wrap(err) - return c, e - } + // StreamSessionEvents can be called internally, and when that + // happens we don't want to emit an event or check for permissions. + if isTeleportServer { + return a.alog.StreamSessionEvents(ctx, sessionID, startIndex) } - // StreamSessionEvents can be called internally, and when that happens we don't want to emit an event. - shouldEmitAuditEvent := !isTeleportServer - if shouldEmitAuditEvent { + if err := a.actionForKindSession(ctx, apidefaults.Namespace, sessionID); err != nil { + c, e := make(chan apievents.AuditEvent), make(chan error, 1) + e <- trace.Wrap(err) + return c, e + } + + // We can only determine the session type after the streaming started. For + // this reason, we delay the emit audit event until the first event or if + // the streaming returns an error. + cb := func(evt apievents.AuditEvent, _ error) { if err := a.authServer.emitter.EmitAuditEvent(a.authServer.closeCtx, &apievents.SessionRecordingAccess{ Metadata: apievents.Metadata{ Type: events.SessionRecordingAccessEvent, @@ -6093,14 +6073,34 @@ func (a *ServerWithRoles) StreamSessionEvents(ctx context.Context, sessionID ses }, SessionID: sessionID.String(), UserMetadata: a.context.Identity.GetIdentity().GetUserMetadata(), - SessionType: string(sessionType), + SessionType: string(sessionTypeFromStartEvent(evt)), Format: metadata.SessionRecordingFormatFromContext(ctx), }); err != nil { - return createErrorChannel(err) + log.WithError(err).Errorf("Failed to emit stream session event audit event") } } - return a.alog.StreamSessionEvents(ctx, sessionID, startIndex) + return a.alog.StreamSessionEvents(events.ContextWithSessionStartCallback(ctx, cb), sessionID, startIndex) +} + +// sessionTypeFromStartEvent determines the session type given the session start +// event. +func sessionTypeFromStartEvent(sessionStart apievents.AuditEvent) types.SessionKind { + switch e := sessionStart.(type) { + case *apievents.SessionStart: + if e.KubernetesCluster != "" { + return types.KubernetesSessionKind + } + return types.SSHSessionKind + case *apievents.DatabaseSessionStart: + return types.DatabaseSessionKind + case *apievents.AppSessionStart: + return types.AppSessionKind + case *apievents.WindowsDesktopSessionStart: + return types.WindowsDesktopSessionKind + default: + return types.UnknownSessionKind + } } // CreateApp creates a new application resource. diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index fdf1b644d4de0..c38c6ae1e8ee5 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -2267,7 +2267,29 @@ func TestStreamSessionEvents(t *testing.T) { func TestStreamSessionEvents_SessionType(t *testing.T) { t.Parallel() - srv := newTestTLSServer(t) + authServerConfig := TestAuthServerConfig{ + Dir: t.TempDir(), + Clock: clockwork.NewFakeClockAt(time.Now().Round(time.Second).UTC()), + } + require.NoError(t, authServerConfig.CheckAndSetDefaults()) + + uploader := eventstest.NewMemoryUploader() + localLog, err := events.NewAuditLog(events.AuditLogConfig{ + DataDir: authServerConfig.Dir, + ServerID: authServerConfig.ClusterName, + Clock: authServerConfig.Clock, + UploadHandler: uploader, + }) + require.NoError(t, err) + authServerConfig.AuditLog = localLog + + as, err := NewTestAuthServer(authServerConfig) + require.NoError(t, err) + + srv, err := as.NewTestTLSServer() + require.NoError(t, err) + t.Cleanup(func() { srv.Close() }) + ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -2278,22 +2300,29 @@ func TestStreamSessionEvents_SessionType(t *testing.T) { identity := TestUser(user.GetName()) clt, err := srv.NewClient(identity) require.NoError(t, err) - sessionID := "44c6cea8-362f-11ea-83aa-125400432324" + sessionID := session.NewID() - // Emitting a session end event will cause the listing to correctly locate - // the recording (even if there might not be a recording file to stream). - require.NoError(t, srv.Auth().EmitAuditEvent(ctx, &apievents.DatabaseSessionEnd{ + streamer, err := events.NewProtoStreamer(events.ProtoStreamerConfig{ + Uploader: uploader, + }) + require.NoError(t, err) + stream, err := streamer.CreateAuditStream(ctx, sessionID) + require.NoError(t, err) + // The event is not required to pass through the auth server, we only need + // the upload to be present. + require.NoError(t, stream.RecordEvent(ctx, eventstest.PrepareEvent(&apievents.DatabaseSessionStart{ Metadata: apievents.Metadata{ - Type: events.DatabaseSessionEndEvent, - Code: events.DatabaseSessionEndCode, + Type: events.DatabaseSessionStartEvent, + Code: events.DatabaseSessionStartCode, }, SessionMetadata: apievents.SessionMetadata{ - SessionID: sessionID, + SessionID: sessionID.String(), }, - })) + }))) + require.NoError(t, stream.Complete(ctx)) accessedFormat := teleport.PTY - clt.StreamSessionEvents(metadata.WithSessionRecordingFormatContext(ctx, accessedFormat), session.ID(sessionID), 0) + clt.StreamSessionEvents(metadata.WithSessionRecordingFormatContext(ctx, accessedFormat), sessionID, 0) // Perform the listing an eventually loop to ensure the event is emitted. var searchEvents []apievents.AuditEvent diff --git a/lib/auth/machineid/workloadidentityv1/decision.go b/lib/auth/machineid/workloadidentityv1/decision.go new file mode 100644 index 0000000000000..53a1a9c1bc5f6 --- /dev/null +++ b/lib/auth/machineid/workloadidentityv1/decision.go @@ -0,0 +1,169 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package workloadidentityv1 + +import ( + "context" + "regexp" + "slices" + "strings" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1" +) + +type decision struct { + templatedWorkloadIdentity *workloadidentityv1pb.WorkloadIdentity + shouldIssue bool + reason error +} + +func decide( + ctx context.Context, + wi *workloadidentityv1pb.WorkloadIdentity, + attrs *workloadidentityv1pb.Attrs, +) *decision { + d := &decision{ + templatedWorkloadIdentity: proto.Clone(wi).(*workloadidentityv1pb.WorkloadIdentity), + } + + // First, evaluate the rules. + if err := evaluateRules(wi, attrs); err != nil { + d.reason = trace.Wrap(err, "attributes did not pass rule evaluation") + return d + } + + // Now we can cook up some templating... + templated, err := templateString(wi.GetSpec().GetSpiffe().GetId(), attrs) + if err != nil { + d.reason = trace.Wrap(err, "templating spec.spiffe.id") + return d + } + d.templatedWorkloadIdentity.Spec.Spiffe.Id = templated + + templated, err = templateString(wi.GetSpec().GetSpiffe().GetHint(), attrs) + if err != nil { + d.reason = trace.Wrap(err, "templating spec.spiffe.hint") + return d + } + d.templatedWorkloadIdentity.Spec.Spiffe.Hint = templated + + // Yay - made it to the end! + d.shouldIssue = true + return d +} + +// getFieldStringValue returns a string value from the given attribute set. +// The attribute is specified as a dot-separated path to the field in the +// attribute set. +// +// The specified attribute must be a string field. If the attribute is not +// found, an error is returned. +// +// TODO(noah): This function will be replaced by the Teleport predicate language +// in a coming PR. +func getFieldStringValue(attrs *workloadidentityv1pb.Attrs, attr string) (string, error) { + attrParts := strings.Split(attr, ".") + message := attrs.ProtoReflect() + // TODO(noah): Improve errors by including the fully qualified attribute + // (e.g add up the parts of the attribute path processed thus far) + for i, part := range attrParts { + fieldDesc := message.Descriptor().Fields().ByTextName(part) + if fieldDesc == nil { + return "", trace.NotFound("attribute %q not found", part) + } + // We expect the final key to point to a string field - otherwise - we + // return an error. + if i == len(attrParts)-1 { + if !slices.Contains([]protoreflect.Kind{ + protoreflect.StringKind, + protoreflect.BoolKind, + protoreflect.Int32Kind, + protoreflect.Int64Kind, + protoreflect.Uint64Kind, + protoreflect.Uint32Kind, + }, fieldDesc.Kind()) { + return "", trace.BadParameter("attribute %q of type %q cannot be converted to string", part, fieldDesc.Kind()) + } + return message.Get(fieldDesc).String(), nil + } + // If we're not processing the final key part, we expect this to point + // to a message that we can further explore. + if fieldDesc.Kind() != protoreflect.MessageKind { + return "", trace.BadParameter("attribute %q is not a message", part) + } + message = message.Get(fieldDesc).Message() + } + return "", nil +} + +// templateString takes a given input string and replaces any values within +// {{ }} with values from the attribute set. +// +// If the specified value is not found in the attribute set, an error is +// returned. +// +// TODO(noah): In a coming PR, this will be replaced by evaluating the values +// within the handlebars as expressions. +func templateString(in string, attrs *workloadidentityv1pb.Attrs) (string, error) { + re := regexp.MustCompile(`\{\{([^{}]+?)\}\}`) + matches := re.FindAllStringSubmatch(in, -1) + + for _, match := range matches { + attrKey := strings.TrimSpace(match[1]) + value, err := getFieldStringValue(attrs, attrKey) + if err != nil { + return "", trace.Wrap(err, "fetching attribute value for %q", attrKey) + } + // We want to have an implicit rule here that if an attribute is + // included in the template, but is not set, we should refuse to issue + // the credential. + if value == "" { + return "", trace.NotFound("attribute %q unset", attrKey) + } + in = strings.Replace(in, match[0], value, 1) + } + + return in, nil +} + +func evaluateRules( + wi *workloadidentityv1pb.WorkloadIdentity, + attrs *workloadidentityv1pb.Attrs, +) error { + if len(wi.GetSpec().GetRules().GetAllow()) == 0 { + return nil + } +ruleLoop: + for _, rule := range wi.GetSpec().GetRules().GetAllow() { + for _, condition := range rule.GetConditions() { + val, err := getFieldStringValue(attrs, condition.Attribute) + if err != nil { + return trace.Wrap(err) + } + if val != condition.Equals { + continue ruleLoop + } + } + return nil + } + // TODO: Eventually, we'll need to work support for deny rules into here. + return trace.AccessDenied("no matching rule found") +} diff --git a/lib/auth/machineid/workloadidentityv1/issuer_service_test.go b/lib/auth/machineid/workloadidentityv1/decision_test.go similarity index 100% rename from lib/auth/machineid/workloadidentityv1/issuer_service_test.go rename to lib/auth/machineid/workloadidentityv1/decision_test.go diff --git a/lib/auth/machineid/workloadidentityv1/issuer_service.go b/lib/auth/machineid/workloadidentityv1/issuer_service.go index 70a7fa1197974..7b498f7f16d9a 100644 --- a/lib/auth/machineid/workloadidentityv1/issuer_service.go +++ b/lib/auth/machineid/workloadidentityv1/issuer_service.go @@ -24,8 +24,6 @@ import ( "log/slog" "math/big" "net/url" - "regexp" - "slices" "strings" "time" @@ -33,7 +31,6 @@ import ( "github.com/jonboulle/clockwork" "github.com/spiffe/go-spiffe/v2/spiffeid" "go.opentelemetry.io/otel" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -126,104 +123,6 @@ func NewIssuanceService(cfg *IssuanceServiceConfig) (*IssuanceService, error) { }, nil } -// getFieldStringValue returns a string value from the given attribute set. -// The attribute is specified as a dot-separated path to the field in the -// attribute set. -// -// The specified attribute must be a string field. If the attribute is not -// found, an error is returned. -// -// TODO(noah): This function will be replaced by the Teleport predicate language -// in a coming PR. -func getFieldStringValue(attrs *workloadidentityv1pb.Attrs, attr string) (string, error) { - attrParts := strings.Split(attr, ".") - message := attrs.ProtoReflect() - // TODO(noah): Improve errors by including the fully qualified attribute - // (e.g add up the parts of the attribute path processed thus far) - for i, part := range attrParts { - fieldDesc := message.Descriptor().Fields().ByTextName(part) - if fieldDesc == nil { - return "", trace.NotFound("attribute %q not found", part) - } - // We expect the final key to point to a string field - otherwise - we - // return an error. - if i == len(attrParts)-1 { - if !slices.Contains([]protoreflect.Kind{ - protoreflect.StringKind, - protoreflect.BoolKind, - protoreflect.Int32Kind, - protoreflect.Int64Kind, - protoreflect.Uint64Kind, - protoreflect.Uint32Kind, - }, fieldDesc.Kind()) { - return "", trace.BadParameter("attribute %q of type %q cannot be converted to string", part, fieldDesc.Kind()) - } - return message.Get(fieldDesc).String(), nil - } - // If we're not processing the final key part, we expect this to point - // to a message that we can further explore. - if fieldDesc.Kind() != protoreflect.MessageKind { - return "", trace.BadParameter("attribute %q is not a message", part) - } - message = message.Get(fieldDesc).Message() - } - return "", nil -} - -// templateString takes a given input string and replaces any values within -// {{ }} with values from the attribute set. -// -// If the specified value is not found in the attribute set, an error is -// returned. -// -// TODO(noah): In a coming PR, this will be replaced by evaluating the values -// within the handlebars as expressions. -func templateString(in string, attrs *workloadidentityv1pb.Attrs) (string, error) { - re := regexp.MustCompile(`\{\{([^{}]+?)\}\}`) - matches := re.FindAllStringSubmatch(in, -1) - - for _, match := range matches { - attrKey := strings.TrimSpace(match[1]) - value, err := getFieldStringValue(attrs, attrKey) - if err != nil { - return "", trace.Wrap(err, "fetching attribute value for %q", attrKey) - } - // We want to have an implicit rule here that if an attribute is - // included in the template, but is not set, we should refuse to issue - // the credential. - if value == "" { - return "", trace.NotFound("attribute %q unset", attrKey) - } - in = strings.Replace(in, match[0], value, 1) - } - - return in, nil -} - -func evaluateRules( - wi *workloadidentityv1pb.WorkloadIdentity, - attrs *workloadidentityv1pb.Attrs, -) error { - if len(wi.GetSpec().GetRules().GetAllow()) == 0 { - return nil - } -ruleLoop: - for _, rule := range wi.GetSpec().GetRules().GetAllow() { - for _, condition := range rule.GetConditions() { - val, err := getFieldStringValue(attrs, condition.Attribute) - if err != nil { - return trace.Wrap(err) - } - if val != condition.Equals { - continue ruleLoop - } - } - return nil - } - // TODO: Eventually, we'll need to work support for deny rules into here. - return trace.AccessDenied("no matching rule found") -} - func (s *IssuanceService) deriveAttrs( authzCtx *authz.Context, workloadAttrs *workloadidentityv1pb.WorkloadAttrs, @@ -251,11 +150,6 @@ func (s *IssuanceService) IssueWorkloadIdentity( return nil, trace.AccessDenied("workload identity issuance experiment is disabled") } - authCtx, err := s.authorizer.Authorize(ctx) - if err != nil { - return nil, trace.Wrap(err) - } - switch { case req.GetName() == "": return nil, trace.BadParameter("name: is required") @@ -263,6 +157,18 @@ func (s *IssuanceService) IssueWorkloadIdentity( return nil, trace.BadParameter("at least one credential type must be requested") } + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindWorkloadIdentity, types.VerbRead); err != nil { + return nil, trace.Wrap(err) + } + attrs, err := s.deriveAttrs(authCtx, req.GetWorkloadAttrs()) + if err != nil { + return nil, trace.Wrap(err, "deriving attributes") + } + wi, err := s.cache.GetWorkloadIdentity(ctx, req.GetName()) if err != nil { return nil, trace.Wrap(err) @@ -276,126 +182,167 @@ func (s *IssuanceService) IssueWorkloadIdentity( return nil, trace.Wrap(err) } - attrs, err := s.deriveAttrs(authCtx, req.GetWorkloadAttrs()) - if err != nil { - return nil, trace.Wrap(err, "deriving attributes") + decision := decide(ctx, wi, attrs) + if !decision.shouldIssue { + return nil, trace.Wrap(decision.reason, "workload identity failed evaluation") } - // Evaluate any rules explicitly configured by the user - if err := evaluateRules(wi, attrs); err != nil { - return nil, trace.Wrap(err) + + var cred *workloadidentityv1pb.Credential + switch v := req.GetCredential().(type) { + case *workloadidentityv1pb.IssueWorkloadIdentityRequest_X509SvidParams: + ca, err := s.getX509CA(ctx) + if err != nil { + return nil, trace.Wrap(err, "fetching X509 SPIFFE CA") + } + cred, err = s.issueX509SVID( + ctx, + ca, + decision.templatedWorkloadIdentity, + v.X509SvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap(err, "issuing X509 SVID") + } + case *workloadidentityv1pb.IssueWorkloadIdentityRequest_JwtSvidParams: + key, issuer, err := s.getJWTIssuerKey(ctx) + if err != nil { + return nil, trace.Wrap(err, "getting JWT issuer key") + } + cred, err = s.issueJWTSVID( + ctx, + key, + issuer, + decision.templatedWorkloadIdentity, + v.JwtSvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap(err, "issuing JWT SVID") + } + default: + return nil, trace.BadParameter("credential: unknown type %T", req.GetCredential()) } - // Perform any templating - spiffeIDPath, err := templateString(wi.GetSpec().GetSpiffe().GetId(), attrs) - if err != nil { - return nil, trace.Wrap(err, "templating spec.spiffe.id") + return &workloadidentityv1pb.IssueWorkloadIdentityResponse{ + Credential: cred, + }, nil +} + +// maxWorkloadIdentitiesIssued is the maximum number of workload identities that +// can be issued in a single request. +// TODO(noah): We'll want to make this tunable via env var or similar to make +// sure we can adjust it as needed. +var maxWorkloadIdentitiesIssued = 10 + +func (s *IssuanceService) IssueWorkloadIdentities( + ctx context.Context, + req *workloadidentityv1pb.IssueWorkloadIdentitiesRequest, +) (*workloadidentityv1pb.IssueWorkloadIdentitiesResponse, error) { + if !experiment.Enabled() { + return nil, trace.AccessDenied("workload identity issuance experiment is disabled") } - spiffeID, err := spiffeid.FromURI(&url.URL{ - Scheme: "spiffe", - Host: s.clusterName, - Path: spiffeIDPath, - }) - if err != nil { - return nil, trace.Wrap(err, "creating SPIFFE ID") + + switch { + case len(req.LabelSelectors) == 0: + return nil, trace.BadParameter("label_selectors: at least one label selector must be specified") + case req.GetCredential() == nil: + return nil, trace.BadParameter("at least one credential type must be requested") } - hint, err := templateString(wi.GetSpec().GetSpiffe().GetHint(), attrs) + authCtx, err := s.authorizer.Authorize(ctx) if err != nil { - return nil, trace.Wrap(err, "templating spec.spiffe.hint") + return nil, trace.Wrap(err) } - - // TODO(noah): Add more sophisticated control of the TTL. - ttl := time.Hour - if req.RequestedTtl != nil && req.RequestedTtl.AsDuration() != 0 { - ttl = req.RequestedTtl.AsDuration() - if ttl > defaultMaxTTL { - ttl = defaultMaxTTL - } + if err := authCtx.CheckAccessToKind(types.KindWorkloadIdentity, types.VerbRead, types.VerbList); err != nil { + return nil, trace.Wrap(err) } - - now := s.clock.Now() - notBefore := now.Add(-1 * time.Minute) - notAfter := now.Add(ttl) - - // Prepare event - evt := &apievents.SPIFFESVIDIssued{ - Metadata: apievents.Metadata{ - Type: events.SPIFFESVIDIssuedEvent, - Code: events.SPIFFESVIDIssuedSuccessCode, - }, - UserMetadata: authz.ClientUserMetadata(ctx), - ConnectionMetadata: authz.ConnectionMetadata(ctx), - SPIFFEID: spiffeID.String(), - Hint: hint, - WorkloadIdentity: wi.GetMetadata().GetName(), - WorkloadIdentityRevision: wi.GetMetadata().GetRevision(), + attrs, err := s.deriveAttrs(authCtx, req.GetWorkloadAttrs()) + if err != nil { + return nil, trace.Wrap(err, "deriving attributes") } - cred := &workloadidentityv1pb.Credential{ - WorkloadIdentityName: wi.GetMetadata().GetName(), - WorkloadIdentityRevision: wi.GetMetadata().GetRevision(), - SpiffeId: spiffeID.String(), - Hint: hint, + // Fetch all workload identities that match the label selectors AND the + // principal can access. + workloadIdentities, err := s.matchingAndAuthorizedWorkloadIdentities( + ctx, + authCtx, + convertLabels(req.LabelSelectors), + ) + if err != nil { + return nil, trace.Wrap(err) + } - ExpiresAt: timestamppb.New(notAfter), - Ttl: durationpb.New(ttl), + // Evaluate rules/templating for each worklaod identity, filtering out those + // that should not be issued. + shouldIssue := []*workloadidentityv1pb.WorkloadIdentity{} + for _, wi := range workloadIdentities { + decision := decide(ctx, wi, attrs) + if decision.shouldIssue { + shouldIssue = append(shouldIssue, decision.templatedWorkloadIdentity) + } + if len(shouldIssue) > maxWorkloadIdentitiesIssued { + // If we're now above the limit, then we want to exit out... + return nil, trace.BadParameter( + "number of identities that would be issued exceeds maximum permitted (max = %d), use more specific labels", + maxWorkloadIdentitiesIssued, + ) + } } + var creds = make([]*workloadidentityv1pb.Credential, 0, len(shouldIssue)) switch v := req.GetCredential().(type) { - case *workloadidentityv1pb.IssueWorkloadIdentityRequest_X509SvidParams: - evt.SVIDType = "x509" - certDer, certSerial, err := s.issueX509SVID( - ctx, - v.X509SvidParams, - notBefore, - notAfter, - spiffeID, - ) + case *workloadidentityv1pb.IssueWorkloadIdentitiesRequest_X509SvidParams: + ca, err := s.getX509CA(ctx) if err != nil { - return nil, trace.Wrap(err, "issuing X509 SVID") + return nil, trace.Wrap(err, "fetching CA to sign X509 SVID") } - serialStr := serialString(certSerial) - cred.Credential = &workloadidentityv1pb.Credential_X509Svid{ - X509Svid: &workloadidentityv1pb.X509SVIDCredential{ - Cert: certDer, - SerialNumber: serialStr, - }, + for _, wi := range shouldIssue { + cred, err := s.issueX509SVID( + ctx, + ca, + wi, + v.X509SvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap( + err, + "issuing X509 SVID for workload identity %q", + wi.GetMetadata().GetName(), + ) + } + creds = append(creds, cred) } - evt.SerialNumber = serialStr - case *workloadidentityv1pb.IssueWorkloadIdentityRequest_JwtSvidParams: - evt.SVIDType = "jwt" - signedJwt, jti, err := s.issueJWTSVID( - ctx, - v.JwtSvidParams, - now, - notAfter, - spiffeID, - ) + case *workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams: + key, issuer, err := s.getJWTIssuerKey(ctx) if err != nil { - return nil, trace.Wrap(err, "issuing JWT SVID") + return nil, trace.Wrap(err, "getting JWT issuer key") } - cred.Credential = &workloadidentityv1pb.Credential_JwtSvid{ - JwtSvid: &workloadidentityv1pb.JWTSVIDCredential{ - Jwt: signedJwt, - Jti: jti, - }, + for _, wi := range shouldIssue { + cred, err := s.issueJWTSVID( + ctx, + key, + issuer, + wi, + v.JwtSvidParams, + req.RequestedTtl.AsDuration(), + ) + if err != nil { + return nil, trace.Wrap( + err, + "issuing JWT SVID for workload identity %q", + wi.GetMetadata().GetName(), + ) + } + creds = append(creds, cred) } - evt.JTI = jti default: return nil, trace.BadParameter("credential: unknown type %T", req.GetCredential()) } - if err := s.emitter.EmitAuditEvent(ctx, evt); err != nil { - s.logger.WarnContext( - ctx, - "failed to emit audit event for SVID issuance", - "error", err, - "event", evt, - ) - } - - return &workloadidentityv1pb.IssueWorkloadIdentityResponse{ - Credential: cred, + return &workloadidentityv1pb.IssueWorkloadIdentitiesResponse{ + Credentials: creds, }, nil } @@ -461,53 +408,127 @@ func (s *IssuanceService) getX509CA( return tlsCA, nil } +func baseEvent( + ctx context.Context, + wi *workloadidentityv1pb.WorkloadIdentity, + spiffeID spiffeid.ID, +) *apievents.SPIFFESVIDIssued { + return &apievents.SPIFFESVIDIssued{ + Metadata: apievents.Metadata{ + Type: events.SPIFFESVIDIssuedEvent, + Code: events.SPIFFESVIDIssuedSuccessCode, + }, + UserMetadata: authz.ClientUserMetadata(ctx), + ConnectionMetadata: authz.ConnectionMetadata(ctx), + SPIFFEID: spiffeID.String(), + Hint: wi.GetSpec().GetSpiffe().GetHint(), + WorkloadIdentity: wi.GetMetadata().GetName(), + WorkloadIdentityRevision: wi.GetMetadata().GetRevision(), + } +} + +func calculateTTL( + clock clockwork.Clock, + requestedTTL time.Duration, +) (time.Time, time.Time, time.Time, time.Duration) { + ttl := time.Hour + if requestedTTL != 0 { + ttl = requestedTTL + if ttl > defaultMaxTTL { + ttl = defaultMaxTTL + } + } + now := clock.Now() + notBefore := now.Add(-1 * time.Minute) + notAfter := now.Add(ttl) + return now, notBefore, notAfter, ttl +} + func (s *IssuanceService) issueX509SVID( ctx context.Context, + ca *tlsca.CertAuthority, + wid *workloadidentityv1pb.WorkloadIdentity, params *workloadidentityv1pb.X509SVIDParams, - notBefore time.Time, - notAfter time.Time, - spiffeID spiffeid.ID, -) (_ []byte, _ *big.Int, err error) { + requestedTTL time.Duration, +) (_ *workloadidentityv1pb.Credential, err error) { ctx, span := tracer.Start(ctx, "IssuanceService/issueX509SVID") defer func() { tracing.EndSpan(span, err) }() switch { case params == nil: - return nil, nil, trace.BadParameter("x509_svid_params: is required") + return nil, trace.BadParameter("x509_svid_params: is required") case len(params.PublicKey) == 0: - return nil, nil, trace.BadParameter("x509_svid_params.public_key: is required") + return nil, trace.BadParameter("x509_svid_params.public_key: is required") } - pubKey, err := x509.ParsePKIXPublicKey(params.PublicKey) + spiffeID, err := spiffeid.FromURI(&url.URL{ + Scheme: "spiffe", + Host: s.clusterName, + Path: wid.GetSpec().GetSpiffe().GetId(), + }) if err != nil { - return nil, nil, trace.Wrap(err, "parsing public key") + return nil, trace.Wrap(err, "parsing SPIFFE ID") } + _, notBefore, notAfter, ttl := calculateTTL(s.clock, requestedTTL) - certSerial, err := generateCertSerial() + pubKey, err := x509.ParsePKIXPublicKey(params.PublicKey) if err != nil { - return nil, nil, trace.Wrap(err, "generating certificate serial") + return nil, trace.Wrap(err, "parsing public key") } - template := x509Template(certSerial, notBefore, notAfter, spiffeID) - ca, err := s.getX509CA(ctx) + certSerial, err := generateCertSerial() if err != nil { - return nil, nil, trace.Wrap(err, "fetching CA to sign X509 SVID") + return nil, trace.Wrap(err, "generating certificate serial") } + serialString := serialString(certSerial) + certBytes, err := x509.CreateCertificate( - rand.Reader, template, ca.Cert, pubKey, ca.Signer, + rand.Reader, + x509Template(certSerial, notBefore, notAfter, spiffeID), + ca.Cert, + pubKey, + ca.Signer, ) if err != nil { - return nil, nil, trace.Wrap(err) + return nil, trace.Wrap(err) } - return certBytes, certSerial, nil + evt := baseEvent(ctx, wid, spiffeID) + evt.SVIDType = "x509" + evt.SerialNumber = serialString + if err := s.emitter.EmitAuditEvent(ctx, evt); err != nil { + s.logger.WarnContext( + ctx, + "failed to emit audit event for SVID issuance", + "error", err, + "event", evt, + ) + } + + return &workloadidentityv1pb.Credential{ + WorkloadIdentityName: wid.GetMetadata().GetName(), + WorkloadIdentityRevision: wid.GetMetadata().GetRevision(), + + SpiffeId: spiffeID.String(), + Hint: wid.GetSpec().GetSpiffe().GetHint(), + + ExpiresAt: timestamppb.New(notAfter), + Ttl: durationpb.New(ttl), + + Credential: &workloadidentityv1pb.Credential_X509Svid{ + X509Svid: &workloadidentityv1pb.X509SVIDCredential{ + Cert: certBytes, + SerialNumber: serialString, + }, + }, + }, nil } const jtiLength = 16 func (s *IssuanceService) getJWTIssuerKey( ctx context.Context, -) (_ *jwt.Key, err error) { +) (_ *jwt.Key, _ string, err error) { ctx, span := tracer.Start(ctx, "IssuanceService/getJWTIssuerKey") defer func() { tracing.EndSpan(span, err) }() @@ -516,79 +537,173 @@ func (s *IssuanceService) getJWTIssuerKey( DomainName: s.clusterName, }, true) if err != nil { - return nil, trace.Wrap(err, "getting SPIFFE CA") + return nil, "", trace.Wrap(err, "getting SPIFFE CA") } jwtSigner, err := s.keyStore.GetJWTSigner(ctx, ca) if err != nil { - return nil, trace.Wrap(err, "getting JWT signer") + return nil, "", trace.Wrap(err, "getting JWT signer") } jwtKey, err := services.GetJWTSigner( jwtSigner, s.clusterName, s.clock, ) if err != nil { - return nil, trace.Wrap(err, "creating JWT signer") + return nil, "", trace.Wrap(err, "creating JWT signer") } - return jwtKey, nil + + // Determine the public address of the proxy for inclusion in the JWT as + // the issuer for purposes of OIDC compatibility. + issuer, err := oidc.IssuerForCluster(ctx, s.cache, "/workload-identity") + if err != nil { + return nil, "", trace.Wrap(err, "determining issuer URI") + } + + return jwtKey, issuer, nil } func (s *IssuanceService) issueJWTSVID( ctx context.Context, + issuerKey *jwt.Key, + issuerURI string, + wid *workloadidentityv1pb.WorkloadIdentity, params *workloadidentityv1pb.JWTSVIDParams, - now time.Time, - notAfter time.Time, - spiffeID spiffeid.ID, -) (_ string, _ string, err error) { + requestedTTL time.Duration, +) (_ *workloadidentityv1pb.Credential, err error) { ctx, span := tracer.Start(ctx, "IssuanceService/issueJWTSVID") defer func() { tracing.EndSpan(span, err) }() switch { case params == nil: - return "", "", trace.BadParameter("jwt_svid_params: is required") + return nil, trace.BadParameter("jwt_svid_params: is required") case len(params.Audiences) == 0: - return "", "", trace.BadParameter("jwt_svid_params.audiences: at least one audience should be specified") + return nil, trace.BadParameter("jwt_svid_params.audiences: at least one audience should be specified") } - jti, err := utils.CryptoRandomHex(jtiLength) - if err != nil { - return "", "", trace.Wrap(err, "generating JTI") - } - - key, err := s.getJWTIssuerKey(ctx) + spiffeID, err := spiffeid.FromURI(&url.URL{ + Scheme: "spiffe", + Host: s.clusterName, + Path: wid.GetSpec().GetSpiffe().GetId(), + }) if err != nil { - return "", "", trace.Wrap(err, "getting JWT issuer key") + return nil, trace.Wrap(err, "parsing SPIFFE ID") } + now, _, notAfter, ttl := calculateTTL(s.clock, requestedTTL) - // Determine the public address of the proxy for inclusion in the JWT as - // the issuer for purposes of OIDC compatibility. - issuer, err := oidc.IssuerForCluster(ctx, s.cache, "/workload-identity") + jti, err := utils.CryptoRandomHex(jtiLength) if err != nil { - return "", "", trace.Wrap(err, "determining issuer URI") + return nil, trace.Wrap(err, "generating JTI") } - signed, err := key.SignJWTSVID(jwt.SignParamsJWTSVID{ + signed, err := issuerKey.SignJWTSVID(jwt.SignParamsJWTSVID{ Audiences: params.Audiences, SPIFFEID: spiffeID, JTI: jti, - Issuer: issuer, + Issuer: issuerURI, SetIssuedAt: now, SetExpiry: notAfter, }) if err != nil { - return "", "", trace.Wrap(err, "signing jwt") + return nil, trace.Wrap(err, "signing jwt") } - return signed, jti, nil + evt := baseEvent(ctx, wid, spiffeID) + evt.SVIDType = "jwt" + evt.JTI = jti + if err := s.emitter.EmitAuditEvent(ctx, evt); err != nil { + s.logger.WarnContext( + ctx, + "failed to emit audit event for SVID issuance", + "error", err, + "event", evt, + ) + } + + return &workloadidentityv1pb.Credential{ + WorkloadIdentityName: wid.GetMetadata().GetName(), + WorkloadIdentityRevision: wid.GetMetadata().GetRevision(), + + SpiffeId: spiffeID.String(), + Hint: wid.GetSpec().GetSpiffe().GetHint(), + + ExpiresAt: timestamppb.New(notAfter), + Ttl: durationpb.New(ttl), + + Credential: &workloadidentityv1pb.Credential_JwtSvid{ + JwtSvid: &workloadidentityv1pb.JWTSVIDCredential{ + Jwt: signed, + Jti: jti, + }, + }, + }, nil } -func (s *IssuanceService) IssueWorkloadIdentities( +func (s *IssuanceService) getAllWorkloadIdentities( ctx context.Context, - req *workloadidentityv1pb.IssueWorkloadIdentitiesRequest, -) (*workloadidentityv1pb.IssueWorkloadIdentitiesResponse, error) { - // TODO(noah): Coming to a PR near you soon! - return nil, trace.NotImplemented("not implemented") +) ([]*workloadidentityv1pb.WorkloadIdentity, error) { + workloadIdentities := []*workloadidentityv1pb.WorkloadIdentity{} + page := "" + for { + pageItems, nextPage, err := s.cache.ListWorkloadIdentities(ctx, 0, page) + if err != nil { + return nil, trace.Wrap(err) + } + workloadIdentities = append(workloadIdentities, pageItems...) + if nextPage == "" { + break + } + page = nextPage + } + return workloadIdentities, nil +} + +// matchingAndAuthorizedWorkloadIdentities returns the workload identities that +// match the provided labels and the principal has access to. +func (s *IssuanceService) matchingAndAuthorizedWorkloadIdentities( + ctx context.Context, + authCtx *authz.Context, + labels types.Labels, +) ([]*workloadidentityv1pb.WorkloadIdentity, error) { + allWorkloadIdentities, err := s.getAllWorkloadIdentities(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + canAccess := []*workloadidentityv1pb.WorkloadIdentity{} + // Filter out identities user cannot access. + for _, wid := range allWorkloadIdentities { + if err := authCtx.Checker.CheckAccess( + types.Resource153ToResourceWithLabels(wid), + services.AccessState{}, + ); err == nil { + canAccess = append(canAccess, wid) + } + } + + canAccessAndInSearch := []*workloadidentityv1pb.WorkloadIdentity{} + for _, wid := range canAccess { + match, _, err := services.MatchLabelGetter( + labels, types.Resource153ToResourceWithLabels(wid), + ) + if err != nil { + // Maybe log and skip rather than returning an error? + return nil, trace.Wrap(err) + } + if match { + canAccessAndInSearch = append(canAccessAndInSearch, wid) + } + } + + return canAccessAndInSearch, nil +} + +func convertLabels(selectors []*workloadidentityv1pb.LabelSelector) types.Labels { + labels := types.Labels{} + for _, selector := range selectors { + labels[selector.Key] = selector.Values + } + return labels } func serialString(serial *big.Int) string { diff --git a/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go b/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go index 3b7a7b1d85759..617d382bf47f3 100644 --- a/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go +++ b/lib/auth/machineid/workloadidentityv1/workloadidentityv1_test.go @@ -18,6 +18,7 @@ package workloadidentityv1_test import ( "context" + "crypto" "crypto/x509" "errors" "fmt" @@ -81,13 +82,19 @@ func newTestTLSServer(t testing.TB) (*auth.TestTLSServer, *eventstest.MockRecord return srv, emitter } -func TestIssueWorkloadIdentity(t *testing.T) { - experimentStatus := experiment.Enabled() - defer experiment.SetEnabled(experimentStatus) - experiment.SetEnabled(true) +type issuanceTestPack struct { + srv *auth.TestTLSServer + eventRecorder *eventstest.MockRecorderEmitter + clock clockwork.Clock + issuer string + spiffeX509CAPool *x509.CertPool + spiffeJWTSigner crypto.Signer + spiffeJWTSignerKID string +} + +func newIssuanceTestPack(t *testing.T, ctx context.Context) *issuanceTestPack { srv, eventRecorder := newTestTLSServer(t) - ctx := context.Background() clock := srv.Auth().GetClock() // Upsert a fake proxy to ensure we have a public address to use for the @@ -119,11 +126,35 @@ func TestIssueWorkloadIdentity(t *testing.T) { kid, err := libjwt.KeyID(jwtSigner.Public()) require.NoError(t, err) + return &issuanceTestPack{ + srv: srv, + eventRecorder: eventRecorder, + clock: clock, + issuer: wantIssuer, + spiffeX509CAPool: spiffeX509CAPool, + spiffeJWTSigner: jwtSigner, + spiffeJWTSignerKID: kid, + } +} + +func TestIssueWorkloadIdentity(t *testing.T) { + experimentStatus := experiment.Enabled() + defer experiment.SetEnabled(experimentStatus) + experiment.SetEnabled(true) + + ctx := context.Background() + tp := newIssuanceTestPack(t, ctx) + wildcardAccess, _, err := auth.CreateUserAndRole( - srv.Auth(), + tp.srv.Auth(), "dog", []string{}, - []types.Rule{}, + []types.Rule{ + types.NewRule( + types.KindWorkloadIdentity, + []string{types.VerbRead, types.VerbList}, + ), + }, auth.WithRoleMutator(func(role types.Role) { role.SetWorkloadIdentityLabels(types.Allow, types.Labels{ types.Wildcard: []string{types.Wildcard}, @@ -131,14 +162,19 @@ func TestIssueWorkloadIdentity(t *testing.T) { }), ) require.NoError(t, err) - wilcardAccessClient, err := srv.NewClient(auth.TestUser(wildcardAccess.GetName())) + wilcardAccessClient, err := tp.srv.NewClient(auth.TestUser(wildcardAccess.GetName())) require.NoError(t, err) specificAccess, _, err := auth.CreateUserAndRole( - srv.Auth(), + tp.srv.Auth(), "cat", []string{}, - []types.Rule{}, + []types.Rule{ + types.NewRule( + types.KindWorkloadIdentity, + []string{types.VerbRead, types.VerbList}, + ), + }, auth.WithRoleMutator(func(role types.Role) { role.SetWorkloadIdentityLabels(types.Allow, types.Labels{ "foo": []string{"bar"}, @@ -146,7 +182,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { }), ) require.NoError(t, err) - specificAccessClient, err := srv.NewClient(auth.TestUser(specificAccess.GetName())) + specificAccessClient, err := tp.srv.NewClient(auth.TestUser(specificAccess.GetName())) require.NoError(t, err) // Generate a keypair to generate x509 SVIDs for. @@ -156,7 +192,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { require.NoError(t, err) // Create some WorkloadIdentity resources - full, err := srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + full, err := tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ Kind: types.KindWorkloadIdentity, Version: types.V1, Metadata: &headerv1.Metadata{ @@ -247,28 +283,28 @@ func TestIssueWorkloadIdentity(t *testing.T) { ), )) // Check expiry makes sense - require.WithinDuration(t, clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) // Check the JWT parsed, err := jwt.ParseSigned(cred.GetJwtSvid().GetJwt()) require.NoError(t, err) claims := jwt.Claims{} - err = parsed.Claims(jwtSigner.Public(), &claims) + err = parsed.Claims(tp.spiffeJWTSigner.Public(), &claims) require.NoError(t, err) // Check headers require.Len(t, parsed.Headers, 1) - require.Equal(t, kid, parsed.Headers[0].KeyID) + require.Equal(t, tp.spiffeJWTSignerKID, parsed.Headers[0].KeyID) // Check claims require.Equal(t, wantSPIFFEID, claims.Subject) require.NotEmpty(t, claims.ID) require.Equal(t, jwt.Audience{"example.com", "test.example.com"}, claims.Audience) - require.Equal(t, wantIssuer, claims.Issuer) - require.WithinDuration(t, clock.Now().Add(wantTTL), claims.Expiry.Time(), 5*time.Second) - require.WithinDuration(t, clock.Now(), claims.IssuedAt.Time(), 5*time.Second) + require.Equal(t, tp.issuer, claims.Issuer) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), claims.Expiry.Time(), 5*time.Second) + require.WithinDuration(t, tp.clock.Now(), claims.IssuedAt.Time(), 5*time.Second) // Check audit log event - evt, ok := eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) + evt, ok := tp.eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) require.True(t, ok) require.NotEmpty(t, evt.ConnectionMetadata.RemoteAddr) require.Equal(t, claims.ID, evt.JTI) @@ -337,7 +373,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { ), )) // Check expiry makes sense - require.WithinDuration(t, clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), cred.GetExpiresAt().AsTime(), time.Second) // Check the X509 cert, err := x509.ParseCertificate(cred.GetX509Svid().GetCert()) @@ -345,9 +381,9 @@ func TestIssueWorkloadIdentity(t *testing.T) { // Check included public key matches require.Equal(t, workloadKey.Public(), cert.PublicKey) // Check cert expiry - require.WithinDuration(t, clock.Now().Add(wantTTL), cert.NotAfter, time.Second) + require.WithinDuration(t, tp.clock.Now().Add(wantTTL), cert.NotAfter, time.Second) // Check cert nbf - require.WithinDuration(t, clock.Now().Add(-1*time.Minute), cert.NotBefore, time.Second) + require.WithinDuration(t, tp.clock.Now().Add(-1*time.Minute), cert.NotBefore, time.Second) // Check cert TTL require.Equal(t, cert.NotAfter.Sub(cert.NotBefore), wantTTL+time.Minute) @@ -371,13 +407,13 @@ func TestIssueWorkloadIdentity(t *testing.T) { // Check cert signature is valid _, err = cert.Verify(x509.VerifyOptions{ - Roots: spiffeX509CAPool, - CurrentTime: srv.Auth().GetClock().Now(), + Roots: tp.spiffeX509CAPool, + CurrentTime: tp.srv.Auth().GetClock().Now(), }) require.NoError(t, err) // Check audit log event - evt, ok := eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) + evt, ok := tp.eventRecorder.LastEvent().(*events.SPIFFESVIDIssued) require.True(t, ok) require.NotEmpty(t, evt.ConnectionMetadata.RemoteAddr) require.Equal(t, cred.GetX509Svid().GetSerialNumber(), evt.SerialNumber) @@ -459,7 +495,7 @@ func TestIssueWorkloadIdentity(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - eventRecorder.Reset() + tp.eventRecorder.Reset() c := workloadidentityv1pb.NewWorkloadIdentityIssuanceServiceClient( tt.client.GetConnection(), ) @@ -472,6 +508,316 @@ func TestIssueWorkloadIdentity(t *testing.T) { } } +func TestIssueWorkloadIdentities(t *testing.T) { + experimentStatus := experiment.Enabled() + defer experiment.SetEnabled(experimentStatus) + experiment.SetEnabled(true) + + ctx := context.Background() + tp := newIssuanceTestPack(t, ctx) + + user, _, err := auth.CreateUserAndRole( + tp.srv.Auth(), + "cat", + []string{}, + []types.Rule{ + types.NewRule( + types.KindWorkloadIdentity, + []string{types.VerbRead, types.VerbList}, + ), + }, + auth.WithRoleMutator(func(role types.Role) { + role.SetWorkloadIdentityLabels(types.Allow, types.Labels{ + "access": []string{"yes"}, + }) + }), + ) + require.NoError(t, err) + client, err := tp.srv.NewClient(auth.TestUser(user.GetName())) + require.NoError(t, err) + + // Generate a keypair to generate x509 SVIDs for. + workloadKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) + require.NoError(t, err) + workloadKeyPubBytes, err := x509.MarshalPKIXPublicKey(workloadKey.Public()) + require.NoError(t, err) + + // Create some WorkloadIdentity resources + _, err = tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: "bar-labeled", + Labels: map[string]string{ + "foo": "bar", + "access": "yes", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{ + Allow: []*workloadidentityv1pb.WorkloadIdentityRule{ + { + Conditions: []*workloadidentityv1pb.WorkloadIdentityCondition{ + { + Attribute: "workload.kubernetes.namespace", + Equals: "default", + }, + }, + }, + }, + }, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/example/{{user.name}}/{{ workload.kubernetes.namespace }}/{{ workload.kubernetes.service_account }}", + Hint: "Wow - what a lovely hint, {{user.name}}!", + }, + }, + }) + require.NoError(t, err) + _, err = tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: "buzz-labeled", + Labels: map[string]string{ + "foo": "buzz", + "access": "yes", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{ + Allow: []*workloadidentityv1pb.WorkloadIdentityRule{ + { + Conditions: []*workloadidentityv1pb.WorkloadIdentityCondition{ + { + Attribute: "workload.kubernetes.namespace", + Equals: "default", + }, + }, + }, + }, + }, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/example/{{user.name}}/{{ workload.kubernetes.namespace }}/{{ workload.kubernetes.service_account }}", + Hint: "Wow - what a lovely hint, {{user.name}}!", + }, + }, + }) + require.NoError(t, err) + + _, err = tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: "inaccessible", + Labels: map[string]string{ + "foo": "bar", + "access": "no", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{}, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/example", + }, + }, + }) + require.NoError(t, err) + + // Make enough to trip the "too many" error + for i := 0; i < 12; i++ { + _, err := tp.srv.Auth().CreateWorkloadIdentity(ctx, &workloadidentityv1pb.WorkloadIdentity{ + Kind: types.KindWorkloadIdentity, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: fmt.Sprintf("%d", i), + Labels: map[string]string{ + "error": "too-many", + "access": "yes", + }, + }, + Spec: &workloadidentityv1pb.WorkloadIdentitySpec{ + Rules: &workloadidentityv1pb.WorkloadIdentityRules{}, + Spiffe: &workloadidentityv1pb.WorkloadIdentitySPIFFE{ + Id: "/exampled", + }, + }, + }) + require.NoError(t, err) + } + + workloadAttrs := func(f func(attrs *workloadidentityv1pb.WorkloadAttrs)) *workloadidentityv1pb.WorkloadAttrs { + attrs := &workloadidentityv1pb.WorkloadAttrs{ + Kubernetes: &workloadidentityv1pb.WorkloadAttrsKubernetes{ + Attested: true, + Namespace: "default", + PodName: "test", + ServiceAccount: "bar", + }, + } + if f != nil { + f(attrs) + } + return attrs + } + tests := []struct { + name string + client *authclient.Client + req *workloadidentityv1pb.IssueWorkloadIdentitiesRequest + requireErr require.ErrorAssertionFunc + assert func(*testing.T, *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) + }{ + { + name: "jwt svid", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"bar", "buzz"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + workloadIdentitiesIssued := []string{} + for _, cred := range res.Credentials { + workloadIdentitiesIssued = append(workloadIdentitiesIssued, cred.WorkloadIdentityName) + + // Check a credential was actually included and is valid. + parsed, err := jwt.ParseSigned(cred.GetJwtSvid().GetJwt()) + require.NoError(t, err) + claims := jwt.Claims{} + err = parsed.Claims(tp.spiffeJWTSigner.Public(), &claims) + require.NoError(t, err) + } + require.EqualValues(t, []string{"bar-labeled", "buzz-labeled"}, workloadIdentitiesIssued) + }, + }, + { + name: "x509 svid", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"bar", "buzz"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_X509SvidParams{ + X509SvidParams: &workloadidentityv1pb.X509SVIDParams{ + PublicKey: workloadKeyPubBytes, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + workloadIdentitiesIssued := []string{} + for _, cred := range res.Credentials { + workloadIdentitiesIssued = append(workloadIdentitiesIssued, cred.WorkloadIdentityName) + // Check X509 cert actually included and signed. + cert, err := x509.ParseCertificate(cred.GetX509Svid().GetCert()) + require.NoError(t, err) + // Check included public key matches + require.Equal(t, workloadKey.Public(), cert.PublicKey) + _, err = cert.Verify(x509.VerifyOptions{ + Roots: tp.spiffeX509CAPool, + CurrentTime: tp.srv.Auth().GetClock().Now(), + }) + require.NoError(t, err) + } + require.EqualValues(t, []string{"bar-labeled", "buzz-labeled"}, workloadIdentitiesIssued) + }, + }, + { + name: "rules prevent issuing", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"bar", "buzz"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(func(attrs *workloadidentityv1pb.WorkloadAttrs) { + attrs.Kubernetes.Namespace = "not-default" + }), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + require.Empty(t, res.Credentials) + }, + }, + { + name: "no matching labels", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "foo", + Values: []string{"muahah"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: require.NoError, + assert: func(t *testing.T, res *workloadidentityv1pb.IssueWorkloadIdentitiesResponse) { + require.Empty(t, res.Credentials) + }, + }, + { + name: "too many to issue", + client: client, + req: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest{ + LabelSelectors: []*workloadidentityv1pb.LabelSelector{ + { + Key: "error", + Values: []string{"too-many"}, + }, + }, + Credential: &workloadidentityv1pb.IssueWorkloadIdentitiesRequest_JwtSvidParams{ + JwtSvidParams: &workloadidentityv1pb.JWTSVIDParams{ + Audiences: []string{"example.com", "test.example.com"}, + }, + }, + WorkloadAttrs: workloadAttrs(nil), + }, + requireErr: func(t require.TestingT, err error, i ...interface{}) { + require.ErrorContains(t, err, "number of identities that would be issued exceeds maximum permitted (max = 10), use more specific labels") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tp.eventRecorder.Reset() + c := workloadidentityv1pb.NewWorkloadIdentityIssuanceServiceClient( + tt.client.GetConnection(), + ) + res, err := c.IssueWorkloadIdentities(ctx, tt.req) + tt.requireErr(t, err) + if tt.assert != nil { + tt.assert(t, res) + } + }) + } +} + func TestResourceService_CreateWorkloadIdentity(t *testing.T) { t.Parallel() srv, eventRecorder := newTestTLSServer(t) diff --git a/lib/client/api.go b/lib/client/api.go index 88693bc768bb4..6cc0caae15286 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -5032,19 +5032,6 @@ func findActiveApps(keyRing *KeyRing) ([]tlsca.RouteToApp, error) { return apps, nil } -// getDesktopEventWebURL returns the web UI URL users can access to -// watch a desktop session recording in the browser -func getDesktopEventWebURL(proxyHost string, cluster string, sid *session.ID, events []events.EventFields) string { - if len(events) < 1 { - return "" - } - start := events[0].GetTimestamp() - end := events[len(events)-1].GetTimestamp() - duration := end.Sub(start) - - return fmt.Sprintf("https://%s/web/cluster/%s/session/%s?recordingType=desktop&durationMs=%d", proxyHost, cluster, sid, duration/time.Millisecond) -} - // SearchSessionEvents allows searching for session events with a full pagination support. func (tc *TeleportClient) SearchSessionEvents(ctx context.Context, fromUTC, toUTC time.Time, pageSize int, order types.EventOrder, max int) ([]apievents.AuditEvent, error) { ctx, span := tc.Tracer.Start( diff --git a/lib/client/api_test.go b/lib/client/api_test.go index 9da016c7af401..136d338b01e39 100644 --- a/lib/client/api_test.go +++ b/lib/client/api_test.go @@ -27,7 +27,6 @@ import ( "math" "os" "testing" - "time" "github.com/coreos/go-semver/semver" "github.com/google/go-cmp/cmp" @@ -45,10 +44,8 @@ import ( "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" - "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/observability/tracing" - "github.com/gravitational/teleport/lib/session" "github.com/gravitational/teleport/lib/utils" ) @@ -929,78 +926,6 @@ func TestFormatConnectToProxyErr(t *testing.T) { } } -func TestGetDesktopEventWebURL(t *testing.T) { - initDate := time.Date(2021, 1, 1, 12, 0, 0, 0, time.UTC) - - tt := []struct { - name string - proxyHost string - cluster string - sid session.ID - events []events.EventFields - expected string - }{ - { - name: "nil events", - events: nil, - expected: "", - }, - { - name: "empty events", - events: make([]events.EventFields, 0), - expected: "", - }, - { - name: "two events, 1000 ms duration", - proxyHost: "host", - cluster: "cluster", - sid: "session_id", - events: []events.EventFields{ - { - "time": initDate, - }, - { - "time": initDate.Add(1000 * time.Millisecond), - }, - }, - expected: "https://host/web/cluster/cluster/session/session_id?recordingType=desktop&durationMs=1000", - }, - { - name: "multiple events", - proxyHost: "host", - cluster: "cluster", - sid: "session_id", - events: []events.EventFields{ - { - "time": initDate, - }, - { - "time": initDate.Add(10 * time.Millisecond), - }, - { - "time": initDate.Add(20 * time.Millisecond), - }, - { - "time": initDate.Add(30 * time.Millisecond), - }, - { - "time": initDate.Add(40 * time.Millisecond), - }, - { - "time": initDate.Add(50 * time.Millisecond), - }, - }, - expected: "https://host/web/cluster/cluster/session/session_id?recordingType=desktop&durationMs=50", - }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - require.Equal(t, tc.expected, getDesktopEventWebURL(tc.proxyHost, tc.cluster, &tc.sid, tc.events)) - }) - } -} - type mockRoleGetter func(ctx context.Context) ([]types.Role, error) func (m mockRoleGetter) GetRoles(ctx context.Context) ([]types.Role, error) { diff --git a/lib/events/api.go b/lib/events/api.go index a546c65d8bcd1..0eb7d639c9619 100644 --- a/lib/events/api.go +++ b/lib/events/api.go @@ -848,6 +848,11 @@ const ( WorkloadIdentityUpdateEvent = "workload_identity.update" // WorkloadIdentityDeleteEvent is emitted when a WorkloadIdentity resource is deleted. WorkloadIdentityDeleteEvent = "workload_identity.delete" + + // ContactCreateEvent is emitted when a Contact resource is created. + ContactCreateEvent = "contact.create" + // ContactDeleteEvent is emitted when a Contact resource is deleted. + ContactDeleteEvent = "contact.delete" ) // Add an entry to eventsMap in lib/events/events_test.go when you add diff --git a/lib/events/athena/athena.go b/lib/events/athena/athena.go index 0fa5416a5e1cb..12eb268708a25 100644 --- a/lib/events/athena/athena.go +++ b/lib/events/athena/athena.go @@ -541,7 +541,11 @@ func (l *Log) SearchSessionEvents(ctx context.Context, req events.SearchSessionE } func (l *Log) Close() error { - return trace.Wrap(l.consumerCloser.Close()) + // consumerCloser is nil when consumer is disabled. + if l.consumerCloser != nil { + return trace.Wrap(l.consumerCloser.Close()) + } + return nil } func (l *Log) IsConsumerDisabled() bool { diff --git a/lib/events/auditlog.go b/lib/events/auditlog.go index 51180746cbe7f..274c3c65c56a6 100644 --- a/lib/events/auditlog.go +++ b/lib/events/auditlog.go @@ -509,9 +509,23 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID e := make(chan error, 1) c := make(chan apievents.AuditEvent) + sessionStartCh := make(chan apievents.AuditEvent, 1) + if startCb, err := sessionStartCallbackFromContext(ctx); err == nil { + go func() { + evt, ok := <-sessionStartCh + if !ok { + startCb(nil, trace.NotFound("session start event not found")) + return + } + + startCb(evt, nil) + }() + } + rawSession, err := os.CreateTemp(l.playbackDir, string(sessionID)+".stream.tar.*") if err != nil { e <- trace.Wrap(trace.ConvertSystemError(err), "creating temporary stream file") + close(sessionStartCh) return c, e } // The file is still perfectly usable after unlinking it, and the space it's @@ -528,6 +542,7 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID if err := os.Remove(rawSession.Name()); err != nil { _ = rawSession.Close() e <- trace.Wrap(trace.ConvertSystemError(err), "removing temporary stream file") + close(sessionStartCh) return c, e } @@ -538,6 +553,7 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID err = trace.NotFound("a recording for session %v was not found", sessionID) } e <- trace.Wrap(err) + close(sessionStartCh) return c, e } l.log.DebugContext(ctx, "Downloaded session to a temporary file for streaming.", @@ -547,6 +563,8 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID go func() { defer rawSession.Close() + defer close(sessionStartCh) + // this shouldn't be necessary as the position should be already 0 (Download // takes an io.WriterAt), but it's better to be safe than sorry if _, err := rawSession.Seek(0, io.SeekStart); err != nil { @@ -555,7 +573,9 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID } protoReader := NewProtoReader(rawSession) + defer protoReader.Close() + firstEvent := true for { if ctx.Err() != nil { e <- trace.Wrap(ctx.Err()) @@ -572,6 +592,11 @@ func (l *AuditLog) StreamSessionEvents(ctx context.Context, sessionID session.ID return } + if firstEvent { + sessionStartCh <- event + firstEvent = false + } + if event.GetIndex() >= startIndex { select { case c <- event: @@ -666,3 +691,39 @@ func (l *AuditLog) periodicSpaceMonitor() { } } } + +// streamSessionEventsContextKey represent context keys used by +// StreamSessionEvents function. +type streamSessionEventsContextKey string + +const ( + // sessionStartCallbackContextKey is the context key used to store the + // session start callback function. + sessionStartCallbackContextKey streamSessionEventsContextKey = "session-start" +) + +// SessionStartCallback is the function used when streaming reaches the start +// event. If any error, such as session not found, the event will be nil, and +// the error will be set. +type SessionStartCallback func(startEvent apievents.AuditEvent, err error) + +// ContextWithSessionStartCallback returns a context.Context containing a +// session start event callback. +func ContextWithSessionStartCallback(ctx context.Context, cb SessionStartCallback) context.Context { + return context.WithValue(ctx, sessionStartCallbackContextKey, cb) +} + +// sessionStartCallbackFromContext returns the session start callback from +// context.Context. +func sessionStartCallbackFromContext(ctx context.Context) (SessionStartCallback, error) { + if ctx == nil { + return nil, trace.BadParameter("context is nil") + } + + cb, ok := ctx.Value(sessionStartCallbackContextKey).(SessionStartCallback) + if !ok { + return nil, trace.BadParameter("session start callback function was not found in the context") + } + + return cb, nil +} diff --git a/lib/events/auditlog_test.go b/lib/events/auditlog_test.go index b76d27a0ee36a..416373e3e6951 100644 --- a/lib/events/auditlog_test.go +++ b/lib/events/auditlog_test.go @@ -154,6 +154,137 @@ func TestConcurrentStreaming(t *testing.T) { } } +func TestStreamSessionEvents(t *testing.T) { + uploader := eventstest.NewMemoryUploader() + alog, err := events.NewAuditLog(events.AuditLogConfig{ + DataDir: t.TempDir(), + Clock: clockwork.NewFakeClock(), + ServerID: "remote", + UploadHandler: uploader, + }) + require.NoError(t, err) + t.Cleanup(func() { alog.Close() }) + + ctx := context.Background() + sid := session.NewID() + sessionEvents := []apievents.AuditEvent{ + &apievents.DatabaseSessionStart{ + Metadata: apievents.Metadata{ + Type: events.DatabaseSessionStartEvent, + Code: events.DatabaseSessionStartCode, + Index: 0, + }, + SessionMetadata: apievents.SessionMetadata{ + SessionID: sid.String(), + }, + }, + &apievents.DatabaseSessionEnd{ + Metadata: apievents.Metadata{ + Type: events.DatabaseSessionEndEvent, + Code: events.DatabaseSessionEndCode, + Index: 1, + }, + SessionMetadata: apievents.SessionMetadata{ + SessionID: sid.String(), + }, + }, + } + + streamer, err := events.NewProtoStreamer(events.ProtoStreamerConfig{ + Uploader: uploader, + }) + require.NoError(t, err) + stream, err := streamer.CreateAuditStream(ctx, sid) + require.NoError(t, err) + for _, event := range sessionEvents { + require.NoError(t, stream.RecordEvent(ctx, eventstest.PrepareEvent(event))) + } + require.NoError(t, stream.Complete(ctx)) + + type callbackResult struct { + event apievents.AuditEvent + err error + } + + t.Run("Success", func(t *testing.T) { + for name, withCallback := range map[string]bool{ + "WithCallback": true, + "WithoutCallback": false, + } { + t.Run(name, func(t *testing.T) { + streamCtx, cancel := context.WithCancel(ctx) + defer cancel() + + callbackCh := make(chan callbackResult, 1) + if withCallback { + streamCtx = events.ContextWithSessionStartCallback(streamCtx, func(ae apievents.AuditEvent, err error) { + callbackCh <- callbackResult{ae, err} + }) + } + + ch, _ := alog.StreamSessionEvents(streamCtx, sid, 0) + for _, event := range sessionEvents { + select { + case receivedEvent := <-ch: + require.NotNil(t, receivedEvent) + require.Equal(t, event.GetCode(), receivedEvent.GetCode()) + require.Equal(t, event.GetType(), receivedEvent.GetType()) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to receive session event %q but got nothing", event.GetType()) + } + } + + if withCallback { + select { + case res := <-callbackCh: + require.NoError(t, res.err) + require.Equal(t, sessionEvents[0].GetCode(), res.event.GetCode()) + require.Equal(t, sessionEvents[0].GetType(), res.event.GetType()) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to receive callback result but got nothing") + } + } + }) + } + }) + + t.Run("Error", func(t *testing.T) { + for name, withCallback := range map[string]bool{ + "WithCallback": true, + "WithoutCallback": false, + } { + t.Run(name, func(t *testing.T) { + streamCtx, cancel := context.WithCancel(ctx) + defer cancel() + + callbackCh := make(chan callbackResult, 1) + if withCallback { + streamCtx = events.ContextWithSessionStartCallback(streamCtx, func(ae apievents.AuditEvent, err error) { + callbackCh <- callbackResult{ae, err} + }) + } + + _, errCh := alog.StreamSessionEvents(streamCtx, session.ID("random"), 0) + select { + case err := <-errCh: + require.Error(t, err) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to get error while stream but got nothing") + } + + if withCallback { + select { + case res := <-callbackCh: + require.Error(t, res.err) + case <-time.After(10 * time.Second): + require.Fail(t, "expected to receive callback result but got nothing") + } + } + }) + } + }) +} + func TestExternalLog(t *testing.T) { m := &eventstest.MockAuditLog{ Emitter: &eventstest.MockRecorderEmitter{}, diff --git a/lib/events/codes.go b/lib/events/codes.go index 24c95319a08bb..2a7568ebf42f9 100644 --- a/lib/events/codes.go +++ b/lib/events/codes.go @@ -681,6 +681,11 @@ const ( // WorkloadIdentityDeleteCode is the workload identity delete event code. WorkloadIdentityDeleteCode = "WID003I" + // ContactCreateCode is the auto update version create event code. + ContactCreateCode = "TCTC001I" + // ContactDeleteCode is the auto update version delete event code. + ContactDeleteCode = "TCTC002I" + // UnknownCode is used when an event of unknown type is encountered. UnknownCode = apievents.UnknownCode ) diff --git a/lib/events/dynamic.go b/lib/events/dynamic.go index 2f59ddfb820e7..eba6ff4372ac0 100644 --- a/lib/events/dynamic.go +++ b/lib/events/dynamic.go @@ -472,6 +472,10 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) { case WorkloadIdentityDeleteEvent: e = &events.WorkloadIdentityDelete{} + case ContactCreateEvent: + e = &events.ContactCreate{} + case ContactDeleteEvent: + e = &events.ContactDelete{} default: slog.ErrorContext(context.Background(), "Attempted to convert dynamic event of unknown type into protobuf event.", "event_type", eventType) unknown := &events.Unknown{} diff --git a/lib/events/events_test.go b/lib/events/events_test.go index edee98e728439..7011bd55af4e4 100644 --- a/lib/events/events_test.go +++ b/lib/events/events_test.go @@ -246,6 +246,8 @@ var eventsMap = map[string]apievents.AuditEvent{ WorkloadIdentityCreateEvent: &apievents.WorkloadIdentityCreate{}, WorkloadIdentityUpdateEvent: &apievents.WorkloadIdentityUpdate{}, WorkloadIdentityDeleteEvent: &apievents.WorkloadIdentityDelete{}, + ContactCreateEvent: &apievents.ContactCreate{}, + ContactDeleteEvent: &apievents.ContactDelete{}, } // TestJSON tests JSON marshal events diff --git a/lib/events/filesessions/fileasync.go b/lib/events/filesessions/fileasync.go index 27b14f4408fd7..62d96fb0a593a 100644 --- a/lib/events/filesessions/fileasync.go +++ b/lib/events/filesessions/fileasync.go @@ -291,7 +291,7 @@ func (u *Uploader) Scan(ctx context.Context) (*ScanStats, error) { stats.Started++ } if stats.Scanned > 0 { - u.log.DebugContext(ctx, "Session recording scan completed ", "scanned", stats.Scanned, "started", stats.Started, "corupted", stats.Corrupted, "upload_dir", u.cfg.ScanDir) + u.log.DebugContext(ctx, "Session recording scan completed ", "scanned", stats.Scanned, "started", stats.Started, "corrupted", stats.Corrupted, "upload_dir", u.cfg.ScanDir) } return &stats, nil } diff --git a/lib/srv/desktop/rdp/rdpclient/src/lib.rs b/lib/srv/desktop/rdp/rdpclient/src/lib.rs index 200f1b6a6186a..c82663f704c73 100644 --- a/lib/srv/desktop/rdp/rdpclient/src/lib.rs +++ b/lib/srv/desktop/rdp/rdpclient/src/lib.rs @@ -39,6 +39,7 @@ use rdpdr::tdp::{ }; use std::ffi::CString; use std::fmt::Debug; +use std::io::ErrorKind; use std::os::raw::c_char; use std::ptr; use util::{from_c_string, from_go_array}; @@ -141,12 +142,17 @@ pub unsafe extern "C" fn client_run(cgo_handle: CgoHandle, params: CGOConnectPar None => ptr::null_mut(), }, }, - Err(e) => { error!("client_run failed: {:?}", e); + let message = match e { + client::ClientError::Tcp(io_err) if io_err.kind() == ErrorKind::TimedOut => { + String::from(TIMEOUT_ERROR_MESSAGE) + } + _ => format!("{}", e), + }; CGOResult { err_code: CGOErrCode::ErrCodeFailure, - message: CString::new(format!("{}", e)) + message: CString::new(message) .map(|c| c.into_raw()) .unwrap_or(ptr::null_mut()), } @@ -154,6 +160,13 @@ pub unsafe extern "C" fn client_run(cgo_handle: CgoHandle, params: CGOConnectPar } } +const TIMEOUT_ERROR_MESSAGE: &str = "Connection Timed Out\n\n\ +Teleport could not connect to the host within the timeout period. \ +This could be due to a firewall blocking connections, an overloaded system, \ +or network congestion. To resolve this issue, ensure that the Teleport agent \ +has connectivity to the Windows host.\n\n\ +Use \"nc -vz HOST 3389\" to help debug this issue."; + fn handle_operation(cgo_handle: CgoHandle, ctx: &'static str, f: T) -> CGOErrCode where T: FnOnce(ClientHandle) -> ClientResult<()>, diff --git a/lib/utils/log/buffer.go b/lib/utils/log/buffer.go index c158808bd545c..d12ac9e11bab0 100644 --- a/lib/utils/log/buffer.go +++ b/lib/utils/log/buffer.go @@ -21,6 +21,14 @@ func newBuffer() *buffer { return bufPool.Get().(*buffer) } +func (b *buffer) Len() int { + return len(*b) +} + +func (b *buffer) SetLen(n int) { + *b = (*b)[:n] +} + func (b *buffer) Free() { // To reduce peak allocation, return only smaller buffers to the pool. const maxBufferSize = 16 << 10 @@ -49,35 +57,6 @@ func (b *buffer) WriteByte(c byte) error { return nil } -func (b *buffer) WritePosInt(i int) { - b.WritePosIntWidth(i, 0) -} - -// WritePosIntWidth writes non-negative integer i to the buffer, padded on the left -// by zeroes to the given width. Use a width of 0 to omit padding. -func (b *buffer) WritePosIntWidth(i, width int) { - // Cheap integer to fixed-width decimal ASCII. - // Copied from log/log.go. - - if i < 0 { - panic("negative int") - } - - // Assemble decimal in reverse order. - var bb [20]byte - bp := len(bb) - 1 - for i >= 10 || width > 1 { - width-- - q := i / 10 - bb[bp] = byte('0' + i - q*10) - bp-- - i = q - } - // i < 10 - bb[bp] = byte('0' + i) - b.Write(bb[bp:]) -} - func (b *buffer) String() string { return string(*b) } diff --git a/lib/utils/log/formatter_test.go b/lib/utils/log/formatter_test.go index 39c717df3425d..e11a9f63620fb 100644 --- a/lib/utils/log/formatter_test.go +++ b/lib/utils/log/formatter_test.go @@ -45,7 +45,7 @@ import ( "github.com/gravitational/teleport" ) -const message = "Adding diagnostic debugging handlers.\t To connect with profiler, use `go tool pprof diag_addr`." +const message = "Adding diagnostic debugging handlers.\t To connect with profiler, use go tool pprof diag_addr." var ( logErr = errors.New("the quick brown fox jumped really high") @@ -76,7 +76,6 @@ func TestOutput(t *testing.T) { loc, err := time.LoadLocation("Africa/Cairo") require.NoError(t, err, "failed getting timezone") clock := clockwork.NewFakeClockAt(time.Now().In(loc)) - formattedNow := clock.Now().UTC().Format(time.RFC3339) t.Run("text", func(t *testing.T) { // fieldsRegex matches all the key value pairs emitted after the message and before the caller. All fields are @@ -88,7 +87,7 @@ func TestOutput(t *testing.T) { // 2) the message // 3) the fields // 4) the caller - outputRegex := regexp.MustCompile("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z)(\\s+.*)(\".*diag_addr`\\.\")(.*)(\\slog/formatter_test.go:\\d{3})") + outputRegex := regexp.MustCompile(`(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)(\s+.*)(".*diag_addr\.")(.*)(\slog/formatter_test.go:\d{3})`) tests := []struct { name string @@ -149,7 +148,7 @@ func TestOutput(t *testing.T) { EnableColors: true, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { - a.Value = slog.StringValue(formattedNow) + a.Value = slog.TimeValue(clock.Now().UTC()) } return a }, @@ -188,7 +187,7 @@ func TestOutput(t *testing.T) { // Match level, and component: DEBU [TEST] assert.Empty(t, cmp.Diff(logrusMatches[2], slogMatches[2]), "level, and component to be identical") - // Match the log message: "Adding diagnostic debugging handlers.\t To connect with profiler, use `go tool pprof diag_addr`.\n" + // Match the log message: "Adding diagnostic debugging handlers.\t To connect with profiler, use go tool pprof diag_addr.\n" assert.Empty(t, cmp.Diff(logrusMatches[3], slogMatches[3]), "expected output messages to be identical") // The last matches are the caller information assert.Equal(t, fmt.Sprintf(" log/formatter_test.go:%d", logrusTestLogLineNumber), logrusMatches[5]) @@ -461,7 +460,13 @@ func TestConcurrentOutput(t *testing.T) { wg.Add(1) go func(i int) { defer wg.Done() - logger.InfoContext(ctx, "Teleport component entered degraded state", "component", i) + logger.InfoContext(ctx, "Teleport component entered degraded state", + slog.Int("component", i), + slog.Group("group", + slog.String("test", "123"), + slog.String("animal", "llama"), + ), + ) }(i) } wg.Wait() diff --git a/lib/utils/log/handle_state.go b/lib/utils/log/handle_state.go new file mode 100644 index 0000000000000..c60132b28e48e --- /dev/null +++ b/lib/utils/log/handle_state.go @@ -0,0 +1,352 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package log + +import ( + "encoding" + "fmt" + "log/slog" + "reflect" + "strconv" + "sync" + "time" + + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + + "github.com/gravitational/teleport" +) + +// handleState adapted from go/src/log/slog/handler.go +type handleState struct { + h *SlogTextHandler + buf *buffer + freeBuf bool // should buf be freed? + prefix *buffer // for text: key prefix + groups *[]string // pool-allocated slice of active groups, for ReplaceAttr +} + +var groupPool = sync.Pool{New: func() any { + s := make([]string, 0, 10) + return &s +}} + +func (s *handleState) free() { + if s.freeBuf { + s.buf.Free() + } + if gs := s.groups; gs != nil { + *gs = (*gs)[:0] + groupPool.Put(gs) + } + s.prefix.Free() +} + +func (s *handleState) openGroups() { + for _, n := range s.h.groups[s.h.nOpenGroups:] { + s.openGroup(n) + } +} + +// openGroup starts a new group of attributes +// with the given name. +func (s *handleState) openGroup(name string) { + s.prefix.WriteString(name) + s.prefix.WriteByte('.') + + // Collect group names for ReplaceAttr. + if s.groups != nil { + *s.groups = append(*s.groups, name) + } +} + +// closeGroup ends the group with the given name. +func (s *handleState) closeGroup(name string) { + *s.prefix = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */] + + if s.groups != nil { + *s.groups = (*s.groups)[:len(*s.groups)-1] + } +} + +// appendAttrs appends the slice of Attrs. +// It reports whether something was appended. +func (s *handleState) appendAttrs(as []slog.Attr) bool { + nonEmpty := false + for _, a := range as { + if s.appendAttr(a) { + nonEmpty = true + } + } + return nonEmpty +} + +// appendAttr appends the Attr's key and value. +// It handles replacement and checking for an empty key. +// It reports whether something was appended. +func (s *handleState) appendAttr(a slog.Attr) bool { + a.Value = a.Value.Resolve() + if rep := s.h.cfg.ReplaceAttr; rep != nil && a.Value.Kind() != slog.KindGroup { + var gs []string + if s.groups != nil { + gs = *s.groups + } + // a.Value is resolved before calling ReplaceAttr, so the user doesn't have to. + a = rep(gs, a) + // The ReplaceAttr function may return an unresolved Attr. + a.Value = a.Value.Resolve() + } + // Elide empty Attrs. + if a.Equal(slog.Attr{}) { + return false + } + + // Handle nested attributes from within component fields. + if a.Key == teleport.ComponentFields { + nonEmpty := false + switch fields := a.Value.Any().(type) { + case map[string]any: + for k, v := range fields { + if s.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + return nonEmpty + case logrus.Fields: + for k, v := range fields { + if s.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + return nonEmpty + } + } + + // Handle special cases before formatting. + if a.Value.Kind() == slog.KindAny { + switch v := a.Value.Any().(type) { + case *slog.Source: + a.Value = slog.StringValue(fmt.Sprintf(" %s:%d", v.File, v.Line)) + case trace.Error: + a.Value = slog.StringValue("[" + v.DebugReport() + "]") + case error: + a.Value = slog.StringValue(fmt.Sprintf("[%v]", v)) + } + } + + if a.Value.Kind() == slog.KindGroup { + attrs := a.Value.Group() + // Output only non-empty groups. + if len(attrs) > 0 { + // The group may turn out to be empty even though it has attrs (for + // example, ReplaceAttr may delete all the attrs). + // So remember where we are in the buffer, to restore the position + // later if necessary. + pos := s.buf.Len() + // Inline a group with an empty key. + if a.Key != "" { + s.openGroup(a.Key) + } + if !s.appendAttrs(attrs) { + s.buf.SetLen(pos) + return false + } + if a.Key != "" { + s.closeGroup(a.Key) + } + } + + return true + } + + if a.Value.Kind() == slog.KindString && a.Key != slog.LevelKey { + val := a.Value.String() + if needsQuoting(val) { + a.Value = slog.StringValue(strconv.Quote(val)) + } + } + + s.appendKey(a.Key) + + // Write the log key directly to avoid quoting + // color formatting that exists. + if a.Key == slog.LevelKey { + s.buf.WriteString(a.Value.String()) + } else { + s.appendValue(a.Value) + } + + return true +} + +func (s *handleState) appendError(err error) { + s.appendString(fmt.Sprintf("!ERROR:%v", err)) +} + +func (s *handleState) appendKey(key string) { + if s.buf.Len() > 0 { + s.buf.WriteString(" ") + } + + // These keys should not be included in the output to match + // the behavior of the lorgus formatter. + if key == slog.TimeKey || + key == teleport.ComponentKey || + key == slog.LevelKey || + key == CallerField || + key == slog.MessageKey || + key == slog.SourceKey { + return + } + + if s.prefix != nil && len(*s.prefix) > 0 { + // TODO: optimize by avoiding allocation. + s.appendString(string(*s.prefix) + key) + } else { + s.appendString(key) + } + + s.buf.WriteByte(':') +} + +func (s *handleState) appendString(str string) { + if str == "" { + return + } + + if needsQuoting(str) { + *s.buf = strconv.AppendQuote(*s.buf, str) + } else { + s.buf.WriteString(str) + } +} + +func (s *handleState) appendValue(v slog.Value) { + defer func() { + if r := recover(); r != nil { + // If it panics with a nil pointer, the most likely cases are + // an encoding.TextMarshaler or error fails to guard against nil, + // in which case "" seems to be the feasible choice. + // + // Adapted from the code in fmt/print.go. + if v := reflect.ValueOf(v.Any()); v.Kind() == reflect.Pointer && v.IsNil() { + s.appendString("") + return + } + + // Otherwise just print the original panic message. + s.appendString(fmt.Sprintf("!PANIC: %v", r)) + } + }() + + if err := appendTextValue(s, v); err != nil { + s.appendError(err) + } +} + +func (s *handleState) appendTime(t time.Time) { + *s.buf = appendRFC3339Millis(*s.buf, t) +} + +func (s *handleState) appendNonBuiltIns(r slog.Record) { + // preformatted Attrs + if pfa := s.h.preformatted; len(pfa) > 0 { + s.buf.WriteString(" ") + s.buf.Write(pfa) + } + // Attrs in Record -- unlike the built-in ones, they are in groups started + // from WithGroup. + // If the record has no Attrs, don't output any groups. + if r.NumAttrs() > 0 { + s.prefix.WriteString(s.h.groupPrefix) + // The group may turn out to be empty even though it has attrs (for + // example, ReplaceAttr may delete all the attrs). + // So remember where we are in the buffer, to restore the position + // later if necessary. + pos := s.buf.Len() + s.openGroups() + empty := true + r.Attrs(func(a slog.Attr) bool { + // The component is handled by the top level handler. + if a.Key == teleport.ComponentKey { + return true + } + if s.appendAttr(a) { + empty = false + } + return true + }) + if empty { + s.buf.SetLen(pos) + } + } +} + +func byteSlice(a any) ([]byte, bool) { + if bs, ok := a.([]byte); ok { + return bs, true + } + // Like Printf's %s, we allow both the slice type and the byte element type to be named. + t := reflect.TypeOf(a) + if t != nil && t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { + return reflect.ValueOf(a).Bytes(), true + } + return nil, false +} + +func appendTextValue(s *handleState, v slog.Value) error { + switch v.Kind() { + case slog.KindString: + s.appendString(v.String()) + case slog.KindTime: + s.appendTime(v.Time()) + case slog.KindAny: + if tm, ok := v.Any().(encoding.TextMarshaler); ok { + data, err := tm.MarshalText() + if err != nil { + return err + } + // TODO: avoid the conversion to string. + s.appendString(string(data)) + return nil + } + if bs, ok := byteSlice(v.Any()); ok { + // As of Go 1.19, this only allocates for strings longer than 32 bytes. + s.buf.WriteString(strconv.Quote(string(bs))) + return nil + } + s.appendString(fmt.Sprintf("%+v", v.Any())) + case slog.KindInt64: + *s.buf = strconv.AppendInt(*s.buf, v.Int64(), 10) + case slog.KindUint64: + *s.buf = strconv.AppendUint(*s.buf, v.Uint64(), 10) + case slog.KindFloat64: + *s.buf = strconv.AppendFloat(*s.buf, v.Float64(), 'g', -1, 64) + case slog.KindBool: + *s.buf = strconv.AppendBool(*s.buf, v.Bool()) + case slog.KindDuration: + *s.buf = append(*s.buf, v.Duration().String()...) + case slog.KindGroup: + *s.buf = fmt.Append(*s.buf, v.Group()) + case slog.KindLogValuer: + *s.buf = fmt.Append(*s.buf, v.Any()) + default: + panic(fmt.Sprintf("bad kind: %s", v.Kind())) + } + return nil +} + +func appendRFC3339Millis(b []byte, t time.Time) []byte { + // Format according to time.RFC3339Nano since it is highly optimized, + // but truncate it to use millisecond resolution. + // Unfortunately, that format trims trailing 0s, so add 1/10 millisecond + // to guarantee that there are exactly 4 digits after the period. + const prefixLen = len("2006-01-02T15:04:05.000") + n := len(b) + t = t.Truncate(time.Millisecond).Add(time.Millisecond / 10) + b = t.AppendFormat(b, time.RFC3339Nano) + b = append(b[:n+prefixLen], b[n+prefixLen+1:]...) // drop the 4th digit + return b +} diff --git a/lib/utils/log/logrus_formatter.go b/lib/utils/log/logrus_formatter.go index 87b3bff3bdc24..a21d922adf809 100644 --- a/lib/utils/log/logrus_formatter.go +++ b/lib/utils/log/logrus_formatter.go @@ -145,7 +145,7 @@ func (tf *TextFormatter) Format(e *logrus.Entry) ([]byte, error) { // write timestamp first if enabled if tf.timestampEnabled { - writeTimeRFC3339(w.b, e.Time) + *w.b = appendRFC3339Millis(*w.b, e.Time.Round(0)) } for _, field := range tf.ExtraFields { diff --git a/lib/utils/log/slog.go b/lib/utils/log/slog.go new file mode 100644 index 0000000000000..b1b0678ec5487 --- /dev/null +++ b/lib/utils/log/slog.go @@ -0,0 +1,131 @@ +/* + * Teleport + * Copyright (C) 2023 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package log + +import ( + "context" + "fmt" + "log/slog" + "reflect" + "strings" + + oteltrace "go.opentelemetry.io/otel/trace" +) + +const ( + // TraceLevel is the logging level when set to Trace verbosity. + TraceLevel = slog.LevelDebug - 1 + + // TraceLevelText is the text representation of Trace verbosity. + TraceLevelText = "TRACE" +) + +// DiscardHandler is a [slog.Handler] that discards all messages. It +// is more efficient than a [slog.Handler] which outputs to [io.Discard] since +// it performs zero formatting. +// TODO(tross): Use slog.DiscardHandler once upgraded to Go 1.24. +type DiscardHandler struct{} + +func (dh DiscardHandler) Enabled(context.Context, slog.Level) bool { return false } +func (dh DiscardHandler) Handle(context.Context, slog.Record) error { return nil } +func (dh DiscardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return dh } +func (dh DiscardHandler) WithGroup(name string) slog.Handler { return dh } + +func addTracingContextToRecord(ctx context.Context, r *slog.Record) { + const ( + traceID = "trace_id" + spanID = "span_id" + ) + + span := oteltrace.SpanFromContext(ctx) + if span == nil { + return + } + + spanContext := span.SpanContext() + if spanContext.HasTraceID() { + r.AddAttrs(slog.String(traceID, spanContext.TraceID().String())) + } + + if spanContext.HasSpanID() { + r.AddAttrs(slog.String(spanID, spanContext.SpanID().String())) + } +} + +// getCaller retrieves source information from the attribute +// and returns the file and line of the caller. The file is +// truncated from the absolute path to package/filename. +func getCaller(s *slog.Source) (file string, line int) { + count := 0 + idx := strings.LastIndexFunc(s.File, func(r rune) bool { + if r == '/' { + count++ + } + + return count == 2 + }) + file = s.File[idx+1:] + line = s.Line + + return file, line +} + +type stringerAttr struct { + fmt.Stringer +} + +// StringerAttr creates a [slog.LogValuer] that will defer to +// the provided [fmt.Stringer]. All slog attributes are always evaluated, +// even if the log event is discarded due to the configured log level. +// A text [slog.Handler] will try to defer evaluation if the attribute is a +// [fmt.Stringer], however, the JSON [slog.Handler] only defers to [json.Marshaler]. +// This means that to defer evaluation and creation of the string representation, +// the object must implement [fmt.Stringer] and [json.Marshaler], otherwise additional +// and unwanted values may be emitted if the logger is configured to use JSON +// instead of text. This wrapping mechanism allows a value that implements [fmt.Stringer], +// to be guaranteed to be lazily constructed and always output the same +// content regardless of the output format. +func StringerAttr(s fmt.Stringer) slog.LogValuer { + return stringerAttr{Stringer: s} +} + +func (s stringerAttr) LogValue() slog.Value { + if s.Stringer == nil { + return slog.StringValue("") + } + return slog.StringValue(s.Stringer.String()) +} + +type typeAttr struct { + val any +} + +// TypeAttr creates a lazily evaluated log value that presents the pretty type name of a value +// as a string. It is roughly equivalent to the '%T' format option, and should only perform +// reflection in the event that logs are actually being generated. +func TypeAttr(val any) slog.LogValuer { + return typeAttr{val} +} + +func (a typeAttr) LogValue() slog.Value { + if t := reflect.TypeOf(a.val); t != nil { + return slog.StringValue(t.String()) + } + return slog.StringValue("nil") +} diff --git a/lib/utils/log/slog_handler.go b/lib/utils/log/slog_handler.go deleted file mode 100644 index b69b83d2a1adb..0000000000000 --- a/lib/utils/log/slog_handler.go +++ /dev/null @@ -1,681 +0,0 @@ -/* - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package log - -import ( - "context" - "fmt" - "io" - "log/slog" - "reflect" - "runtime" - "slices" - "strconv" - "strings" - "sync" - "time" - - "github.com/gravitational/trace" - "github.com/sirupsen/logrus" - oteltrace "go.opentelemetry.io/otel/trace" - - "github.com/gravitational/teleport" -) - -// TraceLevel is the logging level when set to Trace verbosity. -const TraceLevel = slog.LevelDebug - 1 - -// TraceLevelText is the text representation of Trace verbosity. -const TraceLevelText = "TRACE" - -// SlogTextHandler is a [slog.Handler] that outputs messages in a textual -// manner as configured by the Teleport configuration. -type SlogTextHandler struct { - cfg SlogTextHandlerConfig - // withCaller indicates whether the location the log was emitted from - // should be included in the output message. - withCaller bool - // withTimestamp indicates whether the times that the log was emitted at - // should be included in the output message. - withTimestamp bool - // component is the Teleport subcomponent that emitted the log. - component string - // preformatted data from previous calls to WithGroup and WithAttrs. - preformatted []byte - // groupPrefix is for the text handler only. - // It holds the prefix for groups that were already pre-formatted. - // A group will appear here when a call to WithGroup is followed by - // a call to WithAttrs. - groupPrefix buffer - // groups passed in via WithGroup and WithAttrs. - groups []string - // nOpenGroups the number of groups opened in preformatted. - nOpenGroups int - - // mu protects out - it needs to be a pointer so that all cloned - // SlogTextHandler returned from WithAttrs and WithGroup share the - // same mutex. Otherwise, output may be garbled since each clone - // will use its own copy of the mutex to protect out. See - // https://github.com/golang/go/issues/61321 for more details. - mu *sync.Mutex - out io.Writer -} - -// SlogTextHandlerConfig allow the SlogTextHandler functionality -// to be tweaked. -type SlogTextHandlerConfig struct { - // Level is the minimum record level that will be logged. - Level slog.Leveler - // EnableColors allows the level to be printed in color. - EnableColors bool - // Padding to use for various components. - Padding int - // ConfiguredFields are fields explicitly set by users to be included in - // the output message. If there are any entries configured, they will be honored. - // If empty, the default fields will be populated and included in the output. - ConfiguredFields []string - // ReplaceAttr is called to rewrite each non-group attribute before - // it is logged. - ReplaceAttr func(groups []string, a slog.Attr) slog.Attr -} - -// NewSlogTextHandler creates a SlogTextHandler that writes messages to w. -func NewSlogTextHandler(w io.Writer, cfg SlogTextHandlerConfig) *SlogTextHandler { - if cfg.Padding == 0 { - cfg.Padding = defaultComponentPadding - } - - handler := SlogTextHandler{ - cfg: cfg, - withCaller: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField), - withTimestamp: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField), - out: w, - mu: &sync.Mutex{}, - } - - if handler.cfg.ConfiguredFields == nil { - handler.cfg.ConfiguredFields = defaultFormatFields - } - - return &handler -} - -// Enabled returns whether the provided level will be included in output. -func (s *SlogTextHandler) Enabled(ctx context.Context, level slog.Level) bool { - minLevel := slog.LevelInfo - if s.cfg.Level != nil { - minLevel = s.cfg.Level.Level() - } - return level >= minLevel -} - -func (s *SlogTextHandler) appendAttr(buf []byte, a slog.Attr) []byte { - if rep := s.cfg.ReplaceAttr; rep != nil && a.Value.Kind() != slog.KindGroup { - var gs []string - if s.groups != nil { - gs = s.groups - } - // Resolve before calling ReplaceAttr, so the user doesn't have to. - a.Value = a.Value.Resolve() - a = rep(gs, a) - } - - // Resolve the Attr's value before doing anything else. - a.Value = a.Value.Resolve() - // Ignore empty Attrs. - if a.Equal(slog.Attr{}) { - return buf - } - - switch a.Value.Kind() { - case slog.KindString: - value := a.Value.String() - if a.Key == slog.TimeKey { - buf = fmt.Append(buf, value) - break - } - - if a.Key == teleport.ComponentFields { - switch fields := a.Value.Any().(type) { - case map[string]any: - for k, v := range fields { - buf = s.appendAttr(buf, slog.Any(k, v)) - } - case logrus.Fields: - for k, v := range fields { - buf = s.appendAttr(buf, slog.Any(k, v)) - } - } - } - - if needsQuoting(value) { - if a.Key == teleport.ComponentKey || a.Key == slog.LevelKey || a.Key == CallerField || a.Key == slog.MessageKey { - if len(buf) > 0 { - buf = fmt.Append(buf, " ") - } - } else { - if len(buf) > 0 { - buf = fmt.Append(buf, " ") - } - buf = fmt.Appendf(buf, "%s%s:", s.groupPrefix, a.Key) - } - buf = strconv.AppendQuote(buf, value) - break - } - - if a.Key == teleport.ComponentKey || a.Key == slog.LevelKey || a.Key == CallerField || a.Key == slog.MessageKey { - if len(buf) > 0 { - buf = fmt.Append(buf, " ") - } - buf = fmt.Appendf(buf, "%s", a.Value.String()) - break - } - - buf = fmt.Appendf(buf, " %s%s:%s", s.groupPrefix, a.Key, a.Value.String()) - case slog.KindGroup: - attrs := a.Value.Group() - // Ignore empty groups. - if len(attrs) == 0 { - return buf - } - // If the key is non-empty, write it out and indent the rest of the attrs. - // Otherwise, inline the attrs. - if a.Key != "" { - s.groupPrefix = fmt.Append(s.groupPrefix, a.Key) - s.groupPrefix = fmt.Append(s.groupPrefix, ".") - } - for _, ga := range attrs { - buf = s.appendAttr(buf, ga) - } - if a.Key != "" { - s.groupPrefix = s.groupPrefix[:len(s.groupPrefix)-len(a.Key)-1 /* for keyComponentSep */] - if s.groups != nil { - s.groups = (s.groups)[:len(s.groups)-1] - } - } - default: - switch err := a.Value.Any().(type) { - case trace.Error: - buf = fmt.Appendf(buf, " error:[%v]", err.DebugReport()) - case error: - buf = fmt.Appendf(buf, " error:[%v]", a.Value) - default: - buf = fmt.Appendf(buf, " %s:%s", a.Key, a.Value) - } - } - return buf -} - -// writeTimeRFC3339 writes the time in [time.RFC3339Nano] to the buffer. -// This takes half the time of [time.Time.AppendFormat]. Adapted from -// go/src/log/slog/handler.go. -func writeTimeRFC3339(buf *buffer, t time.Time) { - year, month, day := t.Date() - buf.WritePosIntWidth(year, 4) - buf.WriteByte('-') - buf.WritePosIntWidth(int(month), 2) - buf.WriteByte('-') - buf.WritePosIntWidth(day, 2) - buf.WriteByte('T') - hour, min, sec := t.Clock() - buf.WritePosIntWidth(hour, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(min, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(sec, 2) - _, offsetSeconds := t.Zone() - if offsetSeconds == 0 { - buf.WriteByte('Z') - } else { - offsetMinutes := offsetSeconds / 60 - if offsetMinutes < 0 { - buf.WriteByte('-') - offsetMinutes = -offsetMinutes - } else { - buf.WriteByte('+') - } - buf.WritePosIntWidth(offsetMinutes/60, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(offsetMinutes%60, 2) - } -} - -// Handle formats the provided record and writes the output to the -// destination. -func (s *SlogTextHandler) Handle(ctx context.Context, r slog.Record) error { - buf := newBuffer() - defer buf.Free() - - addTracingContextToRecord(ctx, &r) - - if s.withTimestamp && !r.Time.IsZero() { - if s.cfg.ReplaceAttr != nil { - *buf = s.appendAttr(*buf, slog.Time(slog.TimeKey, r.Time)) - } else { - writeTimeRFC3339(buf, r.Time) - } - } - - // Processing fields in this manner allows users to - // configure the level and component position in the output. - // This matches the behavior of the original logrus. All other - // fields location in the output message are static. - for _, field := range s.cfg.ConfiguredFields { - switch field { - case LevelField: - var color int - var level string - switch r.Level { - case TraceLevel: - level = "TRACE" - color = gray - case slog.LevelDebug: - level = "DEBUG" - color = gray - case slog.LevelInfo: - level = "INFO" - color = blue - case slog.LevelWarn: - level = "WARN" - color = yellow - case slog.LevelError: - level = "ERROR" - color = red - case slog.LevelError + 1: - level = "FATAL" - color = red - default: - color = blue - level = r.Level.String() - } - - if !s.cfg.EnableColors { - color = noColor - } - - level = padMax(level, defaultLevelPadding) - if color == noColor { - *buf = s.appendAttr(*buf, slog.String(slog.LevelKey, level)) - } else { - *buf = fmt.Appendf(*buf, " \u001B[%dm%s\u001B[0m", color, level) - } - case ComponentField: - // If a component is provided with the attributes, it should be used instead of - // the component set on the handler. Note that if there are multiple components - // specified in the arguments, the one with the lowest index is used and the others are ignored. - // In the example below, the resulting component in the message output would be "alpaca". - // - // logger := logger.With(teleport.ComponentKey, "fish") - // logger.InfoContext(ctx, "llama llama llama", teleport.ComponentKey, "alpaca", "foo", 123, teleport.ComponentKey, "shark") - component := s.component - r.Attrs(func(attr slog.Attr) bool { - if attr.Key == teleport.ComponentKey { - component = fmt.Sprintf("[%v]", attr.Value) - component = strings.ToUpper(padMax(component, s.cfg.Padding)) - if component[len(component)-1] != ' ' { - component = component[:len(component)-1] + "]" - } - - return false - } - - return true - }) - - *buf = s.appendAttr(*buf, slog.String(teleport.ComponentKey, component)) - default: - if _, ok := knownFormatFields[field]; !ok { - return trace.BadParameter("invalid log format key: %v", field) - } - } - } - - *buf = s.appendAttr(*buf, slog.String(slog.MessageKey, r.Message)) - - // Insert preformatted attributes just after built-in ones. - *buf = append(*buf, s.preformatted...) - if r.NumAttrs() > 0 { - if len(s.groups) > 0 { - for _, n := range s.groups[s.nOpenGroups:] { - s.groupPrefix = fmt.Append(s.groupPrefix, n) - s.groupPrefix = fmt.Append(s.groupPrefix, ".") - } - } - - r.Attrs(func(a slog.Attr) bool { - // Skip adding any component attrs since they are processed above. - if a.Key == teleport.ComponentKey { - return true - } - - *buf = s.appendAttr(*buf, a) - return true - }) - } - - if r.PC != 0 && s.withCaller { - fs := runtime.CallersFrames([]uintptr{r.PC}) - f, _ := fs.Next() - - src := &slog.Source{ - Function: f.Function, - File: f.File, - Line: f.Line, - } - - file, line := getCaller(src) - *buf = fmt.Appendf(*buf, " %s:%d", file, line) - } - - buf.WriteByte('\n') - - s.mu.Lock() - defer s.mu.Unlock() - _, err := s.out.Write(*buf) - return err -} - -// WithAttrs clones the current handler with the provided attributes -// added to any existing attributes. The values are preformatted here -// so that they do not need to be formatted per call to Handle. -func (s *SlogTextHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - if len(attrs) == 0 { - return s - } - s2 := *s - // Force an append to copy the underlying arrays. - s2.preformatted = slices.Clip(s.preformatted) - s2.groups = slices.Clip(s.groups) - - // Add all groups from WithGroup that haven't already been added to the prefix. - if len(s.groups) > 0 { - for _, n := range s.groups[s.nOpenGroups:] { - s2.groupPrefix = fmt.Append(s2.groupPrefix, n) - s2.groupPrefix = fmt.Append(s2.groupPrefix, ".") - } - } - - // Now all groups have been opened. - s2.nOpenGroups = len(s2.groups) - - component := s.component - - // Pre-format the attributes. - for _, a := range attrs { - switch a.Key { - case teleport.ComponentKey: - component = fmt.Sprintf("[%v]", a.Value.String()) - component = strings.ToUpper(padMax(component, s.cfg.Padding)) - if component[len(component)-1] != ' ' { - component = component[:len(component)-1] + "]" - } - case teleport.ComponentFields: - switch fields := a.Value.Any().(type) { - case map[string]any: - for k, v := range fields { - s2.appendAttr(s2.preformatted, slog.Any(k, v)) - } - case logrus.Fields: - for k, v := range fields { - s2.preformatted = s2.appendAttr(s2.preformatted, slog.Any(k, v)) - } - } - default: - s2.preformatted = s2.appendAttr(s2.preformatted, a) - } - } - - s2.component = component - // Remember how many opened groups are in preformattedAttrs, - // so we don't open them again when we handle a Record. - s2.nOpenGroups = len(s2.groups) - return &s2 -} - -// WithGroup opens a new group. -func (s *SlogTextHandler) WithGroup(name string) slog.Handler { - if name == "" { - return s - } - - s2 := *s - s2.groups = append(s2.groups, name) - return &s2 -} - -// SlogJSONHandlerConfig allow the SlogJSONHandler functionality -// to be tweaked. -type SlogJSONHandlerConfig struct { - // Level is the minimum record level that will be logged. - Level slog.Leveler - // ConfiguredFields are fields explicitly set by users to be included in - // the output message. If there are any entries configured, they will be honored. - // If empty, the default fields will be populated and included in the output. - ConfiguredFields []string - // ReplaceAttr is called to rewrite each non-group attribute before - // it is logged. - ReplaceAttr func(groups []string, a slog.Attr) slog.Attr -} - -// SlogJSONHandler is a [slog.Handler] that outputs messages in a json -// format per the config file. -type SlogJSONHandler struct { - *slog.JSONHandler -} - -// NewSlogJSONHandler creates a SlogJSONHandler that outputs to w. -func NewSlogJSONHandler(w io.Writer, cfg SlogJSONHandlerConfig) *SlogJSONHandler { - withCaller := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField) - withComponent := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, ComponentField) - withTimestamp := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField) - - return &SlogJSONHandler{ - JSONHandler: slog.NewJSONHandler(w, &slog.HandlerOptions{ - AddSource: true, - Level: cfg.Level, - ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { - switch a.Key { - case teleport.ComponentKey: - if !withComponent { - return slog.Attr{} - } - if a.Value.Kind() != slog.KindString { - return a - } - - a.Key = ComponentField - case slog.LevelKey: - // The slog.JSONHandler will inject "level" Attr. - // However, this lib's consumer might add an Attr using the same key ("level") and we end up with two records named "level". - // We must check its type before assuming this was injected by the slog.JSONHandler. - lvl, ok := a.Value.Any().(slog.Level) - if !ok { - return a - } - - var level string - switch lvl { - case TraceLevel: - level = "trace" - case slog.LevelDebug: - level = "debug" - case slog.LevelInfo: - level = "info" - case slog.LevelWarn: - level = "warning" - case slog.LevelError: - level = "error" - case slog.LevelError + 1: - level = "fatal" - default: - level = strings.ToLower(lvl.String()) - } - - a.Value = slog.StringValue(level) - case slog.TimeKey: - if !withTimestamp { - return slog.Attr{} - } - - // The slog.JSONHandler will inject "time" Attr. - // However, this lib's consumer might add an Attr using the same key ("time") and we end up with two records named "time". - // We must check its type before assuming this was injected by the slog.JSONHandler. - if a.Value.Kind() != slog.KindTime { - return a - } - - t := a.Value.Time() - if t.IsZero() { - return a - } - - a.Key = TimestampField - a.Value = slog.StringValue(t.Format(time.RFC3339)) - case slog.MessageKey: - // The slog.JSONHandler will inject "msg" Attr. - // However, this lib's consumer might add an Attr using the same key ("msg") and we end up with two records named "msg". - // We must check its type before assuming this was injected by the slog.JSONHandler. - if a.Value.Kind() != slog.KindString { - return a - } - a.Key = messageField - case slog.SourceKey: - if !withCaller { - return slog.Attr{} - } - - // The slog.JSONHandler will inject "source" Attr when AddSource is true. - // However, this lib's consumer might add an Attr using the same key ("source") and we end up with two records named "source". - // We must check its type before assuming this was injected by the slog.JSONHandler. - s, ok := a.Value.Any().(*slog.Source) - if !ok { - return a - } - - file, line := getCaller(s) - a = slog.String(CallerField, fmt.Sprintf("%s:%d", file, line)) - } - - // Convert [slog.KindAny] values that are backed by an [error] or [fmt.Stringer] - // to strings so that only the message is output instead of a json object. The kind is - // first checked to avoid allocating an interface for the values stored inline - // in [slog.Attr]. - if a.Value.Kind() == slog.KindAny { - if err, ok := a.Value.Any().(error); ok { - a.Value = slog.StringValue(err.Error()) - } - - if stringer, ok := a.Value.Any().(fmt.Stringer); ok { - a.Value = slog.StringValue(stringer.String()) - } - } - - return a - }, - }), - } -} - -const ( - traceID = "trace_id" - spanID = "span_id" -) - -func addTracingContextToRecord(ctx context.Context, r *slog.Record) { - span := oteltrace.SpanFromContext(ctx) - if span == nil { - return - } - - spanContext := span.SpanContext() - if spanContext.HasTraceID() { - r.AddAttrs(slog.String(traceID, spanContext.TraceID().String())) - } - - if spanContext.HasSpanID() { - r.AddAttrs(slog.String(spanID, spanContext.SpanID().String())) - } -} - -func (j *SlogJSONHandler) Handle(ctx context.Context, r slog.Record) error { - addTracingContextToRecord(ctx, &r) - return j.JSONHandler.Handle(ctx, r) -} - -// getCaller retrieves source information from the attribute -// and returns the file and line of the caller. The file is -// truncated from the absolute path to package/filename. -func getCaller(s *slog.Source) (file string, line int) { - count := 0 - idx := strings.LastIndexFunc(s.File, func(r rune) bool { - if r == '/' { - count++ - } - - return count == 2 - }) - file = s.File[idx+1:] - line = s.Line - - return file, line -} - -type stringerAttr struct { - fmt.Stringer -} - -// StringerAttr creates a [slog.LogValuer] that will defer to -// the provided [fmt.Stringer]. All slog attributes are always evaluated, -// even if the log event is discarded due to the configured log level. -// A text [slog.Handler] will try to defer evaluation if the attribute is a -// [fmt.Stringer], however, the JSON [slog.Handler] only defers to [json.Marshaler]. -// This means that to defer evaluation and creation of the string representation, -// the object must implement [fmt.Stringer] and [json.Marshaler], otherwise additional -// and unwanted values may be emitted if the logger is configured to use JSON -// instead of text. This wrapping mechanism allows a value that implements [fmt.Stringer], -// to be guaranteed to be lazily constructed and always output the same -// content regardless of the output format. -func StringerAttr(s fmt.Stringer) slog.LogValuer { - return stringerAttr{Stringer: s} -} - -func (s stringerAttr) LogValue() slog.Value { - if s.Stringer == nil { - return slog.StringValue("") - } - return slog.StringValue(s.Stringer.String()) -} - -type typeAttr struct { - val any -} - -// TypeAttr creates a lazily evaluated log value that presents the pretty type name of a value -// as a string. It is roughly equivalent to the '%T' format option, and should only perform -// reflection in the event that logs are actually being generated. -func TypeAttr(val any) slog.LogValuer { - return typeAttr{val} -} - -func (a typeAttr) LogValue() slog.Value { - if t := reflect.TypeOf(a.val); t != nil { - return slog.StringValue(t.String()) - } - return slog.StringValue("nil") -} diff --git a/lib/utils/log/slog_handler_test.go b/lib/utils/log/slog_handler_test.go index 20876b6c4df1c..a75d57fc66994 100644 --- a/lib/utils/log/slog_handler_test.go +++ b/lib/utils/log/slog_handler_test.go @@ -22,13 +22,11 @@ import ( "bytes" "context" "encoding/json" - "fmt" "log/slog" "regexp" "strings" "testing" "testing/slogtest" - "time" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/assert" @@ -41,16 +39,17 @@ import ( func TestSlogTextHandler(t *testing.T) { t.Parallel() clock := clockwork.NewFakeClock() - now := clock.Now().UTC().Format(time.RFC3339) + now := clock.Now().UTC() // Create a handler that doesn't report the caller and automatically sets // the time to whatever time the fake clock has in UTC time. Since the timestamp // is not important for this test overriding, it allows the regex to be simpler. var buf bytes.Buffer h := NewSlogTextHandler(&buf, SlogTextHandlerConfig{ + ConfiguredFields: []string{LevelField, ComponentField, TimestampField}, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { - a.Value = slog.StringValue(now) + a.Value = slog.TimeValue(now) } return a }, @@ -62,8 +61,7 @@ func TestSlogTextHandler(t *testing.T) { // Group 2: verbosity level of output // Group 3: message contents // Group 4: additional attributes - regex := fmt.Sprintf("^(?:(%s)?)\\s?([A-Z]{4})\\s+(\\w+)(?:\\s(.*))?$", now) - lineRegex := regexp.MustCompile(regex) + lineRegex := regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)?\s?([A-Z]{4})\s+(\w+)(?:\s(.*))?$`) results := func() []map[string]any { var ms []map[string]any @@ -75,7 +73,7 @@ func TestSlogTextHandler(t *testing.T) { var m map[string]any matches := lineRegex.FindSubmatch(line) if len(matches) == 0 { - assert.Failf(t, "log output did not match regular expression", "regex: %s output: %s", regex, string(line)) + assert.Failf(t, "log output did not match regular expression", "regex: %s output: %s", lineRegex.String(), string(line)) ms = append(ms, m) continue } diff --git a/lib/utils/log/slog_json_handler.go b/lib/utils/log/slog_json_handler.go new file mode 100644 index 0000000000000..f5c3ec4062b09 --- /dev/null +++ b/lib/utils/log/slog_json_handler.go @@ -0,0 +1,167 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package log + +import ( + "context" + "fmt" + "io" + "log/slog" + "slices" + "strings" + "time" + + "github.com/gravitational/teleport" +) + +// SlogJSONHandlerConfig allows the SlogJSONHandler functionality +// to be tweaked. +type SlogJSONHandlerConfig struct { + // Level is the minimum record level that will be logged. + Level slog.Leveler + // ConfiguredFields are fields explicitly set by users to be included in + // the output message. If there are any entries configured, they will be honored. + // If empty, the default fields will be populated and included in the output. + ConfiguredFields []string + // ReplaceAttr is called to rewrite each non-group attribute before + // it is logged. + ReplaceAttr func(groups []string, a slog.Attr) slog.Attr +} + +// SlogJSONHandler is a [slog.Handler] that outputs messages in a json +// format per the config file. +type SlogJSONHandler struct { + *slog.JSONHandler +} + +// NewSlogJSONHandler creates a SlogJSONHandler that outputs to w. +func NewSlogJSONHandler(w io.Writer, cfg SlogJSONHandlerConfig) *SlogJSONHandler { + withCaller := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField) + withComponent := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, ComponentField) + withTimestamp := len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField) + + return &SlogJSONHandler{ + JSONHandler: slog.NewJSONHandler(w, &slog.HandlerOptions{ + AddSource: true, + Level: cfg.Level, + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + switch a.Key { + case teleport.ComponentKey: + if !withComponent { + return slog.Attr{} + } + if a.Value.Kind() != slog.KindString { + return a + } + + a.Key = ComponentField + case slog.LevelKey: + // The slog.JSONHandler will inject "level" Attr. + // However, this lib's consumer might add an Attr using the same key ("level") and we end up with two records named "level". + // We must check its type before assuming this was injected by the slog.JSONHandler. + lvl, ok := a.Value.Any().(slog.Level) + if !ok { + return a + } + + var level string + switch lvl { + case TraceLevel: + level = "trace" + case slog.LevelDebug: + level = "debug" + case slog.LevelInfo: + level = "info" + case slog.LevelWarn: + level = "warning" + case slog.LevelError: + level = "error" + case slog.LevelError + 1: + level = "fatal" + default: + level = strings.ToLower(lvl.String()) + } + + a.Value = slog.StringValue(level) + case slog.TimeKey: + if !withTimestamp { + return slog.Attr{} + } + + // The slog.JSONHandler will inject "time" Attr. + // However, this lib's consumer might add an Attr using the same key ("time") and we end up with two records named "time". + // We must check its type before assuming this was injected by the slog.JSONHandler. + if a.Value.Kind() != slog.KindTime { + return a + } + + t := a.Value.Time() + if t.IsZero() { + return a + } + + a.Key = TimestampField + a.Value = slog.StringValue(t.Format(time.RFC3339)) + case slog.MessageKey: + // The slog.JSONHandler will inject "msg" Attr. + // However, this lib's consumer might add an Attr using the same key ("msg") and we end up with two records named "msg". + // We must check its type before assuming this was injected by the slog.JSONHandler. + if a.Value.Kind() != slog.KindString { + return a + } + a.Key = messageField + case slog.SourceKey: + if !withCaller { + return slog.Attr{} + } + + // The slog.JSONHandler will inject "source" Attr when AddSource is true. + // However, this lib's consumer might add an Attr using the same key ("source") and we end up with two records named "source". + // We must check its type before assuming this was injected by the slog.JSONHandler. + s, ok := a.Value.Any().(*slog.Source) + if !ok { + return a + } + + file, line := getCaller(s) + a = slog.String(CallerField, fmt.Sprintf("%s:%d", file, line)) + } + + // Convert [slog.KindAny] values that are backed by an [error] or [fmt.Stringer] + // to strings so that only the message is output instead of a json object. The kind is + // first checked to avoid allocating an interface for the values stored inline + // in [slog.Attr]. + if a.Value.Kind() == slog.KindAny { + if err, ok := a.Value.Any().(error); ok { + a.Value = slog.StringValue(err.Error()) + } + + if stringer, ok := a.Value.Any().(fmt.Stringer); ok { + a.Value = slog.StringValue(stringer.String()) + } + } + + return a + }, + }), + } +} + +func (j *SlogJSONHandler) Handle(ctx context.Context, r slog.Record) error { + addTracingContextToRecord(ctx, &r) + return j.JSONHandler.Handle(ctx, r) +} diff --git a/lib/utils/log/slog_text_handler.go b/lib/utils/log/slog_text_handler.go new file mode 100644 index 0000000000000..b3bc4900ac64c --- /dev/null +++ b/lib/utils/log/slog_text_handler.go @@ -0,0 +1,359 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package log + +import ( + "context" + "fmt" + "io" + "log/slog" + "runtime" + "slices" + "strings" + "sync" + + "github.com/gravitational/trace" + "github.com/sirupsen/logrus" + + "github.com/gravitational/teleport" +) + +// SlogTextHandler is a [slog.Handler] that outputs messages in a textual +// manner as configured by the Teleport configuration. +type SlogTextHandler struct { + cfg SlogTextHandlerConfig + // withCaller indicates whether the location the log was emitted from + // should be included in the output message. + withCaller bool + // withTimestamp indicates whether the times that the log was emitted at + // should be included in the output message. + withTimestamp bool + // component is the Teleport subcomponent that emitted the log. + component string + // preformatted data from previous calls to WithGroup and WithAttrs. + preformatted []byte + // groupPrefix is for the text handler only. + // It holds the prefix for groups that were already pre-formatted. + // A group will appear here when a call to WithGroup is followed by + // a call to WithAttrs. + groupPrefix string + // groups passed in via WithGroup and WithAttrs. + groups []string + // nOpenGroups the number of groups opened in preformatted. + nOpenGroups int + + // mu protects out - it needs to be a pointer so that all cloned + // SlogTextHandler returned from WithAttrs and WithGroup share the + // same mutex. Otherwise, output may be garbled since each clone + // will use its own copy of the mutex to protect out. See + // https://github.com/golang/go/issues/61321 for more details. + mu *sync.Mutex + out io.Writer +} + +// SlogTextHandlerConfig allow the SlogTextHandler functionality +// to be tweaked. +type SlogTextHandlerConfig struct { + // Level is the minimum record level that will be logged. + Level slog.Leveler + // EnableColors allows the level to be printed in color. + EnableColors bool + // Padding to use for various components. + Padding int + // ConfiguredFields are fields explicitly set by users to be included in + // the output message. If there are any entries configured, they will be honored. + // If empty, the default fields will be populated and included in the output. + ConfiguredFields []string + // ReplaceAttr is called to rewrite each non-group attribute before + // it is logged. + ReplaceAttr func(groups []string, a slog.Attr) slog.Attr +} + +// NewSlogTextHandler creates a SlogTextHandler that writes messages to w. +func NewSlogTextHandler(w io.Writer, cfg SlogTextHandlerConfig) *SlogTextHandler { + if cfg.Padding == 0 { + cfg.Padding = defaultComponentPadding + } + + handler := SlogTextHandler{ + cfg: cfg, + withCaller: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, CallerField), + withTimestamp: len(cfg.ConfiguredFields) == 0 || slices.Contains(cfg.ConfiguredFields, TimestampField), + out: w, + mu: &sync.Mutex{}, + } + + if handler.cfg.ConfiguredFields == nil { + handler.cfg.ConfiguredFields = defaultFormatFields + } + + return &handler +} + +// Enabled returns whether the provided level will be included in output. +func (s *SlogTextHandler) Enabled(ctx context.Context, level slog.Level) bool { + minLevel := slog.LevelInfo + if s.cfg.Level != nil { + minLevel = s.cfg.Level.Level() + } + return level >= minLevel +} + +func (s *SlogTextHandler) newHandleState(buf *buffer, freeBuf bool) handleState { + state := handleState{ + h: s, + buf: buf, + freeBuf: freeBuf, + prefix: newBuffer(), + } + if s.cfg.ReplaceAttr != nil { + state.groups = groupPool.Get().(*[]string) + *state.groups = append(*state.groups, s.groups[:s.nOpenGroups]...) + } + return state +} + +// Handle formats the provided record and writes the output to the +// destination. +func (s *SlogTextHandler) Handle(ctx context.Context, r slog.Record) error { + state := s.newHandleState(newBuffer(), true) + defer state.free() + + addTracingContextToRecord(ctx, &r) + + // Built-in attributes. They are not in a group. + stateGroups := state.groups + state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups. + rep := s.cfg.ReplaceAttr + + if s.withTimestamp && !r.Time.IsZero() { + if rep == nil { + state.appendKey(slog.TimeKey) + state.appendTime(r.Time.Round(0)) + } else { + state.appendAttr(slog.Time(slog.TimeKey, r.Time.Round(0))) + } + } + + // Processing fields in this manner allows users to + // configure the level and component position in the output. + // This matches the behavior of the original logrus. All other + // fields location in the output message are static. + for _, field := range s.cfg.ConfiguredFields { + switch field { + case LevelField: + var color int + var level string + switch r.Level { + case TraceLevel: + level = "TRACE" + color = gray + case slog.LevelDebug: + level = "DEBUG" + color = gray + case slog.LevelInfo: + level = "INFO" + color = blue + case slog.LevelWarn: + level = "WARN" + color = yellow + case slog.LevelError: + level = "ERROR" + color = red + case slog.LevelError + 1: + level = "FATAL" + color = red + default: + color = blue + level = r.Level.String() + } + + if !s.cfg.EnableColors { + color = noColor + } + + level = padMax(level, defaultLevelPadding) + if color != noColor { + level = fmt.Sprintf("\u001B[%dm%s\u001B[0m", color, level) + } + + if rep == nil { + state.appendKey(slog.LevelKey) + // Write the level directly to stat to avoid quoting + // color formatting that exists. + state.buf.WriteString(level) + } else { + state.appendAttr(slog.String(slog.LevelKey, level)) + } + case ComponentField: + // If a component is provided with the attributes, it should be used instead of + // the component set on the handler. Note that if there are multiple components + // specified in the arguments, the one with the lowest index is used and the others are ignored. + // In the example below, the resulting component in the message output would be "alpaca". + // + // logger := logger.With(teleport.ComponentKey, "fish") + // logger.InfoContext(ctx, "llama llama llama", teleport.ComponentKey, "alpaca", "foo", 123, teleport.ComponentKey, "shark") + component := s.component + r.Attrs(func(attr slog.Attr) bool { + if attr.Key != teleport.ComponentKey { + return true + } + component = fmt.Sprintf("[%v]", attr.Value) + component = strings.ToUpper(padMax(component, s.cfg.Padding)) + if component[len(component)-1] != ' ' { + component = component[:len(component)-1] + "]" + } + + return false + }) + + if rep == nil { + state.appendKey(teleport.ComponentKey) + state.appendString(component) + } else { + state.appendAttr(slog.String(teleport.ComponentKey, component)) + } + default: + if _, ok := knownFormatFields[field]; !ok { + return trace.BadParameter("invalid log format key: %v", field) + } + } + } + + if rep == nil { + state.appendKey(slog.MessageKey) + state.appendString(r.Message) + } else { + state.appendAttr(slog.String(slog.MessageKey, r.Message)) + } + + state.groups = stateGroups // Restore groups passed to ReplaceAttrs. + state.appendNonBuiltIns(r) + + if r.PC != 0 && s.withCaller { + fs := runtime.CallersFrames([]uintptr{r.PC}) + f, _ := fs.Next() + + src := slog.Source{ + Function: f.Function, + File: f.File, + Line: f.Line, + } + src.File, src.Line = getCaller(&src) + + if rep == nil { + state.appendKey(slog.SourceKey) + state.appendString(fmt.Sprintf("%s:%d", src.File, src.Line)) + } else { + state.appendAttr(slog.Any(slog.SourceKey, &src)) + } + + } + + state.buf.WriteByte('\n') + + s.mu.Lock() + defer s.mu.Unlock() + _, err := s.out.Write(*state.buf) + return err +} + +func (s *SlogTextHandler) clone() *SlogTextHandler { + // We can't use assignment because we can't copy the mutex. + return &SlogTextHandler{ + cfg: s.cfg, + withCaller: s.withCaller, + withTimestamp: s.withTimestamp, + component: s.component, + preformatted: slices.Clip(s.preformatted), + groupPrefix: s.groupPrefix, + groups: slices.Clip(s.groups), + nOpenGroups: s.nOpenGroups, + out: s.out, + mu: s.mu, // mutex shared among all clones of this handler + } +} + +// WithAttrs clones the current handler with the provided attributes +// added to any existing attributes. The values are preformatted here +// so that they do not need to be formatted per call to Handle. +func (s *SlogTextHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + if len(attrs) == 0 { + return s + } + + s2 := s.clone() + // Pre-format the attributes as an optimization. + state := s2.newHandleState((*buffer)(&s2.preformatted), false) + defer state.free() + state.prefix.WriteString(s.groupPrefix) + + // Remember the position in the buffer, in case all attrs are empty. + pos := state.buf.Len() + state.openGroups() + + nonEmpty := false + for _, a := range attrs { + switch a.Key { + case teleport.ComponentKey: + component := fmt.Sprintf("[%v]", a.Value.String()) + component = strings.ToUpper(padMax(component, s.cfg.Padding)) + if component[len(component)-1] != ' ' { + component = component[:len(component)-1] + "]" + } + s2.component = component + case teleport.ComponentFields: + switch fields := a.Value.Any().(type) { + case map[string]any: + for k, v := range fields { + if state.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + case logrus.Fields: + for k, v := range fields { + if state.appendAttr(slog.Any(k, v)) { + nonEmpty = true + } + } + } + default: + if state.appendAttr(a) { + nonEmpty = true + } + } + } + + if !nonEmpty { + state.buf.SetLen(pos) + } else { + // Remember the new prefix for later keys. + s2.groupPrefix = state.prefix.String() + // Remember how many opened groups are in preformattedAttrs, + // so we don't open them again when we handle a Record. + s2.nOpenGroups = len(s2.groups) + } + + return s2 +} + +// WithGroup opens a new group. +func (s *SlogTextHandler) WithGroup(name string) slog.Handler { + s2 := s.clone() + s2.groups = append(s2.groups, name) + return s2 +} diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index e293a1dfa6535..9a080398ba71d 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -850,6 +850,7 @@ func (h *Handler) bindDefaultEndpoints() { h.GET("/webapi/sites/:site/events/search", h.WithClusterAuth(h.clusterSearchEvents)) // search site events h.GET("/webapi/sites/:site/events/search/sessions", h.WithClusterAuth(h.clusterSearchSessionEvents)) // search site session events h.GET("/webapi/sites/:site/ttyplayback/:sid", h.WithClusterAuth(h.ttyPlaybackHandle)) + h.GET("/webapi/sites/:site/sessionlength/:sid", h.WithClusterAuth(h.sessionLengthHandle)) // scp file transfer h.GET("/webapi/sites/:site/nodes/:server/:login/scp", h.WithClusterAuth(h.transferFile)) diff --git a/lib/web/tty_playback.go b/lib/web/tty_playback.go index f601f4237666c..76c456a4a7010 100644 --- a/lib/web/tty_playback.go +++ b/lib/web/tty_playback.go @@ -53,6 +53,47 @@ const ( actionPause = byte(1) ) +func (h *Handler) sessionLengthHandle( + w http.ResponseWriter, + r *http.Request, + p httprouter.Params, + sctx *SessionContext, + site reversetunnelclient.RemoteSite, +) (interface{}, error) { + sID := p.ByName("sid") + if sID == "" { + return nil, trace.BadParameter("missing session ID in request URL") + } + + ctx, cancel := context.WithCancel(r.Context()) + defer cancel() + + clt, err := sctx.GetUserClient(ctx, site) + if err != nil { + return nil, trace.Wrap(err) + } + + evts, errs := clt.StreamSessionEvents(ctx, session.ID(sID), 0) + for { + select { + case err := <-errs: + return nil, trace.Wrap(err) + case evt, ok := <-evts: + if !ok { + return nil, trace.NotFound("could not find end event for session %v", sID) + } + switch evt := evt.(type) { + case *events.SessionEnd: + return map[string]any{"durationMs": evt.EndTime.Sub(evt.StartTime).Milliseconds()}, nil + case *events.WindowsDesktopSessionEnd: + return map[string]any{"durationMs": evt.EndTime.Sub(evt.StartTime).Milliseconds()}, nil + case *events.DatabaseSessionEnd: + return map[string]any{"durationMs": evt.EndTime.Sub(evt.StartTime).Milliseconds()}, nil + } + } + } +} + func (h *Handler) ttyPlaybackHandle( w http.ResponseWriter, r *http.Request, diff --git a/tool/tctl/common/token_command.go b/tool/tctl/common/token_command.go index c11a64ce070cc..29f192512e09a 100644 --- a/tool/tctl/common/token_command.go +++ b/tool/tctl/common/token_command.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "os" + "slices" "sort" "strings" "text/template" @@ -139,6 +140,7 @@ func (c *TokensCommand) Initialize(app *kingpin.Application, config *servicecfg. c.tokenList = tokens.Command("ls", "List node and user invitation tokens.") c.tokenList.Flag("format", "Output format, 'text', 'json' or 'yaml'").EnumVar(&c.format, formats...) c.tokenList.Flag("with-secrets", "Do not redact join tokens").BoolVar(&c.withSecrets) + c.tokenList.Flag("labels", labelHelp).StringVar(&c.labels) if c.stdout == nil { c.stdout = os.Stdout @@ -375,10 +377,26 @@ func (c *TokensCommand) Del(ctx context.Context, client *authclient.Client) erro // List is called to execute "tokens ls" command. func (c *TokensCommand) List(ctx context.Context, client *authclient.Client) error { + labels, err := libclient.ParseLabelSpec(c.labels) + if err != nil { + return trace.Wrap(err) + } + tokens, err := client.GetTokens(ctx) if err != nil { return trace.Wrap(err) } + + tokens = slices.DeleteFunc(tokens, func(token types.ProvisionToken) bool { + tokenLabels := token.GetMetadata().Labels + for k, v := range labels { + if tokenLabels[k] != v { + return true + } + } + return false + }) + if len(tokens) == 0 && c.format == teleport.Text { fmt.Fprintln(c.stdout, "No active tokens found.") return nil diff --git a/tool/tctl/common/token_command_test.go b/tool/tctl/common/token_command_test.go index aef8e3556b21e..afc84f963d4a4 100644 --- a/tool/tctl/common/token_command_test.go +++ b/tool/tctl/common/token_command_test.go @@ -82,58 +82,60 @@ func TestTokens(t *testing.T) { clt := testenv.MakeDefaultAuthClient(t, process) // Test all output formats of "tokens add". - t.Run("add", func(t *testing.T) { - buf, err := runTokensCommand(t, clt, []string{"add", "--type=node"}) - require.NoError(t, err) - require.True(t, strings.HasPrefix(buf.String(), "The invite token:")) + buf, err := runTokensCommand(t, clt, []string{"add", "--type=node"}) + require.NoError(t, err) + require.True(t, strings.HasPrefix(buf.String(), "The invite token:")) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.Text}) - require.NoError(t, err) - require.Equal(t, 1, strings.Count(buf.String(), "\n")) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.Text}) + require.NoError(t, err) + require.Equal(t, 1, strings.Count(buf.String(), "\n")) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.JSON}) - require.NoError(t, err) - out := mustDecodeJSON[addedToken](t, buf) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.JSON}) + require.NoError(t, err) + out := mustDecodeJSON[addedToken](t, buf) - require.Len(t, out.Roles, 2) - require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) - require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) + require.Len(t, out.Roles, 2) + require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) + require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.YAML}) - require.NoError(t, err) - out = mustDecodeYAML[addedToken](t, buf) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=node,app", "--format", teleport.YAML}) + require.NoError(t, err) + out = mustDecodeYAML[addedToken](t, buf) - require.Len(t, out.Roles, 2) - require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) - require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) + require.Len(t, out.Roles, 2) + require.Equal(t, types.KindNode, strings.ToLower(out.Roles[0])) + require.Equal(t, types.KindApp, strings.ToLower(out.Roles[1])) - buf, err = runTokensCommand(t, clt, []string{"add", "--type=kube"}) - require.NoError(t, err) - require.Contains(t, buf.String(), `--set roles="kube\,app\,discovery"`, - "Command print out should include setting kube, app and discovery roles for helm install.") - }) + buf, err = runTokensCommand(t, clt, []string{"add", "--type=kube", "--labels=foo=bar"}) + require.NoError(t, err) + require.Contains(t, buf.String(), `--set roles="kube\,app\,discovery"`, + "Command print out should include setting kube, app and discovery roles for helm install.") // Test all output formats of "tokens ls". - t.Run("ls", func(t *testing.T) { - buf, err := runTokensCommand(t, clt, []string{"ls"}) - require.NoError(t, err) - require.True(t, strings.HasPrefix(buf.String(), "Token ")) - require.Equal(t, 7, strings.Count(buf.String(), "\n")) // account for header lines - - buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.Text}) - require.NoError(t, err) - require.Equal(t, 5, strings.Count(buf.String(), "\n")) - - buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.JSON}) - require.NoError(t, err) - jsonOut := mustDecodeJSON[[]listedToken](t, buf) - require.Len(t, jsonOut, 5) - - buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.YAML}) - require.NoError(t, err) - yamlOut := []listedToken{} - mustDecodeYAMLDocuments(t, buf, &yamlOut) - require.Len(t, yamlOut, 5) - require.Equal(t, jsonOut, yamlOut) - }) + buf, err = runTokensCommand(t, clt, []string{"ls"}) + require.NoError(t, err) + require.True(t, strings.HasPrefix(buf.String(), "Token ")) + require.Equal(t, 7, strings.Count(buf.String(), "\n")) // account for header lines + + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.Text}) + require.NoError(t, err) + require.Equal(t, 5, strings.Count(buf.String(), "\n")) + + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.JSON}) + require.NoError(t, err) + jsonOut := mustDecodeJSON[[]listedToken](t, buf) + require.Len(t, jsonOut, 5) + + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.YAML}) + require.NoError(t, err) + yamlOut := []listedToken{} + mustDecodeYAMLDocuments(t, buf, &yamlOut) + require.Len(t, yamlOut, 5) + require.Equal(t, jsonOut, yamlOut) + + // Test filtering by label + buf, err = runTokensCommand(t, clt, []string{"ls", "--format", teleport.JSON, "--labels=foo=bar"}) + require.NoError(t, err) + jsonOut = mustDecodeJSON[[]listedToken](t, buf) + require.Len(t, jsonOut, 1) } diff --git a/web/packages/design/src/Icon/Icons.story.tsx b/web/packages/design/src/Icon/Icons.story.tsx index 47807a6715ae9..b405bec830973 100644 --- a/web/packages/design/src/Icon/Icons.story.tsx +++ b/web/packages/design/src/Icon/Icons.story.tsx @@ -163,6 +163,7 @@ export const Icons = () => ( + @@ -186,6 +187,7 @@ export const Icons = () => ( + diff --git a/web/packages/design/src/Icon/Icons/MinusCircle.tsx b/web/packages/design/src/Icon/Icons/MinusCircle.tsx new file mode 100644 index 0000000000000..fe3a181ffd912 --- /dev/null +++ b/web/packages/design/src/Icon/Icons/MinusCircle.tsx @@ -0,0 +1,67 @@ +/** + * Teleport + * Copyright (C) 2023 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/* MIT License + +Copyright (c) 2020 Phosphor Icons + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +import { Icon, IconProps } from '../Icon'; + +/* + +THIS FILE IS GENERATED. DO NOT EDIT. + +*/ + +export function MinusCircle({ size = 24, color, ...otherProps }: IconProps) { + return ( + + + + + ); +} diff --git a/web/packages/design/src/Icon/Icons/RocketLaunch.tsx b/web/packages/design/src/Icon/Icons/RocketLaunch.tsx new file mode 100644 index 0000000000000..500dc5d066453 --- /dev/null +++ b/web/packages/design/src/Icon/Icons/RocketLaunch.tsx @@ -0,0 +1,67 @@ +/** + * Teleport + * Copyright (C) 2023 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/* MIT License + +Copyright (c) 2020 Phosphor Icons + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +import { Icon, IconProps } from '../Icon'; + +/* + +THIS FILE IS GENERATED. DO NOT EDIT. + +*/ + +export function RocketLaunch({ size = 24, color, ...otherProps }: IconProps) { + return ( + + + + + ); +} diff --git a/web/packages/design/src/Icon/assets/MinusCircle.svg b/web/packages/design/src/Icon/assets/MinusCircle.svg new file mode 100644 index 0000000000000..1058d09b4e10d --- /dev/null +++ b/web/packages/design/src/Icon/assets/MinusCircle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/web/packages/design/src/Icon/assets/RocketLaunch.svg b/web/packages/design/src/Icon/assets/RocketLaunch.svg new file mode 100644 index 0000000000000..e25ea62128692 --- /dev/null +++ b/web/packages/design/src/Icon/assets/RocketLaunch.svg @@ -0,0 +1,4 @@ + + + + diff --git a/web/packages/design/src/Icon/index.ts b/web/packages/design/src/Icon/index.ts index be8d7320be71b..79a733b145bb2 100644 --- a/web/packages/design/src/Icon/index.ts +++ b/web/packages/design/src/Icon/index.ts @@ -149,6 +149,7 @@ export { MagnifyingMinus } from './Icons/MagnifyingMinus'; export { MagnifyingPlus } from './Icons/MagnifyingPlus'; export { Memory } from './Icons/Memory'; export { Minus } from './Icons/Minus'; +export { MinusCircle } from './Icons/MinusCircle'; export { Moon } from './Icons/Moon'; export { MoreHoriz } from './Icons/MoreHoriz'; export { MoreVert } from './Icons/MoreVert'; @@ -172,6 +173,7 @@ export { PushPinFilled } from './Icons/PushPinFilled'; export { Question } from './Icons/Question'; export { Refresh } from './Icons/Refresh'; export { Restore } from './Icons/Restore'; +export { RocketLaunch } from './Icons/RocketLaunch'; export { Rows } from './Icons/Rows'; export { Ruler } from './Icons/Ruler'; export { Run } from './Icons/Run'; diff --git a/web/packages/design/src/Icon/script/IconTemplate.txt b/web/packages/design/src/Icon/script/IconTemplate.txt index 7ba34ed3fa097..e204eb99dfa0e 100644 --- a/web/packages/design/src/Icon/script/IconTemplate.txt +++ b/web/packages/design/src/Icon/script/IconTemplate.txt @@ -40,8 +40,6 @@ SOFTWARE. */ -import React from 'react'; - import { Icon, IconProps } from '../Icon'; /* diff --git a/web/packages/design/src/Mark/Mark.story.tsx b/web/packages/design/src/Mark/Mark.story.tsx index fdc202a8f2044..0b532cd03d2ab 100644 --- a/web/packages/design/src/Mark/Mark.story.tsx +++ b/web/packages/design/src/Mark/Mark.story.tsx @@ -17,8 +17,9 @@ */ import Box from 'design/Box'; +import { IconTooltip } from 'design/Tooltip'; -import { Mark as M } from './Mark'; +import { Mark as M, MarkInverse } from './Mark'; export default { title: 'Design/Mark', @@ -41,3 +42,12 @@ export const SampleText = () => { ); }; + +export const MarkInsideTooltip = () => { + return ( + + Example of MarkInverse component. Note the{' '} + inversed background and text color. + + ); +}; diff --git a/web/packages/design/src/Mark/Mark.tsx b/web/packages/design/src/Mark/Mark.tsx index 2c76a92e0a25c..95552b8e9882b 100644 --- a/web/packages/design/src/Mark/Mark.tsx +++ b/web/packages/design/src/Mark/Mark.tsx @@ -26,3 +26,16 @@ export const Mark = styled.mark` background-color: ${p => p.theme.colors.interactive.tonal.neutral[2]}; color: inherit; `; + +/** + * Returns a MarkInverse that inverts the colors from its parent Mark. + * For example, if current theme is dark theme, parent Mark would use + * light colors, but MarkInverse will use dark colors. + * + * Intended for use in tooltips since tooltips uses inverse background + * color of the current theme. + */ +export const MarkInverse = styled(Mark)` + background-color: ${p => p.theme.colors.tooltip.inverseBackground}; + color: ${p => p.theme.colors.text.main}; +`; diff --git a/web/packages/design/src/Mark/index.ts b/web/packages/design/src/Mark/index.ts index f12d3f30412e7..8ff108e93731d 100644 --- a/web/packages/design/src/Mark/index.ts +++ b/web/packages/design/src/Mark/index.ts @@ -16,4 +16,4 @@ * along with this program. If not, see . */ -export { Mark } from './Mark'; +export { Mark, MarkInverse } from './Mark'; diff --git a/web/packages/design/src/ResourceIcon/assets/argocd.svg b/web/packages/design/src/ResourceIcon/assets/argocd.svg new file mode 100644 index 0000000000000..b419034be124e --- /dev/null +++ b/web/packages/design/src/ResourceIcon/assets/argocd.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/packages/design/src/ResourceIcon/assets/okta-alt.svg b/web/packages/design/src/ResourceIcon/assets/okta-alt.svg new file mode 100644 index 0000000000000..c3c6e69a08c80 --- /dev/null +++ b/web/packages/design/src/ResourceIcon/assets/okta-alt.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/packages/design/src/ResourceIcon/icons.ts b/web/packages/design/src/ResourceIcon/icons.ts index 69656120a82b8..88c24d2a33ecb 100644 --- a/web/packages/design/src/ResourceIcon/icons.ts +++ b/web/packages/design/src/ResourceIcon/icons.ts @@ -35,6 +35,7 @@ import apolloIoLight from './assets/apollo.io-light.svg'; import appleDark from './assets/apple-dark.svg'; import appleLight from './assets/apple-light.svg'; import application from './assets/application.svg'; +import argocd from './assets/argocd.svg'; import asana from './assets/asana.svg'; import assembleDark from './assets/assemble-dark.svg'; import assembleLight from './assets/assemble-light.svg'; @@ -210,6 +211,7 @@ import notion from './assets/notion.svg'; import oasisopen from './assets/oasisopen.svg'; import oktaDark from './assets/okta-dark.svg'; import oktaLight from './assets/okta-light.svg'; +import oktaAlt from './assets/okta-alt.svg'; import onehundredonedomain from './assets/onehundredonedomain.svg'; import oneloginDark from './assets/onelogin-dark.svg'; import oneloginLight from './assets/onelogin-light.svg'; @@ -322,6 +324,7 @@ export { appleDark, appleLight, application, + argocd, asana, assembleDark, assembleLight, @@ -497,6 +500,7 @@ export { oasisopen, oktaDark, oktaLight, + oktaAlt, onehundredonedomain, oneloginDark, oneloginLight, diff --git a/web/packages/design/src/ResourceIcon/resourceIconSpecs.ts b/web/packages/design/src/ResourceIcon/resourceIconSpecs.ts index 17ffe26bbdaeb..ab45fd7d8d4b9 100644 --- a/web/packages/design/src/ResourceIcon/resourceIconSpecs.ts +++ b/web/packages/design/src/ResourceIcon/resourceIconSpecs.ts @@ -50,6 +50,7 @@ export const resourceIconSpecs = { 'apollo.io': { dark: i.apolloIoDark, light: i.apolloIoLight }, apple: { dark: i.appleDark, light: i.appleLight }, application: forAllThemes(i.application), + argocd: forAllThemes(i.argocd), asana: forAllThemes(i.asana), assemble: { dark: i.assembleDark, light: i.assembleLight }, atlassian: forAllThemes(i.atlassian), @@ -195,6 +196,7 @@ export const resourceIconSpecs = { oasisopen: forAllThemes(i.oasisopen), okta: { dark: i.oktaDark, light: i.oktaLight }, + oktaAlt: forAllThemes(i.oktaAlt), '101domain': forAllThemes(i.onehundredonedomain), onelogin: { dark: i.oneloginDark, light: i.oneloginLight }, '1password': { dark: i.onepasswordDark, light: i.onepasswordLight }, diff --git a/web/packages/design/src/theme/themes/bblpTheme.ts b/web/packages/design/src/theme/themes/bblpTheme.ts index 94c03f70948b8..bab7835d0c80d 100644 --- a/web/packages/design/src/theme/themes/bblpTheme.ts +++ b/web/packages/design/src/theme/themes/bblpTheme.ts @@ -204,6 +204,7 @@ const colors: ThemeColors = { tooltip: { background: 'rgba(255, 255, 255, 0.8)', + inverseBackground: 'rgba(0, 0, 0, 0.5)', }, progressBarColor: '#00BFA5', diff --git a/web/packages/design/src/theme/themes/darkTheme.ts b/web/packages/design/src/theme/themes/darkTheme.ts index 9ba4d7b2a98c4..1a7ce32ece88c 100644 --- a/web/packages/design/src/theme/themes/darkTheme.ts +++ b/web/packages/design/src/theme/themes/darkTheme.ts @@ -203,6 +203,7 @@ const colors: ThemeColors = { tooltip: { background: 'rgba(255, 255, 255, 0.8)', + inverseBackground: 'rgba(0, 0, 0, 0.5)', }, progressBarColor: '#00BFA5', diff --git a/web/packages/design/src/theme/themes/lightTheme.ts b/web/packages/design/src/theme/themes/lightTheme.ts index 8c2cc6daf7677..c19ebb8e991cc 100644 --- a/web/packages/design/src/theme/themes/lightTheme.ts +++ b/web/packages/design/src/theme/themes/lightTheme.ts @@ -202,6 +202,7 @@ const colors: ThemeColors = { tooltip: { background: 'rgba(0, 0, 0, 0.80)', + inverseBackground: 'rgba(255, 255, 255, 0.5)', }, progressBarColor: '#007D6B', diff --git a/web/packages/design/src/theme/themes/types.ts b/web/packages/design/src/theme/themes/types.ts index 64a45651a7035..d0519eb98d3dc 100644 --- a/web/packages/design/src/theme/themes/types.ts +++ b/web/packages/design/src/theme/themes/types.ts @@ -141,6 +141,7 @@ export type ThemeColors = { tooltip: { background: string; + inverseBackground: string; }; progressBarColor: string; diff --git a/web/packages/shared/components/ButtonTextWithAddIcon/ButtonTextWithAddIcon.tsx b/web/packages/shared/components/ButtonTextWithAddIcon/ButtonTextWithAddIcon.tsx index 3d65b59944f06..e33a9849b37b4 100644 --- a/web/packages/shared/components/ButtonTextWithAddIcon/ButtonTextWithAddIcon.tsx +++ b/web/packages/shared/components/ButtonTextWithAddIcon/ButtonTextWithAddIcon.tsx @@ -33,7 +33,7 @@ export const ButtonTextWithAddIcon = ({ iconSize?: IconSize; }) => { return ( - + . + */ + +import React, { useState } from 'react'; +import { Flex } from 'design'; + +import { MultiselectMenu } from './MultiselectMenu'; + +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; + +type OptionValue = `option-${number}`; + +const options: { + value: OptionValue; + label: string | React.ReactNode; + disabled?: boolean; + disabledTooltip?: string; +}[] = [ + { value: 'option-1', label: 'Option 1' }, + { value: 'option-2', label: 'Option 2' }, + { value: 'option-3', label: 'Option 3' }, + { value: 'option-4', label: 'Option 4' }, +]; + +const optionsWithCustomLabels: typeof options = [ + { + value: 'option-1', + label: Bold Option 1, + }, + { + value: 'option-3', + label: Italic Option 3, + }, +]; + +export default { + title: 'Shared/Controls/MultiselectMenu', + component: MultiselectMenu, + argTypes: { + buffered: { + control: { type: 'boolean' }, + description: 'Buffer selections until "Apply" is clicked', + table: { defaultValue: { summary: 'false' } }, + }, + showIndicator: { + control: { type: 'boolean' }, + description: 'Show indicator when there are selected options', + table: { defaultValue: { summary: 'true' } }, + }, + showSelectControls: { + control: { type: 'boolean' }, + description: 'Show select controls (Select All/Select None)', + table: { defaultValue: { summary: 'true' } }, + }, + label: { + control: { type: 'text' }, + description: 'Label for the multiselect', + }, + tooltip: { + control: { type: 'text' }, + description: 'Tooltip for the label', + }, + selected: { + control: false, + description: 'Currently selected options', + table: { type: { summary: 'T[]' } }, + }, + onChange: { + control: false, + description: 'Callback when selection changes', + table: { type: { summary: 'selected: T[]' } }, + }, + options: { + control: false, + description: 'Options to select from', + table: { + type: { + summary: + 'Array<{ value: T; label: string | ReactNode; disabled?: boolean; disabledTooltip?: string; }>', + }, + }, + }, + }, + args: { + label: 'Select Options', + tooltip: 'Choose multiple options', + buffered: false, + showIndicator: true, + showSelectControls: true, + }, + parameters: { controls: { expanded: true, exclude: ['userContext'] } }, + render: (args => { + const [selected, setSelected] = useState([]); + return ( + + + + ); + }) satisfies StoryFn>, +} satisfies Meta>; + +type Story = StoryObj>; + +const Default: Story = { args: { options } }; + +const WithCustomLabels: Story = { args: { options: optionsWithCustomLabels } }; + +const WithDisabledOption: Story = { + args: { + options: [ + ...options, + { + value: 'option-5', + label: 'Option 5', + disabled: true, + disabledTooltip: 'Lorum ipsum dolor sit amet', + }, + ], + }, +}; + +export { Default, WithCustomLabels, WithDisabledOption }; diff --git a/web/packages/shared/components/Controls/MultiselectMenu.tsx b/web/packages/shared/components/Controls/MultiselectMenu.tsx new file mode 100644 index 0000000000000..f252cf7aa21be --- /dev/null +++ b/web/packages/shared/components/Controls/MultiselectMenu.tsx @@ -0,0 +1,243 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, { ReactNode, useState } from 'react'; +import styled from 'styled-components'; +import { + ButtonPrimary, + ButtonSecondary, + Flex, + Menu, + MenuItem, + Text, +} from 'design'; +import { ChevronDown } from 'design/Icon'; +import { CheckboxInput } from 'design/Checkbox'; + +import { HoverTooltip } from 'shared/components/ToolTip'; + +type MultiselectMenuProps = { + options: { + value: T; + label: string | ReactNode; + disabled?: boolean; + disabledTooltip?: string; + }[]; + selected: T[]; + onChange: (selected: T[]) => void; + label: string | ReactNode; + tooltip: string; + buffered?: boolean; + showIndicator?: boolean; + showSelectControls?: boolean; +}; + +export const MultiselectMenu = ({ + onChange, + options, + selected, + label, + tooltip, + buffered = false, + showIndicator = true, + showSelectControls = true, +}: MultiselectMenuProps) => { + // we have a separate state in the filter so we can select a few different things and then click "apply" + const [intSelected, setIntSelected] = useState([]); + const [anchorEl, setAnchorEl] = useState(null); + const handleOpen = ( + event: React.MouseEvent + ) => { + setIntSelected(selected || []); + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + // if we cancel, we reset the options to what is already selected in the params + const cancelUpdate = () => { + setIntSelected(selected || []); + handleClose(); + }; + + const handleSelect = (value: T) => { + let newSelected = (buffered ? intSelected : selected).slice(); + + if (newSelected.includes(value)) { + newSelected = newSelected.filter(v => v !== value); + } else { + newSelected.push(value); + } + + (buffered ? setIntSelected : onChange)(newSelected); + }; + + const handleSelectAll = () => { + (buffered ? setIntSelected : onChange)( + options.filter(o => !o.disabled).map(o => o.value) + ); + }; + + const handleClearAll = () => { + (buffered ? setIntSelected : onChange)([]); + }; + + const applyFilters = () => { + onChange(intSelected); + handleClose(); + }; + + return ( + + + + {label} {selected?.length > 0 ? `(${selected?.length})` : ''} + + {selected?.length > 0 && showIndicator && } + + + `margin-top: 36px;`} + menuListCss={() => `overflow-y: auto;`} + transformOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + anchorEl={anchorEl} + open={Boolean(anchorEl)} + onClose={cancelUpdate} + > + {showSelectControls && ( + + + Select All + + + Clear All + + + )} + {options.map(opt => { + const $checkbox = ( + <> + { + handleSelect(opt.value); + }} + id={opt.value} + checked={(buffered ? intSelected : selected)?.includes( + opt.value + )} + /> + + {opt.label} + + + ); + return ( + (!opt.disabled ? handleSelect(opt.value) : null)} + > + {opt.disabled && opt.disabledTooltip ? ( + + {$checkbox} + + ) : ( + $checkbox + )} + + ); + })} + {buffered && ( + + + Apply Filters + + + Cancel + + + )} + + + ); +}; + +const MultiselectMenuOptionsContainer = styled(Flex)<{ + position: 'top' | 'bottom'; +}>` + position: sticky; + ${p => (p.position === 'top' ? 'top: 0;' : 'bottom: 0;')} + background-color: ${p => p.theme.colors.levels.elevated}; + z-index: 1; +`; + +const FiltersExistIndicator = styled.div` + position: absolute; + top: -4px; + right: -4px; + height: 12px; + width: 12px; + background-color: ${p => p.theme.colors.brand}; + border-radius: 50%; + display: inline-block; +`; diff --git a/web/packages/shared/components/Controls/SortMenu.story.tsx b/web/packages/shared/components/Controls/SortMenu.story.tsx new file mode 100644 index 0000000000000..56158b8a8d228 --- /dev/null +++ b/web/packages/shared/components/Controls/SortMenu.story.tsx @@ -0,0 +1,83 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, { useState } from 'react'; +import { Flex } from 'design'; + +import { SortMenu } from './SortMenu'; + +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; + +export default { + title: 'Shared/Controls/SortMenu', + component: SortMenu, + argTypes: { + current: { + control: false, + description: 'Current sort', + table: { + type: { + summary: + "Array<{ fieldName: Exclude; dir: 'ASC' | 'DESC'>", + }, + }, + }, + fields: { + control: false, + description: 'Fields to sort by', + table: { + type: { + summary: + '{ value: Exclude; label: string }[]', + }, + }, + }, + onChange: { + control: false, + description: 'Callback when fieldName or dir is changed', + table: { + type: { + summary: + "(value: { fieldName: Exclude; dir: 'ASC' | 'DESC' }) => void", + }, + }, + }, + }, + args: { + current: { fieldName: 'name', dir: 'ASC' }, + fields: [ + { value: 'name', label: 'Name' }, + { value: 'created', label: 'Created' }, + { value: 'updated', label: 'Updated' }, + ], + }, + parameters: { controls: { expanded: true, exclude: ['userContext'] } }, +} satisfies Meta>; + +const Default: StoryObj = { + render: (({ current, fields }) => { + const [sort, setSort] = useState(current); + return ( + + + + ); + }) satisfies StoryFn, +}; + +export { Default as SortMenu }; diff --git a/web/packages/shared/components/Controls/SortMenu.tsx b/web/packages/shared/components/Controls/SortMenu.tsx new file mode 100644 index 0000000000000..d6bbc5cdf0d2d --- /dev/null +++ b/web/packages/shared/components/Controls/SortMenu.tsx @@ -0,0 +1,120 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, { useState } from 'react'; +import { ButtonBorder, Flex, Menu, MenuItem } from 'design'; +import { ArrowDown, ArrowUp } from 'design/Icon'; + +import { HoverTooltip } from 'shared/components/ToolTip'; + +type SortMenuSort = { + fieldName: Exclude; + dir: 'ASC' | 'DESC'; +}; + +export const SortMenu = ({ + current, + fields, + onChange, +}: { + current: SortMenuSort; + fields: { value: SortMenuSort['fieldName']; label: string }[]; + onChange: (value: SortMenuSort) => void; +}) => { + const [anchorEl, setAnchorEl] = useState(null); + + const handleOpen = (event: React.MouseEvent) => + setAnchorEl(event.currentTarget); + + const handleClose = () => setAnchorEl(null); + + const handleSelect = (value: (typeof fields)[number]['value']) => { + handleClose(); + onChange({ + fieldName: value, + dir: current.dir, + }); + }; + + return ( + + + props.theme.colors.spotBackground[2]}; + `} + textTransform="none" + size="small" + px={2} + onClick={handleOpen} + aria-label="Sort by" + aria-haspopup="true" + aria-expanded={!!anchorEl} + > + {fields.find(f => f.value === current.fieldName)?.label} + + + `margin-top: 36px; margin-left: 28px;`} + transformOrigin={{ + vertical: 'top', + horizontal: 'right', + }} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'right', + }} + anchorEl={anchorEl} + open={Boolean(anchorEl)} + onClose={handleClose} + > + {fields.map(({ value, label }) => ( + handleSelect(value)}> + {label} + + ))} + + + + onChange({ + fieldName: current.fieldName, + dir: current.dir === 'ASC' ? 'DESC' : 'ASC', + }) + } + textTransform="none" + css={` + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-color: ${props => props.theme.colors.spotBackground[2]}; + `} + size="small" + > + {current.dir === 'ASC' ? ( + + ) : ( + + )} + + + + ); +}; diff --git a/web/packages/shared/components/Controls/ViewModeSwitch.story.tsx b/web/packages/shared/components/Controls/ViewModeSwitch.story.tsx new file mode 100644 index 0000000000000..adc77e1975715 --- /dev/null +++ b/web/packages/shared/components/Controls/ViewModeSwitch.story.tsx @@ -0,0 +1,61 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, { useState } from 'react'; +import { Flex } from 'design'; + +import { ViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb'; + +import { ViewModeSwitch } from './ViewModeSwitch'; + +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; + +export default { + title: 'Shared/Controls/ViewModeSwitch', + component: ViewModeSwitch, + argTypes: { + currentViewMode: { + control: { type: 'radio', options: [ViewMode.CARD, ViewMode.LIST] }, + description: 'Current view mode', + table: { defaultValue: { summary: ViewMode.CARD.toString() } }, + }, + setCurrentViewMode: { + control: false, + description: 'Callback to set current view mode', + table: { type: { summary: '(newViewMode: ViewMode) => void' } }, + }, + }, + args: { currentViewMode: ViewMode.CARD }, + parameters: { controls: { expanded: true, exclude: ['userContext'] } }, +} satisfies Meta; + +const Default: StoryObj = { + render: (({ currentViewMode }) => { + const [viewMode, setViewMode] = useState(currentViewMode); + return ( + + + + ); + }) satisfies StoryFn, +}; + +export { Default as ViewModeSwitch }; diff --git a/web/packages/shared/components/Controls/ViewModeSwitch.tsx b/web/packages/shared/components/Controls/ViewModeSwitch.tsx new file mode 100644 index 0000000000000..7997f2de29f66 --- /dev/null +++ b/web/packages/shared/components/Controls/ViewModeSwitch.tsx @@ -0,0 +1,120 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React from 'react'; +import styled from 'styled-components'; +import { Rows, SquaresFour } from 'design/Icon'; + +import { ViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb'; + +import { HoverTooltip } from 'shared/components/ToolTip'; + +export const ViewModeSwitch = ({ + currentViewMode, + setCurrentViewMode, +}: { + currentViewMode: ViewMode; + setCurrentViewMode: (viewMode: ViewMode) => void; +}) => { + return ( + + + setCurrentViewMode(ViewMode.CARD)} + role="radio" + aria-label="Card View" + aria-checked={currentViewMode === ViewMode.CARD} + first + > + + + + + setCurrentViewMode(ViewMode.LIST)} + role="radio" + aria-label="List View" + aria-checked={currentViewMode === ViewMode.LIST} + last + > + + + + + ); +}; + +const ViewModeSwitchContainer = styled.div` + height: 22px; + border: ${p => p.theme.borders[1]} ${p => p.theme.colors.spotBackground[2]}; + border-radius: ${p => p.theme.radii[2]}px; + display: flex; + + .selected { + background-color: ${p => p.theme.colors.spotBackground[1]}; + + &:focus-visible, + &:hover { + background-color: ${p => p.theme.colors.spotBackground[1]}; + } + } +`; + +const ViewModeSwitchButton = styled.button<{ first?: boolean; last?: boolean }>` + height: 100%; + width: 100%; + overflow: hidden; + border: none; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + background-color: transparent; + outline: none; + transition: outline-width 150ms ease; + + ${p => + p.first && + ` + border-top-left-radius: ${p.theme.radii[2]}px; + border-bottom-left-radius: ${p.theme.radii[2]}px; + border-right: ${p.theme.borders[1]} ${p.theme.colors.spotBackground[2]}; + `} + ${p => + p.last && + ` + border-top-right-radius: ${p.theme.radii[2]}px; + border-bottom-right-radius: ${p.theme.radii[2]}px; + `} + + &:focus-visible { + outline: ${p => p.theme.borders[1]} + ${p => p.theme.colors.text.slightlyMuted}; + } + + &:focus-visible, + &:hover { + background-color: ${p => p.theme.colors.spotBackground[0]}; + } +`; diff --git a/web/packages/shared/components/UnifiedResources/FilterPanel.tsx b/web/packages/shared/components/UnifiedResources/FilterPanel.tsx index eb3763d7a5ae7..6e62a50a3d4c9 100644 --- a/web/packages/shared/components/UnifiedResources/FilterPanel.tsx +++ b/web/packages/shared/components/UnifiedResources/FilterPanel.tsx @@ -18,25 +18,19 @@ import React, { useState } from 'react'; import styled from 'styled-components'; -import { ButtonBorder, ButtonPrimary, ButtonSecondary } from 'design/Button'; -import { SortDir } from 'design/DataTable/types'; +import { ButtonBorder, ButtonSecondary } from 'design/Button'; import { Text, Flex, Toggle } from 'design'; -import Menu, { MenuItem } from 'design/Menu'; +import Menu from 'design/Menu'; import { CheckboxInput } from 'design/Checkbox'; -import { - ArrowUp, - ArrowDown, - ChevronDown, - SquaresFour, - Rows, - ArrowsIn, - ArrowsOut, - Refresh, -} from 'design/Icon'; +import { ChevronDown, ArrowsIn, ArrowsOut, Refresh } from 'design/Icon'; import { ViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb'; -import { HoverTooltip } from 'design/Tooltip'; +import { HoverTooltip } from 'shared/components/ToolTip'; +import { SortMenu } from 'shared/components/Controls/SortMenu'; +import { ViewModeSwitch } from 'shared/components/Controls/ViewModeSwitch'; + +import { MultiselectMenu } from 'shared/components/Controls/MultiselectMenu'; import { ResourceAvailabilityFilter, FilterKind } from './UnifiedResources'; import { @@ -136,11 +130,17 @@ export function FilterPanel({ data-testid="select_all" /> - - ({ + value: kind, + label: kindToLabel[kind], + disabled: disabled, + }))} + selected={kinds || []} onChange={onKindsChanged} - availableKinds={availableKinds} - kindsFromParams={kinds || []} + label="Types" + tooltip="Filter by resource type" + buffered /> {ClusterDropdown} {availabilityFilter && ( @@ -197,10 +197,19 @@ export function FilterPanel({ )} { + if (newSort.dir !== sort.dir) { + onSortOrderButtonClicked(); + } + if (newSort.fieldName !== activeSortFieldOption.value) { + onSortFieldChange(newSort.fieldName); + } + }} /> @@ -221,303 +230,6 @@ function oppositeSort( } } -type FilterTypesMenuProps = { - availableKinds: FilterKind[]; - kindsFromParams: string[]; - onChange: (kinds: string[]) => void; -}; - -const FilterTypesMenu = ({ - onChange, - availableKinds, - kindsFromParams, -}: FilterTypesMenuProps) => { - const kindOptions = availableKinds.map(({ kind, disabled }) => ({ - value: kind, - label: kindToLabel[kind], - disabled: disabled, - })); - - const [anchorEl, setAnchorEl] = useState(null); - // we have a separate state in the filter so we can select a few different things and then click "apply" - const [kinds, setKinds] = useState(kindsFromParams || []); - const handleOpen = event => { - setKinds(kindsFromParams); - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - // if we cancel, we reset the kinds to what is already selected in the params - const cancelUpdate = () => { - setKinds(kindsFromParams); - handleClose(); - }; - - const handleSelect = (value: string) => { - let newKinds = [...kinds]; - if (newKinds.includes(value)) { - newKinds = newKinds.filter(v => v !== value); - } else { - newKinds.push(value); - } - setKinds(newKinds); - }; - - const handleSelectAll = () => { - setKinds(kindOptions.filter(k => !k.disabled).map(k => k.value)); - }; - - const handleClearAll = () => { - setKinds([]); - }; - - const applyFilters = () => { - onChange(kinds); - handleClose(); - }; - - return ( - - - - Types{' '} - {kindsFromParams.length > 0 ? `(${kindsFromParams.length})` : ''} - - {kindsFromParams.length > 0 && } - - - `margin-top: 36px;`} - transformOrigin={{ - vertical: 'top', - horizontal: 'left', - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - anchorEl={anchorEl} - open={Boolean(anchorEl)} - onClose={cancelUpdate} - > - - - Select All - - - Clear All - - - {kindOptions.map(kind => { - const $checkbox = ( - <> - { - handleSelect(kind.value); - }} - id={kind.value} - checked={kinds.includes(kind.value)} - /> - - {kind.label} - - - ); - return ( - (!kind.disabled ? handleSelect(kind.value) : null)} - > - {kind.disabled ? ( - - {$checkbox} - - ) : ( - $checkbox - )} - - ); - })} - - - Apply Filters - - - Cancel - - - - - ); -}; - -type SortMenuProps = { - transformOrigin?: any; - anchorOrigin?: any; - sortType: string; - sortDir: SortDir; - onChange: (value: string) => void; - onDirChange: () => void; -}; - -const SortMenu: React.FC = props => { - const { sortType, onChange, onDirChange, sortDir } = props; - const [anchorEl, setAnchorEl] = React.useState(null); - - const handleOpen = event => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const handleSelect = (value: string) => { - handleClose(); - onChange(value); - }; - - return ( - - - props.theme.colors.spotBackground[2]}; - `} - textTransform="none" - size="small" - px={2} - onClick={handleOpen} - > - {sortType} - - - `margin-top: 36px;`} - transformOrigin={{ - vertical: 'top', - horizontal: 'center', - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'center', - }} - anchorEl={anchorEl} - open={Boolean(anchorEl)} - onClose={handleClose} - > - handleSelect('name')}>Name - handleSelect('kind')}>Type - - - props.theme.colors.spotBackground[2]}; - `} - size="small" - > - {sortDir === 'ASC' ? : } - - - - ); -}; - -function kindArraysEqual(arr1: string[], arr2: string[]) { - if (arr1.length !== arr2.length) { - return false; - } - - const sortedArr1 = arr1.slice().sort(); - const sortedArr2 = arr2.slice().sort(); - - for (let i = 0; i < sortedArr1.length; i++) { - if (sortedArr1[i] !== sortedArr2[i]) { - return false; - } - } - - return true; -} - -function ViewModeSwitch({ - currentViewMode, - setCurrentViewMode, -}: { - currentViewMode: ViewMode; - setCurrentViewMode: (viewMode: ViewMode) => void; -}) { - return ( - - setCurrentViewMode(ViewMode.CARD)} - css={` - border-right: 1px solid - ${props => props.theme.colors.spotBackground[2]}; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - `} - > - - - setCurrentViewMode(ViewMode.LIST)} - css={` - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - `} - > - - - - ); -} - const IncludedResourcesSelector = ({ onChange, availabilityFilter, @@ -580,7 +292,9 @@ const IncludedResourcesSelector = ({ onClose={handleClose} > - Show requestable resources + + Show requestable resources + props.theme.colors.spotBackground[2]}; - border-radius: 4px; - display: flex; - - .selected { - background-color: ${props => props.theme.colors.spotBackground[1]}; - - &:hover { - background-color: ${props => props.theme.colors.spotBackground[1]}; - } - } -`; - -const ViewModeSwitchButton = styled.button` - height: 100%; - width: 50%; - overflow: hidden; - border: none; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - - background-color: transparent; - - &:hover { - background-color: ${props => props.theme.colors.spotBackground[0]}; - } -`; - const FiltersExistIndicator = styled.div` position: absolute; top: -4px; @@ -641,8 +322,9 @@ const FiltersExistIndicator = styled.div` const AccessRequestsToggleItem = styled.div` min-height: 40px; box-sizing: border-box; - padding-left: ${props => props.theme.space[2]}px; - padding-right: ${props => props.theme.space[2]}px; + padding-top: 2px; + padding-left: ${props => props.theme.space[3]}px; + padding-right: ${props => props.theme.space[3]}px; display: flex; justify-content: flex-start; align-items: center; diff --git a/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx b/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx index a5eb1e8fc7204..4d4f40a4f49c4 100644 --- a/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx +++ b/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx @@ -65,7 +65,7 @@ export function ChangePasswordWizard({ const reauthState = useReAuthenticate({ challengeScope: MfaChallengeScope.CHANGE_PASSWORD, onMfaResponse: async mfaResponse => - setWebauthnResponse(mfaResponse.webauthn_response), + setWebauthnResponse(mfaResponse?.webauthn_response), }); const [reauthMethod, setReauthMethod] = useState(); diff --git a/web/packages/teleport/src/Audit/EventList/EventTypeCell.tsx b/web/packages/teleport/src/Audit/EventList/EventTypeCell.tsx index a65be04e962d6..3db495c91dd2c 100644 --- a/web/packages/teleport/src/Audit/EventList/EventTypeCell.tsx +++ b/web/packages/teleport/src/Audit/EventList/EventTypeCell.tsx @@ -287,6 +287,8 @@ const EventIconMap: Record = { [eventCodes.PLUGIN_CREATE]: Icons.Info, [eventCodes.PLUGIN_UPDATE]: Icons.Info, [eventCodes.PLUGIN_DELETE]: Icons.Info, + [eventCodes.CONTACT_CREATE]: Icons.Info, + [eventCodes.CONTACT_DELETE]: Icons.Info, [eventCodes.UNKNOWN]: Icons.Question, }; diff --git a/web/packages/teleport/src/AuthConnectors/AuthConnectorTile.tsx b/web/packages/teleport/src/AuthConnectors/AuthConnectorTile.tsx new file mode 100644 index 0000000000000..e69ab077c8219 --- /dev/null +++ b/web/packages/teleport/src/AuthConnectors/AuthConnectorTile.tsx @@ -0,0 +1,210 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import styled, { useTheme } from 'styled-components'; + +import { Flex, H2, Subtitle2, ButtonSecondary, P3, Box } from 'design'; +import { ArrowRight, CircleCheck, Password } from 'design/Icon'; + +import { MenuIcon, MenuItem } from 'shared/components/MenuAction'; +import { AuthType } from 'shared/services'; + +import { State as ResourceState } from 'teleport/components/useResources'; + +export function AuthConnectorTile({ + id, + name, + kind, + Icon, + isDefault, + isPlaceholder, + onSetup, + customDesc, + onEdit, + onDelete, +}: { + name: string; + id: string; + kind: AuthType; + Icon: () => JSX.Element; + isDefault: boolean; + /** + * isPlaceholder is whether this isn't a real existing connector, but a placeholder as a shortcut to set one up. + */ + isPlaceholder: boolean; + onSetup?: () => void; + customDesc?: string; + onEdit?: ResourceState['edit']; + onDelete?: ResourceState['remove']; +}) { + const theme = useTheme(); + const onClickEdit = () => onEdit(id); + const onClickDelete = () => onDelete(id); + + let desc: string; + switch (kind) { + case 'github': + desc = 'GitHub Connector'; + break; + case 'oidc': + desc = 'OIDC Connector'; + break; + case 'saml': + desc = 'SAML Connector'; + break; + default: + kind satisfies never | 'local'; + } + + return ( + + + + + +

{name}

+ {isDefault && } +
+ + {customDesc || desc} + +
+
+ + {isPlaceholder && !!onSetup && ( + + Set Up + + )} + {!isPlaceholder && !!onEdit && !!onDelete && ( + + Edit + Delete + + )} + +
+ ); +} + +/** + * LocalConnectorTile is a hardcoded "auth connector" which represents local auth. + */ +export function LocalConnectorTile() { + return ( + ( + + props.theme.colors.interactive.tonal.neutral[0]}; + height: 61px; + width: 61px; + `} + lineHeight={0} + p={2} + borderRadius={3} + > + + + )} + isDefault={true} + isPlaceholder={false} + name="Local Connector" + customDesc="Manual auth w/ users local to Teleport" + /> + ); +} + +export const ConnectorBox = styled(Box)<{ disabled?: boolean }>` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + font-family: ${props => props.theme.font}; + padding: ${p => p.theme.space[3]}px; + transition: all 0.3s; + border-radius: ${props => props.theme.radii[2]}px; + border: ${props => props.theme.borders[2]} + ${props => props.theme.colors.interactive.tonal.neutral[0]}; + + &:hover { + background: ${props => props.theme.colors.levels.surface}; + border: ${props => props.theme.borders[2]} transparent; + } + + &:focus-visible { + outline: none; + border: ${props => props.theme.borders[2]} + ${props => props.theme.colors.text.muted}; + } + + &:active { + outline: none; + background: ${props => props.theme.colors.levels.surface}; + border: ${props => props.theme.borders[2]} + ${props => props.theme.colors.interactive.tonal.neutral[1]}; + } +`; + +function DefaultIndicator() { + return ( + + props.theme.colors.interactive.tonal.success[1]}; + border-radius: 62px; + `} + > + + + Default + + + ); +} diff --git a/web/packages/teleport/src/AuthConnectors/AuthConnectors.tsx b/web/packages/teleport/src/AuthConnectors/AuthConnectors.tsx index fc16cfe7d20a3..6f0b297c4bdf5 100644 --- a/web/packages/teleport/src/AuthConnectors/AuthConnectors.tsx +++ b/web/packages/teleport/src/AuthConnectors/AuthConnectors.tsx @@ -18,7 +18,7 @@ import { Alert, Box, Flex, H3, Indicator, Link } from 'design'; -import { P } from 'design/Text/Text'; +import { H2, P } from 'design/Text/Text'; import { FeatureBox, FeatureHeaderTitle } from 'teleport/components/Layout'; import ResourceEditor from 'teleport/components/ResourceEditor'; @@ -36,6 +36,7 @@ import ConnectorList from './ConnectorList'; import DeleteConnectorDialog from './DeleteConnectorDialog'; import useAuthConnectors, { State } from './useAuthConnectors'; import templates from './templates'; +import CTAConnectors from './ConnectorList/CTAConnectors'; export function AuthConnectorsContainer() { const state = useAuthConnectors(); @@ -80,34 +81,37 @@ export function AuthConnectors(props: State) { )} {attempt.status === 'success' && ( - {isEmpty && ( - - resources.create('github')} /> - - )} - <> - - -

Auth Connectors

-

{description}

-

- Please{' '} - - view our documentation - {' '} - on how to configure a GitHub connector. -

-
- + + +

Your Connectors

+ {isEmpty ? ( + resources.create('github')} /> + ) : ( + + )} +
+ +
+ +

Auth Connectors

+

{description}

+

+ Please{' '} + + view our documentation + {' '} + on how to configure a GitHub connector. +

+
)} {(resources.status === 'creating' || resources.status === 'editing') && ( diff --git a/web/packages/teleport/src/AuthConnectors/ConnectorList/CTAConnectors.tsx b/web/packages/teleport/src/AuthConnectors/ConnectorList/CTAConnectors.tsx new file mode 100644 index 0000000000000..117b02e4236d6 --- /dev/null +++ b/web/packages/teleport/src/AuthConnectors/ConnectorList/CTAConnectors.tsx @@ -0,0 +1,120 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import styled from 'styled-components'; + +import Box from 'design/Box'; +import { ResourceIcon } from 'design/ResourceIcon'; +import { H2, Subtitle2 } from 'design/Text'; +import Flex from 'design/Flex'; +import { RocketLaunch } from 'design/Icon'; + +import { ButtonLockedFeature } from 'teleport/components/ButtonLockedFeature'; +import { CtaEvent } from 'teleport/services/userEvent'; + +export default function CTAConnectors() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + +

Unlock OIDC & SAML Single Sign-On with Teleport Enterprise

+ + Connect your identity provider to streamline employee onboarding, + simplify compliance, and monitor access patterns against a single + source of truth. + + + + Upgrade to Enterprise + +
+
+ ); +} + +const AuthConnectorsCTABox = styled(Box)` + width: 100%; + padding: ${p => p.theme.space[5]}px; + + border: ${props => props.theme.borders[1]}; + border-color: ${props => props.theme.colors.interactive.tonal.neutral[1]}; + border-radius: ${props => props.theme.radii[2]}px; + background: transparent; + + display: flex; + flex-direction: column; + align-items: flex-start; + gap: ${p => p.theme.space[3]}px; + align-self: stretch; +`; + +const CTALogosContainer = styled(Box)` + display: flex; + flex-direction: row; + width: 100%; + // We have to manually set a height here since the CTALogo's are 'position: absolute' and won't automatically size the container. + //padding (top and bottom) + height of the icon + height: ${p => p.theme.space[4] * 2 + 36}px; + + position: relative; +`; + +const CTALogo = styled(Box)` + background-color: ${props => props.theme.colors.levels.sunken}; + padding: ${p => p.theme.space[4]}px; + + display: flex; + align-items: center; + justify-content: center; + + border: ${props => props.theme.borders[1]}; + border-color: ${props => props.theme.colors.interactive.tonal.neutral[1]}; + border-radius: 50%; + + position: absolute; +`; diff --git a/web/packages/teleport/src/AuthConnectors/ConnectorList/ConnectorList.tsx b/web/packages/teleport/src/AuthConnectors/ConnectorList/ConnectorList.tsx index 39544ef9bda3b..8f3605b4044a1 100644 --- a/web/packages/teleport/src/AuthConnectors/ConnectorList/ConnectorList.tsx +++ b/web/packages/teleport/src/AuthConnectors/ConnectorList/ConnectorList.tsx @@ -16,23 +16,31 @@ * along with this program. If not, see . */ -import { Box, ButtonPrimary, Flex, ResourceIcon, Text } from 'design'; -import { MenuIcon, MenuItem } from 'shared/components/MenuAction'; +import styled from 'styled-components'; -import { State as ResourceState } from 'teleport/components/useResources'; +import { Box } from 'design'; -import { ResponsiveConnector } from 'teleport/AuthConnectors/styles/ConnectorBox.styles'; +import { State as ResourceState } from 'teleport/components/useResources'; import { State as AuthConnectorState } from '../useAuthConnectors'; +import { AuthConnectorTile, LocalConnectorTile } from '../AuthConnectorTile'; +import getSsoIcon from '../ssoIcons/getSsoIcon'; export default function ConnectorList({ items, onEdit, onDelete }: Props) { items = items || []; const $items = items.map(item => { - const { id, name } = item; + const { id, name, kind } = item; + + const Icon = getSsoIcon(kind, name); + return ( - + + {$items} - - ); -} - -function ConnectorListItem({ name, id, onEdit, onDelete }) { - const onClickEdit = () => onEdit(id); - const onClickDelete = () => onDelete(id); - - return ( - - - - Delete... - - - - - - - - {name} - - - - Edit Connector - - + ); } -const menuActionProps = { - style: { - right: '10px', - position: 'absolute', - top: '10px', - }, -}; - type Props = { items: AuthConnectorState['items']; onEdit: ResourceState['edit']; onDelete: ResourceState['remove']; }; + +export const AuthConnectorsGrid = styled(Box)` + width: 100%; + display: grid; + gap: ${p => p.theme.space[3]}px; + grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); +`; diff --git a/web/packages/teleport/src/AuthConnectors/EmptyList/EmptyList.tsx b/web/packages/teleport/src/AuthConnectors/EmptyList/EmptyList.tsx index b3dd5ea561647..0751eb31347f8 100644 --- a/web/packages/teleport/src/AuthConnectors/EmptyList/EmptyList.tsx +++ b/web/packages/teleport/src/AuthConnectors/EmptyList/EmptyList.tsx @@ -16,78 +16,27 @@ * along with this program. If not, see . */ -import { Card, Flex, H1, ResourceIcon, Text } from 'design'; -import { AuthProviderType } from 'shared/services'; - -import { H2 } from 'design'; - -import { ConnectorBox } from 'teleport/AuthConnectors/styles/ConnectorBox.styles'; - -import { - LockedFeatureButton, - LockedFeatureContainer, -} from 'teleport/AuthConnectors/styles/LockedFeatureContainer.styles'; - import getSsoIcon from 'teleport/AuthConnectors/ssoIcons/getSsoIcon'; import { State as ResourceState } from 'teleport/components/useResources'; -import { CtaEvent } from 'teleport/services/userEvent'; - -export default function EmptyList({ onCreate }: Props) { - return ( - -

Select a service provider below

- - {renderGithubConnector(onCreate)} - - {renderLockedItem('oidc')} - {renderLockedItem('saml')} - - Unlock OIDC & SAML with Teleport Enterprise - - - -
- ); -} -function renderGithubConnector(onCreate) { - return ( - - - - - - +import { AuthConnectorTile, LocalConnectorTile } from '../AuthConnectorTile'; +import { AuthConnectorsGrid } from '../ConnectorList/ConnectorList'; -

GitHub

- { - - Sign in using your GitHub account - - } -
- ); -} - -function renderLockedItem(kind: AuthProviderType) { - const { desc, SsoIcon, info } = getSsoIcon(kind); +export default function EmptyList({ onCreate }: Props) { return ( - - - - -

{desc}

- {info && ( - - {info} - - )} -
+ + + onCreate('github')} + /> + ); } diff --git a/web/packages/teleport/src/AuthConnectors/ssoIcons/getSsoIcon.tsx b/web/packages/teleport/src/AuthConnectors/ssoIcons/getSsoIcon.tsx index 27fae0e56fd9a..fbb116b59f3f2 100644 --- a/web/packages/teleport/src/AuthConnectors/ssoIcons/getSsoIcon.tsx +++ b/web/packages/teleport/src/AuthConnectors/ssoIcons/getSsoIcon.tsx @@ -20,93 +20,132 @@ import styled from 'styled-components'; import { AuthProviderType } from 'shared/services'; import { Box, Flex, ResourceIcon } from 'design'; -export default function getSsoIcon(kind: AuthProviderType) { - const desc = formatConnectorTypeDesc(kind); +export default function getSsoIcon( + kind: AuthProviderType, + name?: string +): () => JSX.Element { + const guessedIcon = guessIconFromName(name || ''); + if (guessedIcon) { + return guessedIcon; + } switch (kind) { case 'github': - return { - SsoIcon: () => ( - - - - ), - desc, - info: 'Sign in using your GitHub account', - }; + return () => ( + + + + ); case 'saml': - return { - SsoIcon: () => ( - - - - - - - - - - - - - - - ), - desc, - info: 'Okta, OneLogin, Microsoft Entra ID, etc.', - }; + return () => ( + + + + + + + + + + + + + + + ); case 'oidc': default: - return { - SsoIcon: () => ( - - - - - - - - - - - - - - - ), - desc, - info: 'Google, GitLab, Amazon and more', - }; + return () => ( + + + + + + + + + + + + + + + ); } } -function formatConnectorTypeDesc(kind) { - kind = kind || ''; - if (kind == 'github') { - return `GitHub`; +function guessIconFromName(connectorName: string) { + const name = connectorName.toLocaleLowerCase(); + + if (name.includes('okta')) { + return () => ( + + + + ); + } + if ( + name.includes('entra') || + name.includes('active directory') || + name.includes('microsoft') || + name.includes('azure') + ) { + return () => ( + + + + ); + } + if (name.includes('google')) { + return () => ( + + + + ); + } + if (name.includes('gitlab')) { + return () => ( + + + + ); + } + if (name.includes('onelogin')) { + return () => ( + + + + ); + } + if (name.includes('auth0') || name.includes('authzero')) { + return () => ( + + + + ); } - return kind.toUpperCase(); } const MultiIconContainer = styled(Flex)` - width: 83px; + width: 61px; + height: 61px; flex-wrap: wrap; gap: 3px; - padding: 7px; - border: 1px solid rgba(255, 255, 255, 0.07); + padding: ${p => p.theme.space[2]}px; + border: 1px solid ${p => p.theme.colors.interactive.tonal.neutral[2]}; border-radius: 8px; `; const SmIcon = styled(Box)` - width: ${p => p.theme.space[5]}px; - height: ${p => p.theme.space[5]}px; - line-height: ${p => p.theme.space[5]}px; - background: ${p => p.theme.colors.levels.popout}; - border-radius: 50%; + width: 20px; + height: 20px; display: flex; justify-content: center; + align-items: center; `; const StyledResourceIcon = styled(ResourceIcon).attrs({ width: '20px', -})``; +})` + line-height: 0px !important; +`; diff --git a/web/packages/teleport/src/AuthConnectors/styles/ConnectorBox.styles.ts b/web/packages/teleport/src/AuthConnectors/styles/ConnectorBox.styles.ts deleted file mode 100644 index f88e5e7ad5646..0000000000000 --- a/web/packages/teleport/src/AuthConnectors/styles/ConnectorBox.styles.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Box, Flex } from 'design'; -import styled from 'styled-components'; - -export const ConnectorBox = styled(Box)` - display: flex; - flex-direction: column; - font-family: ${props => props.theme.font}; - width: 320px; - padding: ${p => p.theme.space[4]}px; - margin: ${p => p.theme.space[3]}px ${p => p.theme.space[2]}px; - background: transparent; - transition: all 0.3s; - border-radius: ${props => props.theme.radii[2]}px; - min-height: 190px; - border: ${props => props.theme.borders[2]} - ${p => p.theme.colors.spotBackground[0]}; - - &:hover, - &:focus { - border: ${props => props.theme.borders[2]} - ${p => p.theme.colors.spotBackground[2]}; - background: ${p => p.theme.colors.spotBackground[0]}; - box-shadow: ${p => p.theme.boxShadow[3]}; - cursor: pointer; - } - - &:disabled { - cursor: not-allowed; - color: inherit; - font-family: inherit; - outline: none; - position: relative; - text-align: center; - text-decoration: none; - opacity: 0.24; - box-shadow: none; - } -`; - -export const ResponsiveConnector = styled(Flex)` - position: relative; - box-shadow: ${p => p.theme.boxShadow[5]}; - width: 240px; - height: 240px; - border-radius: ${props => props.theme.radii[2]}px; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: ${props => props.theme.colors.levels.surface}; - padding: ${props => props.theme.space[5]}px; - @media screen and (max-width: ${props => props.theme.breakpoints.tablet}px) { - width: 100%; - } -`; diff --git a/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/ResourceLabelTooltip.story.tsx b/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/ResourceLabelTooltip.story.tsx new file mode 100644 index 0000000000000..67aa0e695d529 --- /dev/null +++ b/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/ResourceLabelTooltip.story.tsx @@ -0,0 +1,29 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { ResourceLabelTooltip } from './ResourceLabelTooltip'; + +export default { + title: 'Teleport/Discover/Shared/ResourceLabelTooltip', +}; + +export const RDS = () => ; +export const EKS = () => ; +export const Server = () => ; +export const Database = () => ; +export const Kube = () => ; diff --git a/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/ResourceLabelTooltip.tsx b/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/ResourceLabelTooltip.tsx new file mode 100644 index 0000000000000..2e7ad2d1980aa --- /dev/null +++ b/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/ResourceLabelTooltip.tsx @@ -0,0 +1,147 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import Link from 'design/Link'; +import { MarkInverse } from 'design/Mark'; +import { Position } from 'design/Popover/Popover'; +import { IconTooltip } from 'design/Tooltip'; +import styled from 'styled-components'; + +/** + * Returns a IconTooltip component with its tip contents + * set to the requested resource kind. + * + * @param resourceKind - the tip contents differ slightly depending + * on the resource kind + * @param toolTipPosition (opt) - the position which the tooltip should + * render (see type Position) + * @returns JSX Element + */ +export function ResourceLabelTooltip({ + resourceKind, + toolTipPosition, +}: { + resourceKind: 'server' | 'eks' | 'rds' | 'kube' | 'db'; + toolTipPosition?: Position; +}) { + let tip; + + switch (resourceKind) { + case 'server': { + tip = ( + <> + Labels allow you to do the following: +
    +
  • + Filter servers by labels when using tsh, tctl, or the web UI. +
  • +
  • + Restrict access to this server with{' '} + + Teleport RBAC + + . Only roles with node_labels that + match these labels will be allowed to access this server. +
  • +
+ + ); + break; + } + case 'kube': + case 'eks': { + tip = ( + <> + Labels allow you to do the following: +
    +
  • + Filter Kubernetes clusters by labels when using tsh, tctl, or the + web UI. +
  • +
  • + Restrict access to this Kubernetes cluster with{' '} + + Teleport RBAC + + . Only roles with kubernetes_labels{' '} + that match these labels will be allowed to access this Kubernetes + cluster. +
  • + {resourceKind === 'eks' && ( +
  • + All the AWS tags from the selected EKS will be included upon + enrollment. +
  • + )} +
+ + ); + break; + } + case 'rds': + case 'db': { + tip = ( + <> + Labels allow you to do the following: +
    +
  • + Filter databases by labels when using tsh, tctl, or the web UI. +
  • +
  • + Restrict access to this database with{' '} + + Teleport RBAC + + . Only roles with db_labels that match + these labels will be allowed to access this database. +
  • + {resourceKind === 'rds' && ( +
  • + All the AWS tags from the selected RDS will be included upon + enrollment. +
  • + )} +
+ + ); + break; + } + default: + resourceKind satisfies never; + } + + return ( + + {tip} + + ); +} + +const Ul = styled.ul` + margin: 0; + padding-left: ${p => p.theme.space[4]}px; +`; diff --git a/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/index.ts b/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/index.ts new file mode 100644 index 0000000000000..fd093b71e1080 --- /dev/null +++ b/web/packages/teleport/src/Discover/Shared/ResourceLabelTooltip/index.ts @@ -0,0 +1,19 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +export { ResourceLabelTooltip } from './ResourceLabelTooltip'; diff --git a/web/packages/teleport/src/JoinTokens/JoinTokens.tsx b/web/packages/teleport/src/JoinTokens/JoinTokens.tsx index 862085cfd0157..1dbcc8b725ad2 100644 --- a/web/packages/teleport/src/JoinTokens/JoinTokens.tsx +++ b/web/packages/teleport/src/JoinTokens/JoinTokens.tsx @@ -446,7 +446,7 @@ const ActionCell = ({ function Directions() { return ( <> - WARNING Roles are defined using{' '} + WARNING Tokens are defined using{' '} . */ +import { useCallback, useEffect } from 'react'; import styled from 'styled-components'; -import { Flex, Box } from 'design'; - +import { Flex, Box, Indicator } from 'design'; import { Danger } from 'design/Alert'; -import { useParams, useLocation } from 'teleport/components/Router'; +import { makeSuccessAttempt, useAsync } from 'shared/hooks/useAsync'; +import { useParams, useLocation } from 'teleport/components/Router'; import session from 'teleport/services/websession'; import { UrlPlayerParams } from 'teleport/config'; import { getUrlParameter } from 'teleport/services/history'; - import { RecordingType } from 'teleport/services/recordings'; +import useTeleport from 'teleport/useTeleport'; + import ActionBar from './ActionBar'; import { DesktopPlayer } from './DesktopPlayer'; import SshPlayer from './SshPlayer'; @@ -38,19 +40,44 @@ import Tabs, { TabItem } from './PlayerTabs'; const validRecordingTypes = ['ssh', 'k8s', 'desktop', 'database']; export function Player() { + const ctx = useTeleport(); const { sid, clusterId } = useParams(); const { search } = useLocation(); + useEffect(() => { + document.title = `Play ${sid} • ${clusterId}`; + }, [sid, clusterId]); + const recordingType = getUrlParameter( 'recordingType', search ) as RecordingType; - const durationMs = Number(getUrlParameter('durationMs', search)); + + // In order to render the progress bar, we need to know the length of the session. + // All in-product links to the session player should include the session duration in the URL. + // Some users manually build the URL based on the session ID and don't specify the session duration. + // For those cases, we make a separate API call to get the duration. + const [fetchDurationAttempt, fetchDuration] = useAsync( + useCallback( + () => ctx.recordingsService.fetchRecordingDuration(clusterId, sid), + [ctx.recordingsService, clusterId, sid] + ) + ); const validRecordingType = validRecordingTypes.includes(recordingType); - const validDurationMs = Number.isInteger(durationMs) && durationMs > 0; + const durationMs = Number(getUrlParameter('durationMs', search)); + const shouldFetchSessionDuration = + validRecordingType && (!Number.isInteger(durationMs) || durationMs <= 0); + + useEffect(() => { + if (shouldFetchSessionDuration) { + fetchDuration(); + } + }, [fetchDuration, shouldFetchSessionDuration]); - document.title = `Play ${sid} • ${clusterId}`; + const combinedAttempt = shouldFetchSessionDuration + ? fetchDurationAttempt + : makeSuccessAttempt({ durationMs }); function onLogout() { session.logout(); @@ -69,13 +96,25 @@ export function Player() { ); } - if (!validDurationMs) { + if ( + combinedAttempt.status === '' || + combinedAttempt.status === 'processing' + ) { + return ( + + + + + + ); + } + if (combinedAttempt.status === 'error') { return ( - Invalid query parameter durationMs:{' '} - {getUrlParameter('durationMs', search)}, should be an integer. + Unable to determine the length of this session. The session + recording may be incomplete or corrupted. @@ -101,15 +140,20 @@ export function Player() { ) : ( - + )} ); } + const StyledPlayer = styled.div` display: flex; height: 100%; diff --git a/web/packages/teleport/src/Support/Support.tsx b/web/packages/teleport/src/Support/Support.tsx index 59012984cb941..7bff9ae9e7ec9 100644 --- a/web/packages/teleport/src/Support/Support.tsx +++ b/web/packages/teleport/src/Support/Support.tsx @@ -96,6 +96,27 @@ export const Support = ({ {isEnterprise && !cfg.isCloud && licenseExpiryDateText && ( )} + {isCloud && ( + + + + Looking for{' '} + + Scheduled Upgrades? + {' '} + It is now in{' '} + + Cluster Management + {' '} + page. + + + )} diff --git a/web/packages/teleport/src/Users/UserReset/UserReset.story.tsx b/web/packages/teleport/src/Users/UserReset/UserReset.story.tsx index fd74a04b68ded..3a68f43ac6a8f 100644 --- a/web/packages/teleport/src/Users/UserReset/UserReset.story.tsx +++ b/web/packages/teleport/src/Users/UserReset/UserReset.story.tsx @@ -16,40 +16,69 @@ * along with this program. If not, see . */ +import { Meta } from '@storybook/react'; + +import { Attempt } from 'shared/hooks/useAttemptNext'; +import { useEffect, useState } from 'react'; + +import cfg from 'teleport/config'; + import { UserReset } from './UserReset'; -export default { - title: 'Teleport/Users/UserReset', +type StoryProps = { + status: 'processing' | 'success' | 'error'; + isMfaEnabled: boolean; + allowPasswordless: boolean; }; -export const Processing = () => { - return ; +const meta: Meta = { + title: 'Teleport/Users/UserReset', + component: Story, + argTypes: { + status: { + control: { type: 'radio' }, + options: ['processing', 'success', 'error'], + }, + }, + args: { + status: 'processing', + isMfaEnabled: true, + allowPasswordless: true, + }, }; -export const Success = () => { - return ; -}; +export default meta; + +export function Story(props: StoryProps) { + const statusToAttempt: Record = { + processing: { status: 'processing' }, + success: { status: 'success' }, + error: { status: 'failed', statusText: 'some server error' }, + }; + const [, setState] = useState({}); + + useEffect(() => { + const defaultAuth = structuredClone(cfg.auth); + cfg.auth.second_factor = props.isMfaEnabled ? 'on' : 'off'; + cfg.auth.allowPasswordless = props.allowPasswordless; + setState({}); // Force re-render of the component with new cfg. + + return () => { + cfg.auth = defaultAuth; + }; + }, [props.isMfaEnabled, props.allowPasswordless]); -export const Failed = () => { return ( {}} + onClose={() => {}} + attempt={statusToAttempt[props.status]} /> ); -}; - -const props = { - username: 'smith', - token: { - value: '0c536179038b386728dfee6602ca297f', - expires: new Date('2021-04-08T07:30:00Z'), - username: 'Lester', - }, - onReset() {}, - onClose() {}, - attempt: { - status: 'processing', - statusText: '', - }, -}; +} diff --git a/web/packages/teleport/src/Users/UserReset/UserReset.tsx b/web/packages/teleport/src/Users/UserReset/UserReset.tsx index 65e15b1a2f2c2..0eb11518a0fb5 100644 --- a/web/packages/teleport/src/Users/UserReset/UserReset.tsx +++ b/web/packages/teleport/src/Users/UserReset/UserReset.tsx @@ -17,7 +17,7 @@ */ import { useState } from 'react'; -import { ButtonPrimary, ButtonSecondary, Text, Alert } from 'design'; +import { ButtonPrimary, ButtonSecondary, Text, Alert, P2 } from 'design'; import Dialog, { DialogHeader, DialogTitle, @@ -26,6 +26,7 @@ import Dialog, { } from 'design/Dialog'; import { useAttemptNext } from 'shared/hooks'; +import cfg from 'teleport/config'; import { ResetToken } from 'teleport/services/user'; import UserTokenLink from './../UserTokenLink'; @@ -58,16 +59,31 @@ export function UserReset({ {attempt.status === 'failed' && ( - + {attempt.statusText} )} - - You are about to reset authentication for user - - {` ${username} `} + + You are about to reset authentication for user{' '} + + {username} - . This will generate a temporary URL which can be used to set up new - authentication. - + . This will generate a temporary URL which can be used to set up + new authentication. + + {cfg.isMfaEnabled() && ( + + All{' '} + {cfg.isPasswordlessEnabled() + ? 'passkeys and MFA methods' + : 'MFA methods'}{' '} + of this user will be removed. The user will be able to set up{' '} + {cfg.isPasswordlessEnabled() ? ( + <>a new passkey or an MFA method + ) : ( + <>a new method + )}{' '} + after following the URL. + + )} - Generate reset url + Generate Reset URL Cancel diff --git a/web/packages/teleport/src/config.ts b/web/packages/teleport/src/config.ts index 6980b827784cd..c8883b598eebf 100644 --- a/web/packages/teleport/src/config.ts +++ b/web/packages/teleport/src/config.ts @@ -17,21 +17,12 @@ */ import { generatePath } from 'react-router'; -import { mergeDeep } from 'shared/utils/highbar'; -import { IncludedResourceMode } from 'shared/components/UnifiedResources'; -import generateResourcePath from './generateResourcePath'; +import { IncludedResourceMode } from 'shared/components/UnifiedResources'; -import { defaultEntitlements } from './entitlement'; +import { mergeDeep } from 'shared/utils/highbar'; import { - AwsOidcPolicyPreset, - IntegrationKind, - PluginKind, - Regions, -} from './services/integrations'; - -import type { Auth2faType, AuthProvider, AuthType, @@ -39,12 +30,23 @@ import type { PrimaryAuthType, } from 'shared/services'; +import { + AwsOidcPolicyPreset, + IntegrationKind, + PluginKind, + Regions, +} from 'teleport/services/integrations'; +import { KubeResourceKind } from 'teleport/services/kube/types'; + +import { defaultEntitlements } from './entitlement'; + +import generateResourcePath from './generateResourcePath'; + import type { SortType } from 'teleport/services/agents'; import type { RecordingType } from 'teleport/services/recordings'; -import type { WebauthnAssertionResponse } from './services/mfa'; +import type { WebauthnAssertionResponse } from 'teleport/services/mfa'; import type { ParticipantMode } from 'teleport/services/session'; -import type { YamlSupportedResourceKind } from './services/yaml/types'; -import type { KubeResourceKind } from './services/kube/types'; +import type { YamlSupportedResourceKind } from 'teleport/services/yaml/types'; const cfg = { /** @deprecated Use cfg.edition instead. */ @@ -268,6 +270,7 @@ const cfg = { ttyPlaybackWsAddr: 'wss://:fqdn/v1/webapi/sites/:clusterId/ttyplayback/:sid?access_token=:token', // TODO(zmb3): get token out of URL activeAndPendingSessionsPath: '/v1/webapi/sites/:clusterId/sessions', + sessionDurationPath: '/v1/webapi/sites/:clusterId/sessionlength/:sid', kubernetesPath: '/v1/webapi/sites/:clusterId/kubernetes?searchAsRoles=:searchAsRoles?&limit=:limit?&startKey=:startKey?&query=:query?&search=:search?&sort=:sort?', @@ -778,6 +781,10 @@ const cfg = { return generatePath(cfg.api.activeAndPendingSessionsPath, { clusterId }); }, + getSessionDurationUrl(clusterId: string, sid: string) { + return generatePath(cfg.api.sessionDurationPath, { clusterId, sid }); + }, + getUnifiedResourcesUrl(clusterId: string, params: UrlResourcesParams) { return generateResourcePath(cfg.api.unifiedResourcesPath, { clusterId, diff --git a/web/packages/teleport/src/features.tsx b/web/packages/teleport/src/features.tsx index 7c26132fcf600..5f8f4d41eadef 100644 --- a/web/packages/teleport/src/features.tsx +++ b/web/packages/teleport/src/features.tsx @@ -569,7 +569,7 @@ export class FeatureClusters implements TeleportFeature { }; hasAccess(flags: FeatureFlags) { - return flags.trustedClusters; + return cfg.isDashboard || flags.trustedClusters; } navigationItem = { diff --git a/web/packages/teleport/src/lib/term/tty.ts b/web/packages/teleport/src/lib/term/tty.ts index 3e924ff466f3f..7c50a38ee9a38 100644 --- a/web/packages/teleport/src/lib/term/tty.ts +++ b/web/packages/teleport/src/lib/term/tty.ts @@ -82,7 +82,7 @@ class Tty extends EventEmitterMfaSender { this.socket.send(bytearray.buffer); } - sendChallengeResponse(data: MfaChallengeResponse) { + sendChallengeResponse(resp: MfaChallengeResponse) { // we want to have the backend listen on a single message type // for any responses. so our data will look like data.webauthn, data.sso, etc // but to be backward compatible, we need to still spread the existing webauthn only fields @@ -90,8 +90,8 @@ class Tty extends EventEmitterMfaSender { // in 19, we can just pass "data" without this extra step // TODO (avatus): DELETE IN 18 const backwardCompatibleData = { - ...data.webauthn_response, - ...data, + ...resp?.webauthn_response, + ...resp, }; const encoded = this._proto.encodeChallengeResponse( JSON.stringify(backwardCompatibleData) diff --git a/web/packages/teleport/src/services/audit/makeEvent.ts b/web/packages/teleport/src/services/audit/makeEvent.ts index 4095ee10fa604..ff1ae2a166e56 100644 --- a/web/packages/teleport/src/services/audit/makeEvent.ts +++ b/web/packages/teleport/src/services/audit/makeEvent.ts @@ -1900,6 +1900,20 @@ export const formatters: Formatters = { return `User [${user}] deleted a plugin [${name}]`; }, }, + [eventCodes.CONTACT_CREATE]: { + type: 'contact.create', + desc: 'Contact Created', + format: ({ user, email, contact_type }) => { + return `User [${user}] created a [${contactTypeStr(contact_type)}] contact [${email}]`; + }, + }, + [eventCodes.CONTACT_DELETE]: { + type: 'contact.delete', + desc: 'Contact Deleted', + format: ({ user, email, contact_type }) => { + return `User [${user}] deleted a [${contactTypeStr(contact_type)}] contact [${email}]`; + }, + }, [eventCodes.UNKNOWN]: { type: 'unknown', desc: 'Unknown Event', @@ -1951,3 +1965,14 @@ function formatMembers(members: { member_name: string }[]) { return `${pluralize(memberNames.length, 'member')} [${memberNamesJoined}]`; } + +function contactTypeStr(type: number): string { + switch (type) { + case 1: + return 'Business'; + case 2: + return 'Security'; + default: + return `Unknown type: ${type}`; + } +} diff --git a/web/packages/teleport/src/services/audit/types.ts b/web/packages/teleport/src/services/audit/types.ts index 066c4116b72c8..c50b27a782df2 100644 --- a/web/packages/teleport/src/services/audit/types.ts +++ b/web/packages/teleport/src/services/audit/types.ts @@ -308,6 +308,8 @@ export const eventCodes = { PLUGIN_CREATE: 'PG001I', PLUGIN_UPDATE: 'PG002I', PLUGIN_DELETE: 'PG003I', + CONTACT_CREATE: 'TCTC001I', + CONTACT_DELETE: 'TCTC002I', } as const; /** @@ -1746,6 +1748,20 @@ export type RawEvents = { server_hostname: string; } >; + [eventCodes.CONTACT_CREATE]: RawEvent< + typeof eventCodes.CONTACT_CREATE, + { + email: string; + contact_type: number; + } + >; + [eventCodes.CONTACT_DELETE]: RawEvent< + typeof eventCodes.CONTACT_DELETE, + { + email: string; + contact_type: number; + } + >; }; /** diff --git a/web/packages/teleport/src/services/auth/auth.ts b/web/packages/teleport/src/services/auth/auth.ts index 3724f1dc8b056..e1533ee4d3297 100644 --- a/web/packages/teleport/src/services/auth/auth.ts +++ b/web/packages/teleport/src/services/auth/auth.ts @@ -239,7 +239,7 @@ const auth = { .then(res => { const request = { action: 'accept', - webauthnAssertionResponse: res.webauthn_response, + webauthnAssertionResponse: res?.webauthn_response, }; return api.put(cfg.getHeadlessSsoPath(transactionId), request); @@ -280,7 +280,9 @@ const auth = { challenge: MfaAuthenticateChallenge, mfaType?: DeviceType, totpCode?: string - ): Promise { + ): Promise { + if (!challenge) return; + // TODO(Joerger): If mfaType is not provided by a parent component, use some global context // to display a component, similar to the one used in useMfa. For now we just default to // whichever method we can succeed with first. @@ -303,7 +305,7 @@ const auth = { } // No viable challenge, return empty response. - return null; + return; }, async getWebAuthnChallengeResponse( @@ -387,7 +389,7 @@ const auth = { return auth .getMfaChallenge({ scope, allowReuse, isMfaRequiredRequest }, abortSignal) .then(challenge => auth.getMfaChallengeResponse(challenge, 'webauthn')) - .then(res => res.webauthn_response); + .then(res => res?.webauthn_response); }, getMfaChallengeResponseForAdminAction(allowReuse?: boolean) { diff --git a/web/packages/teleport/src/services/mfa/makeMfa.ts b/web/packages/teleport/src/services/mfa/makeMfa.ts index 505a972fe33e5..4d98503dafa87 100644 --- a/web/packages/teleport/src/services/mfa/makeMfa.ts +++ b/web/packages/teleport/src/services/mfa/makeMfa.ts @@ -63,13 +63,13 @@ export function parseMfaRegistrationChallengeJson( // parseMfaChallengeJson formats fetched authenticate challenge JSON. export function parseMfaChallengeJson( challenge: MfaAuthenticateChallengeJson -): MfaAuthenticateChallenge { +): MfaAuthenticateChallenge | undefined { if ( !challenge.sso_challenge && !challenge.webauthn_challenge && !challenge.totp_challenge ) { - return null; + return; } // WebAuthn challenge contains Base64URL(byte) fields that needs to diff --git a/web/packages/teleport/src/services/mfa/types.ts b/web/packages/teleport/src/services/mfa/types.ts index f1292c50c99cd..382d7831f82fe 100644 --- a/web/packages/teleport/src/services/mfa/types.ts +++ b/web/packages/teleport/src/services/mfa/types.ts @@ -18,8 +18,7 @@ import { AuthProviderType } from 'shared/services'; -import { Base64urlString } from '../auth/types'; -import { CreateNewHardwareDeviceRequest } from '../auth/types'; +import { Base64urlString, CreateNewHardwareDeviceRequest } from '../auth/types'; export type DeviceType = 'totp' | 'webauthn' | 'sso'; diff --git a/web/packages/teleport/src/services/recordings/recordings.ts b/web/packages/teleport/src/services/recordings/recordings.ts index e27ca67beea03..ba71160aa1795 100644 --- a/web/packages/teleport/src/services/recordings/recordings.ts +++ b/web/packages/teleport/src/services/recordings/recordings.ts @@ -45,4 +45,11 @@ export default class RecordingsService { return { recordings: events.map(makeRecording), startKey: json.startKey }; }); } + + fetchRecordingDuration( + clusterId: string, + sessionId: string + ): Promise<{ durationMs: number }> { + return api.get(cfg.getSessionDurationUrl(clusterId, sessionId)); + } }