diff --git a/api/client/client.go b/api/client/client.go index 407137d6c6444..cb30874816607 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -3076,12 +3076,32 @@ func (c *Client) GetResources(ctx context.Context, req *proto.ListResourcesReque return resp, trail.FromGRPC(err) } +// ListUnifiedResources returns a paginated list of unified resources that the user has access to. +// `nextKey` is used as `startKey` in another call to ListUnifiedResources to retrieve +// the next page. +// It will return a `trace.LimitExceeded` error if the page exceeds gRPC max +// message size. +func (c *Client) ListUnifiedResources(ctx context.Context, req *proto.ListUnifiedResourcesRequest) (*proto.ListUnifiedResourcesResponse, error) { + if err := req.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + + resp, err := c.grpc.ListUnifiedResources(ctx, req) + return resp, trail.FromGRPC(err) +} + // GetResourcesClient is an interface used by GetResources to abstract over implementations of // the ListResources method. type GetResourcesClient interface { GetResources(ctx context.Context, req *proto.ListResourcesRequest) (*proto.ListResourcesResponse, error) } +// ListUnifiedResourcesClient is an interface used by ListUnifiedResources to abstract over implementations of +// the ListUnifiedResources method. +type ListUnifiedResourcesClient interface { + ListUnifiedResources(ctx context.Context, req *proto.ListUnifiedResourcesRequest) (*proto.ListUnifiedResourcesResponse, error) +} + // ResourcePage holds a page of results from [GetResourcePage]. type ResourcePage[T types.ResourceWithLabels] struct { // Resources retrieved for a single [proto.ListResourcesRequest]. The length of @@ -3094,6 +3114,86 @@ type ResourcePage[T types.ResourceWithLabels] struct { NextKey string } +// getResourceFromProtoPage extracts the resource from the PaginatedResource returned +// from the rpc ListUnifiedResources +func getResourceFromProtoPage(resource *proto.PaginatedResource) (types.ResourceWithLabels, error) { + var out types.ResourceWithLabels + if r := resource.GetNode(); r != nil { + out = r + return out, nil + } else if r := resource.GetDatabaseServer(); r != nil { + out = r + return out, nil + } else if r := resource.GetDatabaseService(); r != nil { + out = r + return out, nil + } else if r := resource.GetAppServerOrSAMLIdPServiceProvider(); r != nil { + out = r + return out, nil + } else if r := resource.GetWindowsDesktop(); r != nil { + out = r + return out, nil + } else if r := resource.GetWindowsDesktopService(); r != nil { + out = r + return out, nil + } else if r := resource.GetKubeCluster(); r != nil { + out = r + return out, nil + } else if r := resource.GetKubernetesServer(); r != nil { + out = r + return out, nil + } else if r := resource.GetUserGroup(); r != nil { + out = r + return out, nil + } else if r := resource.GetAppServer(); r != nil { + out = r + return out, nil + } else { + return nil, trace.BadParameter("received unsupported resource %T", resource.Resource) + } +} + +// ListUnifiedResourcePage is a helper for getting a single page of unified resources that match the provided request. +func ListUnifiedResourcePage(ctx context.Context, clt ListUnifiedResourcesClient, req *proto.ListUnifiedResourcesRequest) (ResourcePage[types.ResourceWithLabels], error) { + var out ResourcePage[types.ResourceWithLabels] + + // Set the limit to the default size if one was not provided within + // an acceptable range. + if req.Limit == 0 || req.Limit > int32(defaults.DefaultChunkSize) { + req.Limit = int32(defaults.DefaultChunkSize) + } + + for { + resp, err := clt.ListUnifiedResources(ctx, req) + if err != nil { + if trace.IsLimitExceeded(err) { + // Cut chunkSize in half if gRPC max message size is exceeded. + req.Limit /= 2 + // This is an extremely unlikely scenario, but better to cover it anyways. + if req.Limit == 0 { + return out, trace.Wrap(trail.FromGRPC(err), "resource is too large to retrieve") + } + + continue + } + + return out, trail.FromGRPC(err) + } + + for _, respResource := range resp.Resources { + resource, err := getResourceFromProtoPage(respResource) + if err != nil { + return out, trace.Wrap(err) + } + out.Resources = append(out.Resources, resource) + } + + out.NextKey = resp.NextKey + + return out, nil + } +} + // GetResourcePage is a helper for getting a single page of resources that match the provide request. func GetResourcePage[T types.ResourceWithLabels](ctx context.Context, clt GetResourcesClient, req *proto.ListResourcesRequest) (ResourcePage[T], error) { var out ResourcePage[T] diff --git a/api/client/proto/authservice.pb.go b/api/client/proto/authservice.pb.go index dac0da6c2aee7..20336e8c5ae75 100644 --- a/api/client/proto/authservice.pb.go +++ b/api/client/proto/authservice.pb.go @@ -10284,6 +10284,201 @@ func (*PaginatedResource) XXX_OneofWrappers() []interface{} { } } +// ListUnifiedResourcesRequest is a request to receive a paginated list of unified resources +type ListUnifiedResourcesRequest struct { + // Kinds is a list of kinds to match against a resource's kind. This can be used in a + // unified resource request that can include multiple types. + Kinds []string `protobuf:"bytes,1,rep,name=Kinds,proto3" json:"kinds,omitempty"` + // Limit is the maximum amount of resources to retrieve. + Limit int32 `protobuf:"varint,2,opt,name=Limit,proto3" json:"limit,omitempty"` + // StartKey is used to start listing resources from a specific spot. It + // should be set to the previous NextKey value if using pagination, or + // left empty. + StartKey string `protobuf:"bytes,3,opt,name=StartKey,proto3" json:"start_key,omitempty"` + // Labels is a label-based matcher if non-empty. + Labels map[string]string `protobuf:"bytes,4,rep,name=Labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // PredicateExpression defines boolean conditions that will be matched against the resource. + PredicateExpression string `protobuf:"bytes,5,opt,name=PredicateExpression,proto3" json:"predicate_expression,omitempty"` + // SearchKeywords is a list of search keywords to match against resource field values. + SearchKeywords []string `protobuf:"bytes,6,rep,name=SearchKeywords,proto3" json:"search_keywords,omitempty"` + // SortBy describes which resource field and which direction to sort by. + SortBy types.SortBy `protobuf:"bytes,7,opt,name=SortBy,proto3" json:"sort_by,omitempty"` + // WindowsDesktopFilter specifies windows desktop specific filters. + WindowsDesktopFilter types.WindowsDesktopFilter `protobuf:"bytes,8,opt,name=WindowsDesktopFilter,proto3" json:"windows_desktop_filter,omitempty"` + // UseSearchAsRoles indicates that the response should include all resources + // the caller is able to request access to using search_as_roles + UseSearchAsRoles bool `protobuf:"varint,9,opt,name=UseSearchAsRoles,proto3" json:"use_search_as_roles,omitempty"` + // UsePreviewAsRoles indicates that the response should include all resources + // the caller would be able to access with their preview_as_roles + UsePreviewAsRoles bool `protobuf:"varint,10,opt,name=UsePreviewAsRoles,proto3" json:"use_preview_as_roles,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListUnifiedResourcesRequest) Reset() { *m = ListUnifiedResourcesRequest{} } +func (m *ListUnifiedResourcesRequest) String() string { return proto.CompactTextString(m) } +func (*ListUnifiedResourcesRequest) ProtoMessage() {} +func (*ListUnifiedResourcesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0ffcffcda38ae159, []int{152} +} +func (m *ListUnifiedResourcesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListUnifiedResourcesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListUnifiedResourcesRequest.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 *ListUnifiedResourcesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListUnifiedResourcesRequest.Merge(m, src) +} +func (m *ListUnifiedResourcesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListUnifiedResourcesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListUnifiedResourcesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListUnifiedResourcesRequest proto.InternalMessageInfo + +func (m *ListUnifiedResourcesRequest) GetKinds() []string { + if m != nil { + return m.Kinds + } + return nil +} + +func (m *ListUnifiedResourcesRequest) GetLimit() int32 { + if m != nil { + return m.Limit + } + return 0 +} + +func (m *ListUnifiedResourcesRequest) GetStartKey() string { + if m != nil { + return m.StartKey + } + return "" +} + +func (m *ListUnifiedResourcesRequest) GetLabels() map[string]string { + if m != nil { + return m.Labels + } + return nil +} + +func (m *ListUnifiedResourcesRequest) GetPredicateExpression() string { + if m != nil { + return m.PredicateExpression + } + return "" +} + +func (m *ListUnifiedResourcesRequest) GetSearchKeywords() []string { + if m != nil { + return m.SearchKeywords + } + return nil +} + +func (m *ListUnifiedResourcesRequest) GetSortBy() types.SortBy { + if m != nil { + return m.SortBy + } + return types.SortBy{} +} + +func (m *ListUnifiedResourcesRequest) GetWindowsDesktopFilter() types.WindowsDesktopFilter { + if m != nil { + return m.WindowsDesktopFilter + } + return types.WindowsDesktopFilter{} +} + +func (m *ListUnifiedResourcesRequest) GetUseSearchAsRoles() bool { + if m != nil { + return m.UseSearchAsRoles + } + return false +} + +func (m *ListUnifiedResourcesRequest) GetUsePreviewAsRoles() bool { + if m != nil { + return m.UsePreviewAsRoles + } + return false +} + +// ListUnifiedResourceResponse response of ListUnifiedResources. +type ListUnifiedResourcesResponse struct { + // Resources is a list of resource. + Resources []*PaginatedResource `protobuf:"bytes,1,rep,name=Resources,proto3" json:"resources,omitempty"` + // NextKey is the next Key to use as StartKey in a ListResourcesRequest to + // continue retrieving pages of resource. If NextKey is empty, there are no + // more pages. + NextKey string `protobuf:"bytes,2,opt,name=NextKey,proto3" json:"next_key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListUnifiedResourcesResponse) Reset() { *m = ListUnifiedResourcesResponse{} } +func (m *ListUnifiedResourcesResponse) String() string { return proto.CompactTextString(m) } +func (*ListUnifiedResourcesResponse) ProtoMessage() {} +func (*ListUnifiedResourcesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0ffcffcda38ae159, []int{153} +} +func (m *ListUnifiedResourcesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListUnifiedResourcesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListUnifiedResourcesResponse.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 *ListUnifiedResourcesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListUnifiedResourcesResponse.Merge(m, src) +} +func (m *ListUnifiedResourcesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListUnifiedResourcesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListUnifiedResourcesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListUnifiedResourcesResponse proto.InternalMessageInfo + +func (m *ListUnifiedResourcesResponse) GetResources() []*PaginatedResource { + if m != nil { + return m.Resources + } + return nil +} + +func (m *ListUnifiedResourcesResponse) GetNextKey() string { + if m != nil { + return m.NextKey + } + return "" +} + // ListResourcesRequest defines a request to retrieve resources paginated. Only // one type of resource can be retrieved per request. // @@ -10334,7 +10529,7 @@ func (m *ListResourcesRequest) Reset() { *m = ListResourcesRequest{} } func (m *ListResourcesRequest) String() string { return proto.CompactTextString(m) } func (*ListResourcesRequest) ProtoMessage() {} func (*ListResourcesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{152} + return fileDescriptor_0ffcffcda38ae159, []int{154} } func (m *ListResourcesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10466,7 +10661,7 @@ func (m *ListResourcesResponse) Reset() { *m = ListResourcesResponse{} } func (m *ListResourcesResponse) String() string { return proto.CompactTextString(m) } func (*ListResourcesResponse) ProtoMessage() {} func (*ListResourcesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{153} + return fileDescriptor_0ffcffcda38ae159, []int{155} } func (m *ListResourcesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10531,7 +10726,7 @@ func (m *CreateSessionTrackerRequest) Reset() { *m = CreateSessionTracke func (m *CreateSessionTrackerRequest) String() string { return proto.CompactTextString(m) } func (*CreateSessionTrackerRequest) ProtoMessage() {} func (*CreateSessionTrackerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{154} + return fileDescriptor_0ffcffcda38ae159, []int{156} } func (m *CreateSessionTrackerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10580,7 +10775,7 @@ func (m *GetSessionTrackerRequest) Reset() { *m = GetSessionTrackerReque func (m *GetSessionTrackerRequest) String() string { return proto.CompactTextString(m) } func (*GetSessionTrackerRequest) ProtoMessage() {} func (*GetSessionTrackerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{155} + return fileDescriptor_0ffcffcda38ae159, []int{157} } func (m *GetSessionTrackerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10629,7 +10824,7 @@ func (m *RemoveSessionTrackerRequest) Reset() { *m = RemoveSessionTracke func (m *RemoveSessionTrackerRequest) String() string { return proto.CompactTextString(m) } func (*RemoveSessionTrackerRequest) ProtoMessage() {} func (*RemoveSessionTrackerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{156} + return fileDescriptor_0ffcffcda38ae159, []int{158} } func (m *RemoveSessionTrackerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10677,7 +10872,7 @@ func (m *SessionTrackerUpdateState) Reset() { *m = SessionTrackerUpdateS func (m *SessionTrackerUpdateState) String() string { return proto.CompactTextString(m) } func (*SessionTrackerUpdateState) ProtoMessage() {} func (*SessionTrackerUpdateState) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{157} + return fileDescriptor_0ffcffcda38ae159, []int{159} } func (m *SessionTrackerUpdateState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10725,7 +10920,7 @@ func (m *SessionTrackerAddParticipant) Reset() { *m = SessionTrackerAddP func (m *SessionTrackerAddParticipant) String() string { return proto.CompactTextString(m) } func (*SessionTrackerAddParticipant) ProtoMessage() {} func (*SessionTrackerAddParticipant) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{158} + return fileDescriptor_0ffcffcda38ae159, []int{160} } func (m *SessionTrackerAddParticipant) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10773,7 +10968,7 @@ func (m *SessionTrackerRemoveParticipant) Reset() { *m = SessionTrackerR func (m *SessionTrackerRemoveParticipant) String() string { return proto.CompactTextString(m) } func (*SessionTrackerRemoveParticipant) ProtoMessage() {} func (*SessionTrackerRemoveParticipant) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{159} + return fileDescriptor_0ffcffcda38ae159, []int{161} } func (m *SessionTrackerRemoveParticipant) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10822,7 +11017,7 @@ func (m *SessionTrackerUpdateExpiry) Reset() { *m = SessionTrackerUpdate func (m *SessionTrackerUpdateExpiry) String() string { return proto.CompactTextString(m) } func (*SessionTrackerUpdateExpiry) ProtoMessage() {} func (*SessionTrackerUpdateExpiry) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{160} + return fileDescriptor_0ffcffcda38ae159, []int{162} } func (m *SessionTrackerUpdateExpiry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10877,7 +11072,7 @@ func (m *UpdateSessionTrackerRequest) Reset() { *m = UpdateSessionTracke func (m *UpdateSessionTrackerRequest) String() string { return proto.CompactTextString(m) } func (*UpdateSessionTrackerRequest) ProtoMessage() {} func (*UpdateSessionTrackerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{161} + return fileDescriptor_0ffcffcda38ae159, []int{163} } func (m *UpdateSessionTrackerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -10995,7 +11190,7 @@ func (m *PresenceMFAChallengeRequest) Reset() { *m = PresenceMFAChalleng func (m *PresenceMFAChallengeRequest) String() string { return proto.CompactTextString(m) } func (*PresenceMFAChallengeRequest) ProtoMessage() {} func (*PresenceMFAChallengeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{162} + return fileDescriptor_0ffcffcda38ae159, []int{164} } func (m *PresenceMFAChallengeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11046,7 +11241,7 @@ func (m *PresenceMFAChallengeSend) Reset() { *m = PresenceMFAChallengeSe func (m *PresenceMFAChallengeSend) String() string { return proto.CompactTextString(m) } func (*PresenceMFAChallengeSend) ProtoMessage() {} func (*PresenceMFAChallengeSend) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{163} + return fileDescriptor_0ffcffcda38ae159, []int{165} } func (m *PresenceMFAChallengeSend) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11133,7 +11328,7 @@ func (m *GetDomainNameResponse) Reset() { *m = GetDomainNameResponse{} } func (m *GetDomainNameResponse) String() string { return proto.CompactTextString(m) } func (*GetDomainNameResponse) ProtoMessage() {} func (*GetDomainNameResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{164} + return fileDescriptor_0ffcffcda38ae159, []int{166} } func (m *GetDomainNameResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11182,7 +11377,7 @@ func (m *GetClusterCACertResponse) Reset() { *m = GetClusterCACertRespon func (m *GetClusterCACertResponse) String() string { return proto.CompactTextString(m) } func (*GetClusterCACertResponse) ProtoMessage() {} func (*GetClusterCACertResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{165} + return fileDescriptor_0ffcffcda38ae159, []int{167} } func (m *GetClusterCACertResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11230,7 +11425,7 @@ func (m *GetLicenseResponse) Reset() { *m = GetLicenseResponse{} } func (m *GetLicenseResponse) String() string { return proto.CompactTextString(m) } func (*GetLicenseResponse) ProtoMessage() {} func (*GetLicenseResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{166} + return fileDescriptor_0ffcffcda38ae159, []int{168} } func (m *GetLicenseResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11278,7 +11473,7 @@ func (m *ListReleasesResponse) Reset() { *m = ListReleasesResponse{} } func (m *ListReleasesResponse) String() string { return proto.CompactTextString(m) } func (*ListReleasesResponse) ProtoMessage() {} func (*ListReleasesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{167} + return fileDescriptor_0ffcffcda38ae159, []int{169} } func (m *ListReleasesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11327,7 +11522,7 @@ func (m *GetOIDCAuthRequestRequest) Reset() { *m = GetOIDCAuthRequestReq func (m *GetOIDCAuthRequestRequest) String() string { return proto.CompactTextString(m) } func (*GetOIDCAuthRequestRequest) ProtoMessage() {} func (*GetOIDCAuthRequestRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{168} + return fileDescriptor_0ffcffcda38ae159, []int{170} } func (m *GetOIDCAuthRequestRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11376,7 +11571,7 @@ func (m *GetSAMLAuthRequestRequest) Reset() { *m = GetSAMLAuthRequestReq func (m *GetSAMLAuthRequestRequest) String() string { return proto.CompactTextString(m) } func (*GetSAMLAuthRequestRequest) ProtoMessage() {} func (*GetSAMLAuthRequestRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{169} + return fileDescriptor_0ffcffcda38ae159, []int{171} } func (m *GetSAMLAuthRequestRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11425,7 +11620,7 @@ func (m *GetGithubAuthRequestRequest) Reset() { *m = GetGithubAuthReques func (m *GetGithubAuthRequestRequest) String() string { return proto.CompactTextString(m) } func (*GetGithubAuthRequestRequest) ProtoMessage() {} func (*GetGithubAuthRequestRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{170} + return fileDescriptor_0ffcffcda38ae159, []int{172} } func (m *GetGithubAuthRequestRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11476,7 +11671,7 @@ func (m *GetSSODiagnosticInfoRequest) Reset() { *m = GetSSODiagnosticInf func (m *GetSSODiagnosticInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetSSODiagnosticInfoRequest) ProtoMessage() {} func (*GetSSODiagnosticInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{171} + return fileDescriptor_0ffcffcda38ae159, []int{173} } func (m *GetSSODiagnosticInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11537,7 +11732,7 @@ func (m *UpstreamInventoryOneOf) Reset() { *m = UpstreamInventoryOneOf{} func (m *UpstreamInventoryOneOf) String() string { return proto.CompactTextString(m) } func (*UpstreamInventoryOneOf) ProtoMessage() {} func (*UpstreamInventoryOneOf) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{172} + return fileDescriptor_0ffcffcda38ae159, []int{174} } func (m *UpstreamInventoryOneOf) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11652,7 +11847,7 @@ func (m *DownstreamInventoryOneOf) Reset() { *m = DownstreamInventoryOne func (m *DownstreamInventoryOneOf) String() string { return proto.CompactTextString(m) } func (*DownstreamInventoryOneOf) ProtoMessage() {} func (*DownstreamInventoryOneOf) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{173} + return fileDescriptor_0ffcffcda38ae159, []int{175} } func (m *DownstreamInventoryOneOf) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11751,7 +11946,7 @@ func (m *DownstreamInventoryPing) Reset() { *m = DownstreamInventoryPing func (m *DownstreamInventoryPing) String() string { return proto.CompactTextString(m) } func (*DownstreamInventoryPing) ProtoMessage() {} func (*DownstreamInventoryPing) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{174} + return fileDescriptor_0ffcffcda38ae159, []int{176} } func (m *DownstreamInventoryPing) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11800,7 +11995,7 @@ func (m *UpstreamInventoryPong) Reset() { *m = UpstreamInventoryPong{} } func (m *UpstreamInventoryPong) String() string { return proto.CompactTextString(m) } func (*UpstreamInventoryPong) ProtoMessage() {} func (*UpstreamInventoryPong) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{175} + return fileDescriptor_0ffcffcda38ae159, []int{177} } func (m *UpstreamInventoryPong) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11865,7 +12060,7 @@ func (m *UpstreamInventoryHello) Reset() { *m = UpstreamInventoryHello{} func (m *UpstreamInventoryHello) String() string { return proto.CompactTextString(m) } func (*UpstreamInventoryHello) ProtoMessage() {} func (*UpstreamInventoryHello) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{176} + return fileDescriptor_0ffcffcda38ae159, []int{178} } func (m *UpstreamInventoryHello) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -11958,7 +12153,7 @@ func (m *UpstreamInventoryAgentMetadata) Reset() { *m = UpstreamInventor func (m *UpstreamInventoryAgentMetadata) String() string { return proto.CompactTextString(m) } func (*UpstreamInventoryAgentMetadata) ProtoMessage() {} func (*UpstreamInventoryAgentMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{177} + return fileDescriptor_0ffcffcda38ae159, []int{179} } func (m *UpstreamInventoryAgentMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12058,7 +12253,7 @@ func (m *DownstreamInventoryHello) Reset() { *m = DownstreamInventoryHel func (m *DownstreamInventoryHello) String() string { return proto.CompactTextString(m) } func (*DownstreamInventoryHello) ProtoMessage() {} func (*DownstreamInventoryHello) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{178} + return fileDescriptor_0ffcffcda38ae159, []int{180} } func (m *DownstreamInventoryHello) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12119,7 +12314,7 @@ func (m *InventoryUpdateLabelsRequest) Reset() { *m = InventoryUpdateLab func (m *InventoryUpdateLabelsRequest) String() string { return proto.CompactTextString(m) } func (*InventoryUpdateLabelsRequest) ProtoMessage() {} func (*InventoryUpdateLabelsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{179} + return fileDescriptor_0ffcffcda38ae159, []int{181} } func (m *InventoryUpdateLabelsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12185,7 +12380,7 @@ func (m *DownstreamInventoryUpdateLabels) Reset() { *m = DownstreamInven func (m *DownstreamInventoryUpdateLabels) String() string { return proto.CompactTextString(m) } func (*DownstreamInventoryUpdateLabels) ProtoMessage() {} func (*DownstreamInventoryUpdateLabels) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{180} + return fileDescriptor_0ffcffcda38ae159, []int{182} } func (m *DownstreamInventoryUpdateLabels) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12244,7 +12439,7 @@ func (m *InventoryHeartbeat) Reset() { *m = InventoryHeartbeat{} } func (m *InventoryHeartbeat) String() string { return proto.CompactTextString(m) } func (*InventoryHeartbeat) ProtoMessage() {} func (*InventoryHeartbeat) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{181} + return fileDescriptor_0ffcffcda38ae159, []int{183} } func (m *InventoryHeartbeat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12294,7 +12489,7 @@ func (m *InventoryStatusRequest) Reset() { *m = InventoryStatusRequest{} func (m *InventoryStatusRequest) String() string { return proto.CompactTextString(m) } func (*InventoryStatusRequest) ProtoMessage() {} func (*InventoryStatusRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{182} + return fileDescriptor_0ffcffcda38ae159, []int{184} } func (m *InventoryStatusRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12352,7 +12547,7 @@ func (m *InventoryStatusSummary) Reset() { *m = InventoryStatusSummary{} func (m *InventoryStatusSummary) String() string { return proto.CompactTextString(m) } func (*InventoryStatusSummary) ProtoMessage() {} func (*InventoryStatusSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{183} + return fileDescriptor_0ffcffcda38ae159, []int{185} } func (m *InventoryStatusSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12429,7 +12624,7 @@ func (m *InventoryConnectedServiceCountsRequest) Reset() { func (m *InventoryConnectedServiceCountsRequest) String() string { return proto.CompactTextString(m) } func (*InventoryConnectedServiceCountsRequest) ProtoMessage() {} func (*InventoryConnectedServiceCountsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{184} + return fileDescriptor_0ffcffcda38ae159, []int{186} } func (m *InventoryConnectedServiceCountsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12471,7 +12666,7 @@ func (m *InventoryConnectedServiceCounts) Reset() { *m = InventoryConnec func (m *InventoryConnectedServiceCounts) String() string { return proto.CompactTextString(m) } func (*InventoryConnectedServiceCounts) ProtoMessage() {} func (*InventoryConnectedServiceCounts) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{185} + return fileDescriptor_0ffcffcda38ae159, []int{187} } func (m *InventoryConnectedServiceCounts) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12525,7 +12720,7 @@ func (m *InventoryPingRequest) Reset() { *m = InventoryPingRequest{} } func (m *InventoryPingRequest) String() string { return proto.CompactTextString(m) } func (*InventoryPingRequest) ProtoMessage() {} func (*InventoryPingRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{186} + return fileDescriptor_0ffcffcda38ae159, []int{188} } func (m *InventoryPingRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12581,7 +12776,7 @@ func (m *InventoryPingResponse) Reset() { *m = InventoryPingResponse{} } func (m *InventoryPingResponse) String() string { return proto.CompactTextString(m) } func (*InventoryPingResponse) ProtoMessage() {} func (*InventoryPingResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{187} + return fileDescriptor_0ffcffcda38ae159, []int{189} } func (m *InventoryPingResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12630,7 +12825,7 @@ func (m *GetClusterAlertsResponse) Reset() { *m = GetClusterAlertsRespon func (m *GetClusterAlertsResponse) String() string { return proto.CompactTextString(m) } func (*GetClusterAlertsResponse) ProtoMessage() {} func (*GetClusterAlertsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{188} + return fileDescriptor_0ffcffcda38ae159, []int{190} } func (m *GetClusterAlertsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12677,7 +12872,7 @@ func (m *GetAlertAcksRequest) Reset() { *m = GetAlertAcksRequest{} } func (m *GetAlertAcksRequest) String() string { return proto.CompactTextString(m) } func (*GetAlertAcksRequest) ProtoMessage() {} func (*GetAlertAcksRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{189} + return fileDescriptor_0ffcffcda38ae159, []int{191} } func (m *GetAlertAcksRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12719,7 +12914,7 @@ func (m *GetAlertAcksResponse) Reset() { *m = GetAlertAcksResponse{} } func (m *GetAlertAcksResponse) String() string { return proto.CompactTextString(m) } func (*GetAlertAcksResponse) ProtoMessage() {} func (*GetAlertAcksResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{190} + return fileDescriptor_0ffcffcda38ae159, []int{192} } func (m *GetAlertAcksResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12769,7 +12964,7 @@ func (m *ClearAlertAcksRequest) Reset() { *m = ClearAlertAcksRequest{} } func (m *ClearAlertAcksRequest) String() string { return proto.CompactTextString(m) } func (*ClearAlertAcksRequest) ProtoMessage() {} func (*ClearAlertAcksRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{191} + return fileDescriptor_0ffcffcda38ae159, []int{193} } func (m *ClearAlertAcksRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12818,7 +13013,7 @@ func (m *UpsertClusterAlertRequest) Reset() { *m = UpsertClusterAlertReq func (m *UpsertClusterAlertRequest) String() string { return proto.CompactTextString(m) } func (*UpsertClusterAlertRequest) ProtoMessage() {} func (*UpsertClusterAlertRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{192} + return fileDescriptor_0ffcffcda38ae159, []int{194} } func (m *UpsertClusterAlertRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12867,7 +13062,7 @@ func (m *GetConnectionDiagnosticRequest) Reset() { *m = GetConnectionDia func (m *GetConnectionDiagnosticRequest) String() string { return proto.CompactTextString(m) } func (*GetConnectionDiagnosticRequest) ProtoMessage() {} func (*GetConnectionDiagnosticRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{193} + return fileDescriptor_0ffcffcda38ae159, []int{195} } func (m *GetConnectionDiagnosticRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12918,7 +13113,7 @@ func (m *AppendDiagnosticTraceRequest) Reset() { *m = AppendDiagnosticTr func (m *AppendDiagnosticTraceRequest) String() string { return proto.CompactTextString(m) } func (*AppendDiagnosticTraceRequest) ProtoMessage() {} func (*AppendDiagnosticTraceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{194} + return fileDescriptor_0ffcffcda38ae159, []int{196} } func (m *AppendDiagnosticTraceRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -12973,7 +13168,7 @@ func (m *SubmitUsageEventRequest) Reset() { *m = SubmitUsageEventRequest func (m *SubmitUsageEventRequest) String() string { return proto.CompactTextString(m) } func (*SubmitUsageEventRequest) ProtoMessage() {} func (*SubmitUsageEventRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{195} + return fileDescriptor_0ffcffcda38ae159, []int{197} } func (m *SubmitUsageEventRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13020,7 +13215,7 @@ func (m *GetLicenseRequest) Reset() { *m = GetLicenseRequest{} } func (m *GetLicenseRequest) String() string { return proto.CompactTextString(m) } func (*GetLicenseRequest) ProtoMessage() {} func (*GetLicenseRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{196} + return fileDescriptor_0ffcffcda38ae159, []int{198} } func (m *GetLicenseRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13060,7 +13255,7 @@ func (m *ListReleasesRequest) Reset() { *m = ListReleasesRequest{} } func (m *ListReleasesRequest) String() string { return proto.CompactTextString(m) } func (*ListReleasesRequest) ProtoMessage() {} func (*ListReleasesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{197} + return fileDescriptor_0ffcffcda38ae159, []int{199} } func (m *ListReleasesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13104,7 +13299,7 @@ func (m *CreateTokenV2Request) Reset() { *m = CreateTokenV2Request{} } func (m *CreateTokenV2Request) String() string { return proto.CompactTextString(m) } func (*CreateTokenV2Request) ProtoMessage() {} func (*CreateTokenV2Request) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{198} + return fileDescriptor_0ffcffcda38ae159, []int{200} } func (m *CreateTokenV2Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13181,7 +13376,7 @@ func (m *UpsertTokenV2Request) Reset() { *m = UpsertTokenV2Request{} } func (m *UpsertTokenV2Request) String() string { return proto.CompactTextString(m) } func (*UpsertTokenV2Request) ProtoMessage() {} func (*UpsertTokenV2Request) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{199} + return fileDescriptor_0ffcffcda38ae159, []int{201} } func (m *UpsertTokenV2Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13256,7 +13451,7 @@ func (m *GetHeadlessAuthenticationRequest) Reset() { *m = GetHeadlessAut func (m *GetHeadlessAuthenticationRequest) String() string { return proto.CompactTextString(m) } func (*GetHeadlessAuthenticationRequest) ProtoMessage() {} func (*GetHeadlessAuthenticationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{200} + return fileDescriptor_0ffcffcda38ae159, []int{202} } func (m *GetHeadlessAuthenticationRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13314,7 +13509,7 @@ func (m *UpdateHeadlessAuthenticationStateRequest) Reset() { func (m *UpdateHeadlessAuthenticationStateRequest) String() string { return proto.CompactTextString(m) } func (*UpdateHeadlessAuthenticationStateRequest) ProtoMessage() {} func (*UpdateHeadlessAuthenticationStateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{201} + return fileDescriptor_0ffcffcda38ae159, []int{203} } func (m *UpdateHeadlessAuthenticationStateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13380,7 +13575,7 @@ func (m *ExportUpgradeWindowsRequest) Reset() { *m = ExportUpgradeWindow func (m *ExportUpgradeWindowsRequest) String() string { return proto.CompactTextString(m) } func (*ExportUpgradeWindowsRequest) ProtoMessage() {} func (*ExportUpgradeWindowsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{202} + return fileDescriptor_0ffcffcda38ae159, []int{204} } func (m *ExportUpgradeWindowsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13446,7 +13641,7 @@ func (m *ExportUpgradeWindowsResponse) Reset() { *m = ExportUpgradeWindo func (m *ExportUpgradeWindowsResponse) String() string { return proto.CompactTextString(m) } func (*ExportUpgradeWindowsResponse) ProtoMessage() {} func (*ExportUpgradeWindowsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0ffcffcda38ae159, []int{203} + return fileDescriptor_0ffcffcda38ae159, []int{205} } func (m *ExportUpgradeWindowsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -13659,6 +13854,9 @@ func init() { proto.RegisterType((*CreatePrivilegeTokenRequest)(nil), "proto.CreatePrivilegeTokenRequest") proto.RegisterType((*CreateRegisterChallengeRequest)(nil), "proto.CreateRegisterChallengeRequest") proto.RegisterType((*PaginatedResource)(nil), "proto.PaginatedResource") + proto.RegisterType((*ListUnifiedResourcesRequest)(nil), "proto.ListUnifiedResourcesRequest") + proto.RegisterMapType((map[string]string)(nil), "proto.ListUnifiedResourcesRequest.LabelsEntry") + proto.RegisterType((*ListUnifiedResourcesResponse)(nil), "proto.ListUnifiedResourcesResponse") proto.RegisterType((*ListResourcesRequest)(nil), "proto.ListResourcesRequest") proto.RegisterMapType((map[string]string)(nil), "proto.ListResourcesRequest.LabelsEntry") proto.RegisterType((*ListResourcesResponse)(nil), "proto.ListResourcesResponse") @@ -13725,793 +13923,800 @@ func init() { } var fileDescriptor_0ffcffcda38ae159 = []byte{ - // 12572 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0xbd, 0x5d, 0x6c, 0x1c, 0x49, - 0x92, 0x18, 0xcc, 0x6e, 0x92, 0x22, 0x19, 0xfc, 0x6b, 0x25, 0x49, 0x91, 0x6a, 0x51, 0x6a, 0xa9, - 0x34, 0xd2, 0x68, 0xb4, 0x3b, 0xfa, 0xa1, 0x66, 0x66, 0xe7, 0x6f, 0x67, 0xb6, 0xbb, 0x49, 0x89, - 0x94, 0x28, 0x92, 0x53, 0xcd, 0x9f, 0xd9, 0xdd, 0xb9, 0xed, 0x2d, 0x76, 0xa7, 0xc8, 0xfa, 0xd4, - 0xac, 0xea, 0xab, 0x2a, 0x4a, 0xa3, 0x3d, 0xdc, 0x67, 0x9f, 0xcf, 0xbe, 0x07, 0x03, 0xf6, 0xed, - 0x01, 0x77, 0xc6, 0x1d, 0xee, 0xe1, 0x0c, 0xd8, 0x2f, 0x36, 0x60, 0xc0, 0x2f, 0x87, 0x7b, 0xb0, - 0x5f, 0x0c, 0x1b, 0xf0, 0xda, 0xc0, 0x01, 0x06, 0xee, 0x0e, 0x06, 0xfc, 0xc0, 0xf3, 0xed, 0x23, - 0x61, 0x1b, 0x30, 0x0c, 0xfb, 0x61, 0x01, 0x03, 0x46, 0xfe, 0x56, 0x66, 0x55, 0x56, 0x75, 0x53, - 0xa3, 0xdd, 0x17, 0x89, 0x9d, 0x19, 0x11, 0x99, 0x19, 0x99, 0x15, 0x19, 0x19, 0x19, 0x19, 0x01, - 0x77, 0x22, 0xdc, 0xc1, 0x5d, 0x3f, 0x88, 0xee, 0x76, 0xf0, 0x81, 0xd3, 0x7a, 0x75, 0xb7, 0xd5, - 0x71, 0xb1, 0x17, 0xdd, 0xed, 0x06, 0x7e, 0xe4, 0xdf, 0x75, 0x8e, 0xa3, 0xc3, 0x10, 0x07, 0x2f, - 0xdc, 0x16, 0xbe, 0x43, 0x4b, 0xd0, 0x30, 0xfd, 0xaf, 0x3c, 0x7b, 0xe0, 0x1f, 0xf8, 0x0c, 0x86, - 0xfc, 0xc5, 0x2a, 0xcb, 0x97, 0x0e, 0x7c, 0xff, 0xa0, 0x83, 0x19, 0xf2, 0xfe, 0xf1, 0xb3, 0xbb, - 0xf8, 0xa8, 0x1b, 0xbd, 0xe2, 0x95, 0x95, 0x64, 0x65, 0xe4, 0x1e, 0xe1, 0x30, 0x72, 0x8e, 0xba, - 0x1c, 0xe0, 0x1d, 0xd9, 0x15, 0x27, 0x8a, 0x48, 0x4d, 0xe4, 0xfa, 0xde, 0xdd, 0x17, 0xf7, 0xd5, - 0x9f, 0x1c, 0xf4, 0x56, 0x6e, 0xaf, 0x5b, 0x38, 0x88, 0xc2, 0xbe, 0x20, 0xf1, 0x0b, 0xec, 0x45, - 0xa9, 0xe6, 0x39, 0x64, 0xf4, 0xaa, 0x8b, 0x43, 0x06, 0x22, 0xfe, 0xe3, 0xa0, 0xd7, 0xcc, 0xa0, - 0xf4, 0x5f, 0x0e, 0xf2, 0xae, 0x19, 0xe4, 0x25, 0xde, 0x27, 0x3c, 0xf5, 0xe4, 0x1f, 0x3d, 0xc0, - 0x03, 0xa7, 0xdb, 0xc5, 0x41, 0xfc, 0x47, 0xaa, 0xaf, 0xc7, 0xa1, 0x73, 0x80, 0x79, 0x1f, 0x5f, - 0xdc, 0x57, 0x7f, 0x32, 0x50, 0xeb, 0x4f, 0x0a, 0x30, 0xbc, 0xe7, 0x44, 0xad, 0x43, 0xf4, 0x39, - 0x0c, 0x3f, 0x71, 0xbd, 0x76, 0xb8, 0x50, 0xb8, 0x3a, 0x78, 0x6b, 0x7c, 0xa9, 0x74, 0x87, 0xf5, - 0x97, 0x56, 0x92, 0x8a, 0xda, 0xfc, 0xcf, 0x4e, 0x2a, 0x03, 0xa7, 0x27, 0x95, 0xe9, 0xe7, 0x04, - 0xec, 0xdb, 0xfe, 0x91, 0x1b, 0xd1, 0x09, 0xb4, 0x19, 0x1e, 0xda, 0x81, 0x99, 0x6a, 0xa7, 0xe3, - 0xbf, 0xdc, 0x72, 0x82, 0xc8, 0x75, 0x3a, 0x8d, 0xe3, 0x56, 0x0b, 0x87, 0xe1, 0x42, 0xf1, 0x6a, - 0xe1, 0xd6, 0x68, 0xed, 0xfa, 0xe9, 0x49, 0xa5, 0xe2, 0x90, 0xea, 0x66, 0x97, 0xd5, 0x37, 0x43, - 0x06, 0xa0, 0x10, 0x32, 0xe1, 0x5b, 0xff, 0x6d, 0x18, 0x4a, 0xab, 0x7e, 0x18, 0xd5, 0xc9, 0xb4, - 0xd9, 0xf8, 0xd7, 0x8f, 0x71, 0x18, 0xa1, 0xeb, 0x70, 0x8e, 0x94, 0xad, 0x2d, 0x2f, 0x14, 0xae, - 0x16, 0x6e, 0x8d, 0xd5, 0xc6, 0x4f, 0x4f, 0x2a, 0x23, 0x87, 0x7e, 0x18, 0x35, 0xdd, 0xb6, 0xcd, - 0xab, 0xd0, 0x3b, 0x30, 0xba, 0xe1, 0xb7, 0xf1, 0x86, 0x73, 0x84, 0x69, 0x2f, 0xc6, 0x6a, 0x93, - 0xa7, 0x27, 0x95, 0x31, 0xcf, 0x6f, 0xe3, 0xa6, 0xe7, 0x1c, 0x61, 0x5b, 0x56, 0xa3, 0x5d, 0x18, - 0xb2, 0xfd, 0x0e, 0x5e, 0x18, 0xa4, 0x60, 0xb5, 0xd3, 0x93, 0xca, 0x50, 0xe0, 0x77, 0xf0, 0x2f, - 0x4e, 0x2a, 0x1f, 0x1c, 0xb8, 0xd1, 0xe1, 0xf1, 0xfe, 0x9d, 0x96, 0x7f, 0x74, 0xf7, 0x20, 0x70, - 0x5e, 0xb8, 0x6c, 0xa5, 0x39, 0x9d, 0xbb, 0xf1, 0x7a, 0xec, 0xba, 0x7c, 0x72, 0x1b, 0xaf, 0xc2, - 0x08, 0x1f, 0x11, 0x4a, 0x36, 0xa5, 0x87, 0xf6, 0x60, 0xb6, 0xda, 0x6e, 0xbb, 0x0c, 0x63, 0x2b, - 0x70, 0xbd, 0x96, 0xdb, 0x75, 0x3a, 0xe1, 0xc2, 0xd0, 0xd5, 0xc1, 0x5b, 0x63, 0x9c, 0x29, 0xb2, - 0xbe, 0xd9, 0x95, 0x00, 0x0a, 0x53, 0x8c, 0x04, 0xd0, 0x03, 0x18, 0x5d, 0xde, 0x68, 0x90, 0xbe, - 0x87, 0x0b, 0xc3, 0x94, 0xd8, 0xfc, 0xe9, 0x49, 0x65, 0xa6, 0xed, 0x85, 0x74, 0x68, 0x2a, 0x01, - 0x09, 0x88, 0x3e, 0x80, 0x89, 0xad, 0xe3, 0xfd, 0x8e, 0xdb, 0xda, 0x5e, 0x6f, 0x3c, 0xc1, 0xaf, - 0x16, 0xce, 0x5d, 0x2d, 0xdc, 0x9a, 0xa8, 0xa1, 0xd3, 0x93, 0xca, 0x54, 0x97, 0x96, 0x37, 0xa3, - 0x4e, 0xd8, 0x7c, 0x8e, 0x5f, 0xd9, 0x1a, 0x5c, 0x8c, 0xd7, 0x68, 0xac, 0x12, 0xbc, 0x91, 0x14, - 0x5e, 0x18, 0x1e, 0xaa, 0x78, 0x0c, 0x0e, 0xdd, 0x05, 0xb0, 0xf1, 0x91, 0x1f, 0xe1, 0x6a, 0xbb, - 0x1d, 0x2c, 0x8c, 0x52, 0xde, 0x4e, 0x9f, 0x9e, 0x54, 0xc6, 0x03, 0x5a, 0xda, 0x74, 0xda, 0xed, - 0xc0, 0x56, 0x40, 0x50, 0x1d, 0x46, 0x6d, 0x9f, 0x31, 0x78, 0x61, 0xec, 0x6a, 0xe1, 0xd6, 0xf8, - 0xd2, 0x34, 0x5f, 0x86, 0xa2, 0xb8, 0x76, 0xe1, 0xf4, 0xa4, 0x82, 0x02, 0xfe, 0x4b, 0x1d, 0xa5, - 0x80, 0x40, 0x15, 0x18, 0xd9, 0xf0, 0xeb, 0x4e, 0xeb, 0x10, 0x2f, 0x00, 0x5d, 0x7b, 0xc3, 0xa7, - 0x27, 0x95, 0xc2, 0xbb, 0xb6, 0x28, 0x45, 0x2f, 0x60, 0x3c, 0x9e, 0xa8, 0x70, 0x61, 0x9c, 0xb2, - 0x6f, 0xfb, 0xf4, 0xa4, 0x72, 0x21, 0xa4, 0xc5, 0x4d, 0x32, 0xf5, 0x0a, 0x07, 0xbf, 0xc1, 0x2a, - 0x50, 0x1b, 0x7a, 0x3c, 0x34, 0x3a, 0x51, 0x9a, 0xb4, 0x2f, 0xef, 0x78, 0x61, 0xe4, 0xec, 0x77, - 0x70, 0x5c, 0x55, 0x0d, 0x43, 0x1c, 0x10, 0x7a, 0x6b, 0xcb, 0xd6, 0xff, 0x2d, 0x00, 0xda, 0xec, - 0x62, 0xaf, 0xd1, 0x58, 0x25, 0x2b, 0x5e, 0x2c, 0xf8, 0x6f, 0xc3, 0x18, 0x63, 0x2d, 0xe1, 0x7f, - 0x91, 0xf2, 0x7f, 0xea, 0xf4, 0xa4, 0x02, 0x9c, 0xff, 0x84, 0xf7, 0x31, 0x00, 0xba, 0x01, 0x83, - 0xdb, 0xdb, 0xeb, 0x74, 0x35, 0x0f, 0xd6, 0x66, 0x4e, 0x4f, 0x2a, 0x83, 0x51, 0xd4, 0xf9, 0xc5, - 0x49, 0x65, 0x74, 0xf9, 0x38, 0xa0, 0x1d, 0xb7, 0x49, 0x3d, 0xba, 0x01, 0x23, 0xf5, 0xce, 0x71, - 0x18, 0xe1, 0x60, 0x61, 0x28, 0xfe, 0x8c, 0x5a, 0xac, 0xc8, 0x16, 0x75, 0xe8, 0x5b, 0x30, 0xb4, - 0x13, 0xe2, 0x60, 0x61, 0x98, 0xce, 0xc8, 0x24, 0x9f, 0x11, 0x52, 0xb4, 0xbb, 0x54, 0x1b, 0x25, - 0xdf, 0xca, 0x71, 0x88, 0x03, 0x9b, 0x02, 0xa1, 0x3b, 0x30, 0xcc, 0xd8, 0x7a, 0x8e, 0x8a, 0x91, - 0x49, 0x39, 0x7f, 0x1d, 0xbc, 0xfb, 0x41, 0x6d, 0xec, 0xf4, 0xa4, 0x32, 0x4c, 0xd9, 0x6b, 0x0f, - 0x0b, 0xa6, 0x14, 0x4a, 0x45, 0x7b, 0x94, 0xe0, 0x92, 0x85, 0x6b, 0x7d, 0x0b, 0xc6, 0x95, 0xe1, - 0xa3, 0x45, 0x18, 0x22, 0xff, 0xd3, 0xcf, 0x7c, 0x82, 0x35, 0x46, 0xe4, 0xb7, 0x4d, 0x4b, 0xad, - 0x3f, 0x9d, 0x84, 0x12, 0xc1, 0xd4, 0x64, 0x83, 0xc6, 0xaa, 0x42, 0x2f, 0x56, 0xdd, 0x02, 0xd9, - 0x36, 0x17, 0x12, 0x13, 0xa7, 0x27, 0x95, 0xd1, 0x63, 0x5e, 0x16, 0xf7, 0x0c, 0x35, 0x60, 0x64, - 0xe5, 0xeb, 0xae, 0x1b, 0xe0, 0x90, 0x32, 0x76, 0x7c, 0xa9, 0x7c, 0x87, 0xed, 0x59, 0x77, 0xc4, - 0x9e, 0x75, 0x67, 0x5b, 0xec, 0x59, 0xb5, 0xcb, 0x5c, 0x58, 0x9e, 0xc7, 0x0c, 0x25, 0x5e, 0x4d, - 0x3f, 0xfd, 0xeb, 0x4a, 0xc1, 0x16, 0x94, 0xd0, 0xb7, 0xe1, 0xdc, 0x43, 0x3f, 0x38, 0x72, 0x22, - 0x3e, 0x03, 0xb3, 0xa7, 0x27, 0x95, 0xd2, 0x33, 0x5a, 0xa2, 0x2c, 0x6e, 0x0e, 0x83, 0x1e, 0xc2, - 0x94, 0xed, 0x1f, 0x47, 0x78, 0xdb, 0x17, 0xf3, 0x36, 0x4c, 0xb1, 0xae, 0x9c, 0x9e, 0x54, 0xca, - 0x01, 0xa9, 0x69, 0x46, 0x7e, 0x93, 0x4f, 0xa0, 0x82, 0x9f, 0xc0, 0x42, 0x2b, 0x30, 0x55, 0xa5, - 0xd2, 0x95, 0xf3, 0x8c, 0xcd, 0xd6, 0x58, 0xed, 0xf2, 0xe9, 0x49, 0xe5, 0xa2, 0x43, 0x6b, 0x9a, - 0x01, 0xaf, 0x52, 0xc9, 0xe8, 0x48, 0x68, 0x03, 0xce, 0x3f, 0x39, 0xde, 0xc7, 0x81, 0x87, 0x23, - 0x1c, 0x8a, 0x1e, 0x8d, 0xd0, 0x1e, 0x5d, 0x3d, 0x3d, 0xa9, 0x2c, 0x3e, 0x97, 0x95, 0x86, 0x3e, - 0xa5, 0x51, 0x11, 0x86, 0x69, 0xde, 0xd1, 0x65, 0x27, 0x72, 0xf6, 0x9d, 0x10, 0x53, 0xa1, 0x31, - 0xbe, 0x74, 0x81, 0xb1, 0xf8, 0x4e, 0xa2, 0xb6, 0x76, 0x9d, 0x73, 0xf9, 0x92, 0x1c, 0x7b, 0x9b, - 0x57, 0x29, 0x0d, 0x25, 0x69, 0x12, 0xd9, 0x29, 0xf7, 0x85, 0x31, 0xda, 0x5b, 0x2a, 0x3b, 0xe5, - 0xbe, 0xa0, 0x4a, 0x15, 0xb9, 0x43, 0xac, 0xc3, 0xf0, 0x0e, 0xd9, 0x3d, 0xa9, 0x4c, 0x99, 0x5a, - 0xba, 0xc6, 0x7b, 0x94, 0x5c, 0x7d, 0x77, 0xc8, 0x0f, 0x0a, 0x48, 0xbf, 0xbb, 0x69, 0xba, 0xe3, - 0xaa, 0x7b, 0x25, 0xad, 0x43, 0x5f, 0x00, 0xf0, 0x5e, 0x55, 0xbb, 0xdd, 0x85, 0x71, 0x3a, 0xc8, - 0xf3, 0xfa, 0x20, 0xab, 0xdd, 0x6e, 0xed, 0x0a, 0x1f, 0xdf, 0x05, 0x39, 0x3e, 0xa7, 0xdb, 0x55, - 0xa8, 0x29, 0x44, 0xd0, 0xe7, 0x30, 0x41, 0x45, 0x8e, 0x98, 0xd1, 0x09, 0x3a, 0xa3, 0x97, 0x4e, - 0x4f, 0x2a, 0xf3, 0xe4, 0x83, 0x33, 0xcd, 0xa7, 0x86, 0x80, 0xfe, 0x7f, 0x98, 0xe3, 0xe4, 0xf6, - 0x5c, 0xaf, 0xed, 0xbf, 0x0c, 0x97, 0x71, 0xf8, 0x3c, 0xf2, 0xbb, 0x0b, 0x93, 0xb4, 0x7b, 0x8b, - 0x7a, 0xf7, 0x74, 0x98, 0xda, 0x6d, 0xde, 0x53, 0x4b, 0xf6, 0xf4, 0x25, 0x03, 0x68, 0xb6, 0x19, - 0x84, 0xd2, 0xac, 0xb9, 0x19, 0xb4, 0x06, 0xd3, 0x3b, 0x21, 0xd6, 0xc6, 0x30, 0x45, 0xe5, 0x77, - 0x85, 0xcc, 0xf0, 0x71, 0x88, 0x9b, 0x59, 0xe3, 0x48, 0xe2, 0x21, 0x1b, 0xd0, 0x72, 0xe0, 0x77, - 0x13, 0x6b, 0x7c, 0x9a, 0x72, 0xc4, 0x3a, 0x3d, 0xa9, 0x5c, 0x69, 0x07, 0x7e, 0xb7, 0x99, 0xbd, - 0xd0, 0x0d, 0xd8, 0xe8, 0x47, 0x70, 0xa1, 0xee, 0x7b, 0x1e, 0x6e, 0x11, 0xf9, 0xb9, 0xec, 0x3a, - 0x07, 0x9e, 0x1f, 0x46, 0x6e, 0x6b, 0x6d, 0x79, 0xa1, 0x44, 0xd7, 0xd0, 0x4d, 0x32, 0xfa, 0x96, - 0x84, 0x68, 0xb6, 0x25, 0x48, 0xd3, 0x6d, 0x2b, 0xb4, 0x33, 0xa8, 0xa0, 0x1f, 0xc2, 0x24, 0x6f, - 0x0b, 0x07, 0x74, 0x69, 0x9e, 0xcf, 0x5f, 0x68, 0x12, 0x98, 0x6d, 0xc4, 0x81, 0xf8, 0xc9, 0x54, - 0x1b, 0x9d, 0x16, 0xfa, 0x0a, 0xc6, 0x9f, 0x3e, 0xac, 0xda, 0x38, 0xec, 0xfa, 0x5e, 0x88, 0x17, - 0x10, 0x9d, 0xd1, 0x2b, 0x9c, 0xf4, 0xd3, 0x87, 0xd5, 0xea, 0x71, 0x74, 0x88, 0xbd, 0xc8, 0x6d, - 0x39, 0x11, 0x16, 0x50, 0xb5, 0x32, 0x59, 0x79, 0x47, 0xcf, 0x9c, 0x66, 0xc0, 0x4b, 0x94, 0x51, - 0xa8, 0xe4, 0x50, 0x19, 0x46, 0x1b, 0x8d, 0xd5, 0x75, 0xff, 0xc0, 0xf5, 0x16, 0x66, 0x08, 0x33, - 0x6c, 0xf9, 0x1b, 0xed, 0xc3, 0x9c, 0xa2, 0xa0, 0x37, 0xc9, 0xff, 0xf8, 0x08, 0x7b, 0xd1, 0xc2, - 0x2c, 0xed, 0xc3, 0xbb, 0xf2, 0x84, 0x71, 0x47, 0xd5, 0xe3, 0x5f, 0xdc, 0xbf, 0x53, 0x8d, 0x7f, - 0x36, 0x04, 0x92, 0x3d, 0xeb, 0x18, 0x4a, 0xad, 0x2f, 0x61, 0x4c, 0x7e, 0x76, 0x68, 0x04, 0x06, - 0xab, 0x9d, 0x4e, 0x69, 0x80, 0xfc, 0xd1, 0x68, 0xac, 0x96, 0x0a, 0x68, 0x0a, 0x20, 0x96, 0x35, - 0xa5, 0x22, 0x9a, 0x80, 0x51, 0x21, 0x0b, 0x4a, 0x83, 0x14, 0xbe, 0xdb, 0x2d, 0x0d, 0x21, 0x04, - 0x53, 0xfa, 0x8a, 0x2c, 0x0d, 0x5b, 0x3b, 0x30, 0x26, 0x19, 0x89, 0xa6, 0x61, 0x7c, 0x67, 0xa3, - 0xb1, 0xb5, 0x52, 0x5f, 0x7b, 0xb8, 0xb6, 0xb2, 0x5c, 0x1a, 0x40, 0x97, 0xe1, 0xe2, 0x76, 0x63, - 0xb5, 0xb9, 0x5c, 0x6b, 0xae, 0x6f, 0xd6, 0xab, 0xeb, 0xcd, 0x2d, 0x7b, 0xf3, 0xcb, 0xef, 0x37, - 0xb7, 0x77, 0x36, 0x36, 0x56, 0xd6, 0x4b, 0x05, 0xb4, 0x00, 0xb3, 0xa4, 0xfa, 0xc9, 0x4e, 0x6d, - 0x45, 0x05, 0x28, 0x15, 0xad, 0xff, 0x5c, 0x48, 0x49, 0x3a, 0xb4, 0x04, 0xe3, 0x0d, 0x76, 0x96, - 0xa2, 0xb3, 0xcf, 0xf4, 0xda, 0xd2, 0xe9, 0x49, 0x65, 0x82, 0x1f, 0xb1, 0xd8, 0xc4, 0xaa, 0x40, - 0x64, 0xf3, 0xda, 0x22, 0x53, 0xd8, 0xf2, 0x3b, 0xea, 0xe6, 0xd5, 0xe5, 0x65, 0xb6, 0xac, 0x45, - 0x4b, 0xca, 0x36, 0xc7, 0x94, 0x5c, 0xaa, 0x48, 0x89, 0x6d, 0x4e, 0x15, 0x79, 0x72, 0xc3, 0x5b, - 0x8a, 0xf9, 0xc4, 0x77, 0x27, 0x8a, 0x63, 0x10, 0xb1, 0x12, 0xce, 0x3a, 0xce, 0x10, 0x22, 0xe8, - 0x93, 0x24, 0x77, 0xf9, 0x08, 0xa9, 0x94, 0x4c, 0xc8, 0x0a, 0x3b, 0x01, 0x8a, 0x2a, 0x30, 0xcc, - 0x56, 0x17, 0x1b, 0x24, 0xd5, 0x22, 0x3a, 0xa4, 0xc0, 0x66, 0xe5, 0xd6, 0x3f, 0x1c, 0x54, 0x05, - 0x2a, 0xd1, 0x1a, 0x14, 0x26, 0x52, 0xad, 0x81, 0x32, 0x8f, 0x96, 0x12, 0x05, 0xa1, 0x81, 0xc3, - 0x90, 0xea, 0x5b, 0x9c, 0x22, 0x55, 0x10, 0x42, 0x56, 0x48, 0x8e, 0x10, 0x31, 0x00, 0x51, 0x62, - 0x99, 0xb6, 0x40, 0x95, 0xd8, 0xc1, 0x58, 0x89, 0xe5, 0xfa, 0x04, 0x53, 0x62, 0x63, 0x10, 0x32, - 0x91, 0x7c, 0x43, 0xa3, 0x7d, 0x18, 0x8a, 0x27, 0x92, 0x6f, 0x82, 0x7c, 0x22, 0x15, 0x20, 0xf4, - 0x31, 0x40, 0x75, 0xaf, 0x41, 0x75, 0x41, 0x7b, 0x83, 0x6f, 0xea, 0xf4, 0xf3, 0x73, 0x5e, 0x86, - 0x4c, 0xec, 0x39, 0x81, 0xaa, 0xed, 0x2a, 0xd0, 0xa8, 0x06, 0x93, 0xd5, 0x9f, 0x1c, 0x07, 0x78, - 0xad, 0x4d, 0xbe, 0xe0, 0x88, 0xa9, 0xf5, 0x63, 0xb5, 0xc5, 0xd3, 0x93, 0xca, 0x82, 0x43, 0x2a, - 0x9a, 0x2e, 0xaf, 0x51, 0x08, 0xe8, 0x28, 0x68, 0x13, 0xce, 0x3f, 0xaa, 0x6f, 0xf1, 0xa5, 0x55, - 0x6d, 0xb5, 0xfc, 0x63, 0x2f, 0xe2, 0x3b, 0xf9, 0xb5, 0xd3, 0x93, 0xca, 0xe5, 0x83, 0x56, 0xb7, - 0x29, 0x96, 0xa1, 0xc3, 0xaa, 0xd5, 0xad, 0x3c, 0x85, 0x6b, 0x75, 0x60, 0xea, 0x11, 0x8e, 0xc8, - 0x52, 0x12, 0x6a, 0x59, 0xfe, 0x9c, 0x7c, 0x0a, 0xe3, 0x7b, 0x6e, 0x74, 0xd8, 0xc0, 0xad, 0x00, - 0x47, 0xe2, 0xd0, 0x48, 0x39, 0xf0, 0xd2, 0x8d, 0x0e, 0x9b, 0x21, 0x2b, 0x57, 0x05, 0x90, 0x02, - 0x6e, 0xad, 0xc0, 0x34, 0x6f, 0x4d, 0x6a, 0x81, 0x4b, 0x3a, 0xc1, 0x02, 0x25, 0x48, 0x67, 0x41, - 0x25, 0xa8, 0x93, 0xf9, 0xd3, 0x22, 0xcc, 0xd5, 0x0f, 0x1d, 0xef, 0x00, 0x6f, 0x39, 0x61, 0xf8, - 0xd2, 0x0f, 0xda, 0x4a, 0xe7, 0xa9, 0x0a, 0x9c, 0xea, 0x3c, 0xd5, 0x79, 0x97, 0x60, 0x7c, 0xb3, - 0xd3, 0x16, 0x38, 0x5c, 0x3d, 0xa7, 0x6d, 0xf9, 0x9d, 0x76, 0xb3, 0x2b, 0x68, 0xa9, 0x40, 0x04, - 0x67, 0x03, 0xbf, 0x94, 0x38, 0x83, 0x31, 0x8e, 0x87, 0x5f, 0x2a, 0x38, 0x0a, 0x10, 0x5a, 0x81, - 0xf3, 0x0d, 0xdc, 0xf2, 0xbd, 0xf6, 0x43, 0xa7, 0x15, 0xf9, 0xc1, 0xb6, 0xff, 0x1c, 0x7b, 0x7c, - 0x7d, 0x51, 0x0d, 0x26, 0xa4, 0x95, 0xcd, 0x67, 0xb4, 0xb6, 0x19, 0x91, 0x6a, 0x3b, 0x8d, 0x81, - 0x36, 0x61, 0x74, 0x8f, 0xdb, 0x17, 0xb8, 0x4e, 0x7f, 0xe3, 0x8e, 0x34, 0x38, 0xd4, 0x03, 0x4c, - 0x17, 0x85, 0xd3, 0x91, 0xa7, 0x12, 0xb9, 0x21, 0x50, 0xe1, 0x22, 0x20, 0x6d, 0x49, 0xc4, 0xda, - 0x81, 0xc9, 0xad, 0xce, 0xf1, 0x81, 0xeb, 0x11, 0x31, 0xd0, 0xc0, 0xbf, 0x8e, 0x96, 0x01, 0xe2, - 0x02, 0x6e, 0x50, 0x98, 0xe1, 0x27, 0x81, 0xb8, 0x62, 0xf7, 0x01, 0xff, 0x90, 0x68, 0x09, 0x55, - 0xdd, 0x6c, 0x05, 0xcf, 0xfa, 0xfb, 0x83, 0x80, 0xf8, 0x04, 0x50, 0x59, 0xdf, 0xc0, 0x11, 0x11, - 0xc3, 0x17, 0xa0, 0x28, 0xcf, 0xfd, 0xe7, 0x4e, 0x4f, 0x2a, 0x45, 0xb7, 0x6d, 0x17, 0xd7, 0x96, - 0xd1, 0x7b, 0x30, 0x4c, 0xc1, 0x28, 0xff, 0xa7, 0x64, 0x7b, 0x2a, 0x05, 0x26, 0x39, 0xe8, 0x1e, - 0x64, 0x33, 0x60, 0xf4, 0x3e, 0x8c, 0x2d, 0xe3, 0x0e, 0x3e, 0x70, 0x22, 0x5f, 0x7c, 0xdd, 0xec, - 0x24, 0x2d, 0x0a, 0x95, 0x35, 0x17, 0x43, 0x12, 0xbd, 0xdd, 0xc6, 0x4e, 0xe8, 0x7b, 0xaa, 0xde, - 0x1e, 0xd0, 0x12, 0x55, 0x6f, 0x67, 0x30, 0xe8, 0x0f, 0x0a, 0x30, 0x5e, 0xf5, 0x3c, 0x7e, 0x42, - 0x0d, 0x39, 0xd7, 0xe7, 0xee, 0x48, 0xbb, 0xcd, 0xba, 0xb3, 0x8f, 0x3b, 0xbb, 0x4e, 0xe7, 0x18, - 0x87, 0xb5, 0xaf, 0x88, 0x2a, 0xf5, 0x5f, 0x4e, 0x2a, 0x9f, 0x9c, 0xe1, 0xcc, 0x19, 0x5b, 0x80, - 0xb6, 0x03, 0xc7, 0x8d, 0xc2, 0xd3, 0x93, 0xca, 0x9c, 0x13, 0x37, 0xa8, 0x7e, 0x37, 0x4a, 0x3f, - 0xd0, 0x3b, 0xea, 0x61, 0x8d, 0xcb, 0xe2, 0xc4, 0xe1, 0x97, 0x9f, 0xd3, 0xac, 0xeb, 0x72, 0x27, - 0x5c, 0x5b, 0xce, 0x9a, 0x02, 0xab, 0x0e, 0x8b, 0x8f, 0x70, 0x64, 0xe3, 0x10, 0x47, 0x62, 0xd1, - 0xd2, 0x25, 0x17, 0x9b, 0x6d, 0x46, 0xe8, 0x6f, 0x89, 0x4c, 0xe7, 0x83, 0x2d, 0x54, 0x51, 0x63, - 0xfd, 0xdd, 0x02, 0x54, 0xea, 0x01, 0x66, 0x9a, 0x48, 0x06, 0xa1, 0x7c, 0x61, 0xb2, 0x08, 0x43, - 0xdb, 0xaf, 0xba, 0xe2, 0x3c, 0x47, 0x6b, 0x09, 0x97, 0x6c, 0x5a, 0xda, 0xe7, 0xe1, 0xd8, 0x7a, - 0x06, 0x73, 0x36, 0xf6, 0xf0, 0x4b, 0x72, 0x54, 0xd7, 0xce, 0x97, 0x15, 0x18, 0x66, 0x5f, 0x5e, - 0x6a, 0x08, 0xac, 0xfc, 0x6c, 0x67, 0x75, 0xeb, 0x9f, 0x15, 0xa1, 0xc4, 0x86, 0x5b, 0xf3, 0xa3, - 0xfe, 0xc6, 0xc7, 0x47, 0x50, 0xec, 0x71, 0xbc, 0xbf, 0x19, 0x73, 0x7b, 0x30, 0x56, 0x0e, 0x68, - 0x57, 0xc9, 0x1e, 0x27, 0x2a, 0xc9, 0x80, 0xd8, 0x2a, 0x60, 0x56, 0xa9, 0xd4, 0x19, 0x1d, 0xfd, - 0x4e, 0x01, 0xce, 0xb1, 0x75, 0x95, 0xbf, 0x72, 0xf7, 0xde, 0xcc, 0xca, 0x2d, 0x45, 0xf4, 0x2f, - 0xf5, 0x3b, 0x62, 0x75, 0xd6, 0xbf, 0x28, 0xc2, 0x79, 0x85, 0x57, 0x5c, 0xfd, 0x7c, 0x87, 0xe9, - 0x36, 0x0a, 0xc3, 0xa8, 0x9d, 0x8f, 0xe8, 0x36, 0xcd, 0xf8, 0x0c, 0x4f, 0x39, 0xf7, 0x0e, 0x8c, - 0x92, 0x21, 0x25, 0x4d, 0x82, 0x74, 0x87, 0x65, 0xa0, 0xa2, 0xba, 0x6f, 0xee, 0xdd, 0x85, 0x51, - 0xfa, 0x27, 0x99, 0x91, 0xa1, 0xec, 0x19, 0x91, 0x40, 0xc8, 0x05, 0x78, 0xec, 0xbb, 0xde, 0x53, - 0x1c, 0x1d, 0xfa, 0x6d, 0xbe, 0xd7, 0xaf, 0x11, 0x39, 0xf8, 0xff, 0xf9, 0xae, 0xd7, 0x3c, 0xa2, - 0xc5, 0x67, 0x35, 0x39, 0xc5, 0x04, 0x6d, 0x85, 0xb8, 0x75, 0x0f, 0x4a, 0x44, 0x64, 0xf5, 0xbf, - 0xb4, 0xac, 0x59, 0x40, 0x8f, 0x70, 0x54, 0xf3, 0xb5, 0xcd, 0xd4, 0x9a, 0x84, 0xf1, 0x2d, 0xd7, - 0x3b, 0x10, 0x3f, 0xff, 0xe5, 0x20, 0x4c, 0xb0, 0xdf, 0x7c, 0x06, 0x12, 0x2a, 0x4f, 0xa1, 0x1f, - 0x95, 0xe7, 0x43, 0x98, 0x24, 0x3a, 0x03, 0x0e, 0x76, 0x71, 0x40, 0x54, 0x2d, 0x3e, 0x1f, 0xf4, - 0x30, 0x13, 0xd2, 0x8a, 0xe6, 0x0b, 0x56, 0x63, 0xeb, 0x80, 0x68, 0x1d, 0xa6, 0x58, 0xc1, 0x43, - 0xec, 0x44, 0xc7, 0xb1, 0x3d, 0x66, 0x9a, 0x9f, 0x67, 0x44, 0x31, 0x93, 0x67, 0x9c, 0xd6, 0x33, - 0x5e, 0x68, 0x27, 0x70, 0xd1, 0xe7, 0x30, 0xbd, 0x15, 0xf8, 0x5f, 0xbf, 0x52, 0x94, 0x3c, 0x26, - 0xd2, 0xe7, 0x4e, 0x4f, 0x2a, 0xe7, 0xbb, 0xa4, 0xaa, 0xa9, 0xaa, 0x7a, 0x49, 0x68, 0xb2, 0xa6, - 0xd6, 0xc2, 0x9a, 0x1f, 0xb8, 0xde, 0x01, 0x9d, 0xcd, 0x51, 0xb6, 0xa6, 0xdc, 0xb0, 0xb9, 0x4f, - 0x0b, 0x6d, 0x59, 0x9d, 0x30, 0x88, 0x8e, 0xf4, 0x36, 0x88, 0xde, 0x03, 0x58, 0xf7, 0x9d, 0x76, - 0xb5, 0xd3, 0xa9, 0x57, 0x43, 0x6a, 0x0c, 0xe1, 0x4a, 0x4c, 0xc7, 0x77, 0xda, 0x4d, 0xa7, 0xd3, - 0x69, 0xb6, 0x9c, 0xd0, 0x56, 0x60, 0x1e, 0x0f, 0x8d, 0x9e, 0x2b, 0x8d, 0xd8, 0xd3, 0xeb, 0x6e, - 0x0b, 0x7b, 0x21, 0xde, 0x73, 0x02, 0xcf, 0xf5, 0x0e, 0x42, 0xeb, 0xa7, 0xe7, 0x60, 0x54, 0x0e, - 0xf9, 0x8e, 0x7a, 0x20, 0xe2, 0xaa, 0x11, 0x95, 0x50, 0xb1, 0xc1, 0xc6, 0x56, 0x20, 0xd0, 0x45, - 0x7a, 0x44, 0xe2, 0x4a, 0xd9, 0x08, 0x59, 0xdd, 0x4e, 0xb7, 0x6b, 0x93, 0x32, 0xb2, 0x13, 0x2c, - 0xd7, 0x28, 0xff, 0x47, 0xd9, 0x4e, 0xd0, 0xde, 0xb7, 0x8b, 0xcb, 0x35, 0xb2, 0xca, 0x36, 0xd7, - 0x96, 0xeb, 0x94, 0x95, 0xa3, 0x6c, 0x95, 0xf9, 0x6e, 0xbb, 0x65, 0xd3, 0x52, 0x52, 0xdb, 0xa8, - 0x3e, 0x5d, 0xe7, 0xec, 0xa2, 0xb5, 0xa1, 0x73, 0xd4, 0xb1, 0x69, 0x29, 0x39, 0x2a, 0xb0, 0xb3, - 0x77, 0xdd, 0xf7, 0xa2, 0xc0, 0xef, 0x84, 0x54, 0xa3, 0x1d, 0x65, 0xd3, 0xc9, 0x0f, 0xed, 0x2d, - 0x5e, 0x65, 0x27, 0x40, 0xd1, 0x1e, 0xcc, 0x57, 0xdb, 0x2f, 0x1c, 0xaf, 0x85, 0xdb, 0xac, 0x66, - 0xcf, 0x0f, 0x9e, 0x3f, 0xeb, 0xf8, 0x2f, 0x43, 0xca, 0xef, 0x51, 0x6e, 0xe3, 0xe2, 0x20, 0xc2, - 0x06, 0xf0, 0x52, 0x00, 0xd9, 0x59, 0xd8, 0x44, 0x4a, 0xd6, 0x3b, 0xfe, 0x71, 0x9b, 0xcf, 0x02, - 0x95, 0x92, 0x2d, 0x52, 0x60, 0xb3, 0x72, 0xc2, 0xa5, 0xd5, 0xc6, 0x53, 0x6a, 0x51, 0xe2, 0x5c, - 0x3a, 0x0c, 0x8f, 0x6c, 0x52, 0x86, 0x6e, 0xc0, 0x88, 0x38, 0xf5, 0x30, 0x93, 0x34, 0x35, 0xb4, - 0x8a, 0xd3, 0x8e, 0xa8, 0x23, 0x9f, 0x84, 0x8d, 0x5b, 0xfe, 0x0b, 0x1c, 0xbc, 0xaa, 0xfb, 0x6d, - 0x2c, 0xec, 0x1f, 0xfc, 0x7c, 0xcf, 0x2a, 0x9a, 0x2d, 0x52, 0x63, 0xeb, 0x80, 0xa4, 0x01, 0xa6, - 0x38, 0x85, 0x0b, 0xd3, 0x71, 0x03, 0x4c, 0xb1, 0x0a, 0x6d, 0x51, 0x87, 0x96, 0xe1, 0x7c, 0xf5, - 0x38, 0xf2, 0x8f, 0x9c, 0xc8, 0x6d, 0xed, 0x74, 0x0f, 0x02, 0x87, 0x34, 0x52, 0xa2, 0x08, 0xf4, - 0x68, 0xe7, 0x88, 0xca, 0xe6, 0x31, 0xaf, 0xb5, 0xd3, 0x08, 0xe8, 0x03, 0x98, 0x58, 0x0b, 0x99, - 0x8d, 0xcb, 0x09, 0x71, 0x9b, 0x1a, 0x2a, 0x78, 0x2f, 0xdd, 0xb0, 0x49, 0x2d, 0x5e, 0x4d, 0x72, - 0x18, 0x6c, 0xdb, 0x1a, 0x1c, 0xb2, 0xe0, 0x5c, 0x35, 0x0c, 0xdd, 0x30, 0xa2, 0xf6, 0x87, 0xd1, - 0x1a, 0x9c, 0x9e, 0x54, 0xce, 0x39, 0xb4, 0xc4, 0xe6, 0x35, 0x68, 0x0f, 0xc6, 0x97, 0x31, 0x39, - 0x48, 0x6c, 0x07, 0xc7, 0x61, 0x44, 0xad, 0x09, 0xe3, 0x4b, 0x17, 0xf9, 0x87, 0xad, 0xd4, 0xf0, - 0xb5, 0xcc, 0x8e, 0x08, 0x6d, 0x5a, 0xde, 0x8c, 0x48, 0x85, 0xaa, 0xea, 0x28, 0xf0, 0x8f, 0x87, - 0x46, 0xc7, 0x4b, 0x13, 0xcc, 0x04, 0xff, 0x78, 0x68, 0x74, 0xb2, 0x34, 0x65, 0xfd, 0x61, 0x01, - 0x50, 0x9a, 0x22, 0xba, 0x0b, 0x23, 0xd8, 0x23, 0x9b, 0x7e, 0x9b, 0x7f, 0x19, 0x54, 0x0e, 0xf0, - 0x22, 0x85, 0xba, 0x80, 0x42, 0x5f, 0xc0, 0x0c, 0xeb, 0x80, 0x18, 0x7b, 0xc7, 0x3d, 0x72, 0x23, - 0xfa, 0xb5, 0x0c, 0xb3, 0xd3, 0x93, 0xa1, 0x5a, 0x3d, 0x3d, 0xf1, 0x6a, 0xca, 0xa9, 0x75, 0x52, - 0x69, 0xdd, 0x87, 0xf3, 0x4c, 0x6e, 0xf7, 0x7d, 0x80, 0xb2, 0xb6, 0x00, 0x1a, 0xf8, 0xc8, 0xe9, - 0x1e, 0xfa, 0xe4, 0x0b, 0xaf, 0xa9, 0xbf, 0xb8, 0x02, 0x8e, 0xb8, 0x42, 0x2c, 0x2b, 0x76, 0x1f, - 0x88, 0x73, 0xaf, 0x80, 0xb4, 0x15, 0x2c, 0xeb, 0xcf, 0x8b, 0x80, 0xaa, 0xc7, 0x6d, 0x37, 0x6a, - 0x44, 0x01, 0x76, 0x8e, 0x44, 0x37, 0x3e, 0x82, 0x09, 0xb6, 0x05, 0xb3, 0x62, 0xda, 0x1d, 0xa2, - 0xdd, 0xb3, 0x29, 0x52, 0xab, 0x56, 0x07, 0x6c, 0x0d, 0x94, 0xa0, 0xda, 0x38, 0x3c, 0x3e, 0x12, - 0xa8, 0x45, 0x0d, 0x55, 0xad, 0x22, 0xa8, 0xea, 0x6f, 0xf4, 0x39, 0x4c, 0xd5, 0xfd, 0xa3, 0x2e, - 0xe1, 0x09, 0x47, 0x1e, 0xe4, 0x9a, 0x08, 0x6f, 0x57, 0xab, 0x5c, 0x1d, 0xb0, 0x13, 0xe0, 0x68, - 0x03, 0x66, 0x1e, 0x76, 0x8e, 0xc3, 0xc3, 0xaa, 0xd7, 0xae, 0x77, 0xfc, 0x50, 0x50, 0x19, 0xe2, - 0x96, 0x7c, 0xbe, 0x73, 0xa4, 0x21, 0x56, 0x07, 0x6c, 0x13, 0x22, 0xba, 0x01, 0xc3, 0x2b, 0x2f, - 0xb0, 0x17, 0xc9, 0x5b, 0x11, 0x7e, 0xad, 0xba, 0xe9, 0xe1, 0xcd, 0x67, 0xab, 0x03, 0x36, 0xab, - 0xad, 0x8d, 0xc1, 0x88, 0xd8, 0x35, 0xef, 0x92, 0x8f, 0x4f, 0xb2, 0x93, 0x9c, 0x3e, 0x8e, 0x43, - 0x54, 0x86, 0xd1, 0x9d, 0x2e, 0x11, 0xe6, 0x42, 0x25, 0xb6, 0xe5, 0x6f, 0xeb, 0xdb, 0x3a, 0xa7, - 0xd1, 0xa2, 0x6a, 0xb7, 0x60, 0xc0, 0x71, 0x81, 0xb5, 0xaa, 0x33, 0x37, 0x1f, 0x5a, 0x6b, 0xb7, - 0x98, 0x68, 0xb7, 0x94, 0xe4, 0xb5, 0x35, 0x67, 0x64, 0x9e, 0xf5, 0x25, 0x5c, 0xd9, 0xe9, 0x92, - 0x43, 0x62, 0xb5, 0xdb, 0xed, 0xb8, 0x2d, 0x66, 0x95, 0xa3, 0xbb, 0xab, 0x58, 0x2c, 0x1f, 0xc0, - 0x39, 0x56, 0xc0, 0x97, 0x89, 0x58, 0x83, 0xd5, 0x6e, 0x97, 0xef, 0xe9, 0x0f, 0x98, 0x18, 0x60, - 0xbb, 0xb4, 0xcd, 0xa1, 0xad, 0x9f, 0x16, 0xe0, 0x0a, 0xfb, 0x02, 0x32, 0x49, 0x7f, 0x0b, 0xc6, - 0xe8, 0xad, 0x66, 0xd7, 0x69, 0x69, 0x6a, 0x9f, 0x27, 0x0a, 0xed, 0xb8, 0x5e, 0xb9, 0x2f, 0x2e, - 0x66, 0xdf, 0x17, 0x8b, 0x0f, 0x6c, 0xd0, 0xf8, 0x81, 0x7d, 0x01, 0x16, 0xef, 0x51, 0xa7, 0x93, - 0xea, 0x54, 0xf8, 0x3a, 0xbd, 0xb2, 0xfe, 0x7b, 0x11, 0xe6, 0x1f, 0x61, 0x0f, 0x07, 0x0e, 0x1d, - 0xa7, 0x76, 0xc2, 0x51, 0xef, 0xa5, 0x0a, 0xb9, 0xf7, 0x52, 0x52, 0x7d, 0x2f, 0x66, 0xa8, 0xef, - 0x17, 0x61, 0x70, 0xc7, 0x5e, 0xe3, 0xc3, 0xa2, 0x1b, 0xd3, 0x71, 0xe0, 0xda, 0xa4, 0x0c, 0xad, - 0xc5, 0x77, 0x5a, 0x43, 0x3d, 0xef, 0xb4, 0x66, 0xb8, 0x8d, 0x7f, 0x84, 0xdf, 0x69, 0xe9, 0x37, - 0x59, 0x1b, 0xca, 0x19, 0x81, 0x88, 0x9b, 0xdb, 0xfc, 0x9b, 0xca, 0x18, 0x20, 0x57, 0xf7, 0x57, - 0xbc, 0x28, 0x78, 0xc5, 0x96, 0x00, 0xd3, 0xfa, 0x85, 0xae, 0x5f, 0xfe, 0x02, 0xc6, 0x15, 0x10, - 0x54, 0x82, 0xc1, 0xe7, 0xfc, 0x3e, 0x6f, 0xcc, 0x26, 0x7f, 0xa2, 0x6f, 0xc3, 0xf0, 0x0b, 0x72, - 0xee, 0xe0, 0x62, 0xe4, 0x42, 0x7c, 0x26, 0x69, 0x44, 0x44, 0xdb, 0x62, 0x87, 0x12, 0x9b, 0x01, - 0x7d, 0x5c, 0xfc, 0xb0, 0x60, 0x7d, 0x02, 0x0b, 0xe9, 0xde, 0x70, 0x15, 0xb6, 0xd7, 0xa9, 0xce, - 0x5a, 0x86, 0xd9, 0x47, 0x38, 0xa2, 0x0b, 0x97, 0x7e, 0x44, 0xca, 0x75, 0x63, 0xe2, 0x3b, 0xcb, - 0xb1, 0x26, 0x5a, 0x0d, 0x98, 0x4b, 0x50, 0xe1, 0xed, 0x7f, 0x0c, 0x23, 0xbc, 0x48, 0x4a, 0x54, - 0xee, 0x80, 0x81, 0xf7, 0x79, 0xc5, 0xee, 0x12, 0x5b, 0xb7, 0x9c, 0xb2, 0x2d, 0x10, 0xac, 0x43, - 0xb8, 0xb0, 0xee, 0x86, 0x0a, 0x55, 0xb9, 0x1c, 0x2f, 0xc1, 0x58, 0x97, 0xec, 0x39, 0xa1, 0xfb, - 0x13, 0xb6, 0x8c, 0x86, 0xed, 0x51, 0x52, 0xd0, 0x70, 0x7f, 0x82, 0xd1, 0x65, 0x00, 0x5a, 0x49, - 0x87, 0xc9, 0xa5, 0x00, 0x05, 0x67, 0xc7, 0x58, 0x04, 0xf4, 0x5e, 0x97, 0xad, 0x1b, 0x9b, 0xfe, - 0x6d, 0x05, 0x30, 0x9f, 0x6a, 0x89, 0x0f, 0xe0, 0x2e, 0x8c, 0xf2, 0x8e, 0x85, 0x09, 0x8b, 0x8f, - 0x3a, 0x02, 0x5b, 0x02, 0xa1, 0x9b, 0x30, 0xed, 0xe1, 0xaf, 0xa3, 0x66, 0xaa, 0x0f, 0x93, 0xa4, - 0x78, 0x4b, 0xf4, 0xc3, 0xfa, 0x35, 0x6a, 0x54, 0x68, 0x78, 0xfe, 0xcb, 0x67, 0x1d, 0xe7, 0x39, - 0x4e, 0x35, 0xfc, 0x5d, 0x18, 0x6d, 0xf4, 0x6e, 0x98, 0x7d, 0x3e, 0xa2, 0x71, 0x5b, 0xa2, 0x58, - 0x1d, 0x28, 0x93, 0x21, 0x11, 0xcd, 0x73, 0xad, 0xbd, 0xf5, 0xcb, 0x66, 0xe0, 0x0b, 0xb8, 0x64, - 0x6c, 0xed, 0x97, 0xcd, 0xc4, 0xbf, 0x29, 0xc2, 0x3c, 0xdb, 0x4c, 0xd2, 0x2b, 0xb8, 0x7f, 0x51, - 0xf3, 0x2b, 0xb1, 0x85, 0xdf, 0x33, 0xd8, 0xc2, 0x29, 0x8a, 0x6a, 0x0b, 0xd7, 0x2c, 0xe0, 0x1f, - 0x9a, 0x2d, 0xe0, 0x54, 0x23, 0xd5, 0x2d, 0xe0, 0x49, 0xbb, 0xf7, 0x4a, 0xb6, 0xdd, 0x9b, 0x5a, - 0x01, 0x0d, 0x76, 0x6f, 0x83, 0xb5, 0xfb, 0xf1, 0xd0, 0x68, 0xb1, 0x34, 0x68, 0xed, 0xc2, 0x42, - 0x9a, 0xc5, 0x6f, 0xe0, 0xf3, 0xfe, 0xb3, 0x02, 0x5c, 0xe6, 0x8a, 0x40, 0xe2, 0x23, 0x38, 0xfb, - 0x0c, 0xbe, 0x0f, 0x13, 0x1c, 0x77, 0x3b, 0x5e, 0x2c, 0xb5, 0xf3, 0xa7, 0x27, 0x95, 0x49, 0x21, - 0xb0, 0x98, 0xd4, 0xd3, 0xc0, 0xd0, 0xfb, 0x8a, 0x91, 0x83, 0x19, 0xce, 0x2e, 0x92, 0x5d, 0x8d, - 0x59, 0x43, 0x32, 0x4d, 0x1d, 0xd6, 0x57, 0x70, 0x25, 0xab, 0xe3, 0x6f, 0x80, 0x2f, 0xff, 0xa6, - 0x00, 0x97, 0x38, 0x79, 0xed, 0x73, 0x7a, 0x2d, 0xc9, 0x7c, 0x06, 0x47, 0x90, 0xc7, 0x30, 0x4e, - 0x1a, 0x14, 0xfd, 0x1e, 0xe4, 0xdb, 0x0f, 0xd7, 0xae, 0xe3, 0x9a, 0x65, 0x27, 0x72, 0xf8, 0x0d, - 0x9e, 0x73, 0xd4, 0x69, 0x8a, 0xfe, 0xab, 0xc8, 0xd6, 0x0f, 0x60, 0xd1, 0x3c, 0x84, 0x37, 0xc0, - 0x9f, 0xc7, 0x50, 0x36, 0x08, 0xce, 0xd7, 0xdb, 0xb7, 0xbe, 0x0f, 0x97, 0x8c, 0xb4, 0xde, 0x40, - 0x37, 0x57, 0xc9, 0xae, 0x1c, 0xbd, 0x81, 0x29, 0xb4, 0xf6, 0xe0, 0xa2, 0x81, 0xd2, 0x1b, 0xe8, - 0xe2, 0x23, 0x98, 0x97, 0xda, 0xe8, 0x37, 0xea, 0xe1, 0x53, 0xb8, 0xcc, 0x08, 0xbd, 0x99, 0x59, - 0x79, 0x02, 0x97, 0x38, 0xb9, 0x37, 0xc0, 0xbd, 0x55, 0x58, 0x8c, 0x0f, 0x9d, 0x06, 0x5d, 0xa2, - 0x6f, 0x21, 0x63, 0xad, 0xc3, 0xd5, 0x98, 0x52, 0xc6, 0xc6, 0xda, 0x3f, 0x35, 0xa6, 0x32, 0xc5, - 0xb3, 0xf4, 0x46, 0x66, 0x74, 0x0f, 0x2e, 0x68, 0x44, 0xdf, 0x98, 0x3a, 0xb1, 0x06, 0x33, 0x8c, - 0xb0, 0xae, 0x5e, 0x2e, 0xa9, 0xea, 0xe5, 0xf8, 0xd2, 0xf9, 0x98, 0x24, 0x2d, 0xde, 0x7d, 0x60, - 0xd0, 0x38, 0x9f, 0x52, 0x8d, 0x53, 0x80, 0xc4, 0x3d, 0x7c, 0x1f, 0xce, 0xb1, 0x12, 0xde, 0x3f, - 0x03, 0x31, 0xa6, 0x50, 0x33, 0x34, 0x0e, 0x6c, 0xfd, 0x08, 0x2e, 0xb3, 0xd3, 0x5a, 0x6c, 0xd9, - 0xd3, 0x4f, 0x54, 0xdf, 0x4d, 0x1c, 0xd6, 0x2e, 0x72, 0xba, 0x49, 0xf8, 0x8c, 0x33, 0xdb, 0xbe, - 0x58, 0xdb, 0x59, 0xf4, 0xfb, 0x72, 0xda, 0x15, 0x87, 0xb0, 0xa2, 0xf1, 0x10, 0x76, 0x1d, 0xae, - 0xc9, 0x43, 0x58, 0xb2, 0x19, 0x69, 0xad, 0xfe, 0x01, 0x5c, 0x62, 0x03, 0x15, 0x5e, 0x09, 0x7a, - 0x37, 0x3e, 0x49, 0x0c, 0x73, 0x9e, 0x0f, 0x53, 0x87, 0xce, 0x18, 0xe4, 0x3f, 0x28, 0x88, 0x4f, - 0xce, 0x4c, 0xfc, 0x57, 0x7d, 0x2a, 0xdd, 0x80, 0x8a, 0x64, 0x88, 0xde, 0xa3, 0xd7, 0x3b, 0x92, - 0x3e, 0x85, 0x39, 0x95, 0x8c, 0xdb, 0xc2, 0xbb, 0xf7, 0x89, 0xc2, 0x8a, 0xde, 0x23, 0x9f, 0x05, - 0x2d, 0x10, 0xcb, 0x6e, 0xc1, 0xc0, 0x37, 0x0a, 0x6f, 0x4b, 0x48, 0xab, 0x09, 0x8b, 0xe9, 0xa9, - 0x70, 0x5b, 0xc2, 0x53, 0x0b, 0x7d, 0x4e, 0x3e, 0x61, 0x5a, 0xc2, 0x27, 0x23, 0x93, 0xa8, 0xf8, - 0x8e, 0x19, 0xba, 0xc0, 0xb2, 0x2c, 0x21, 0x6a, 0x12, 0xe3, 0x27, 0xad, 0x8b, 0xf5, 0xf0, 0x9b, - 0x80, 0x44, 0x55, 0xbd, 0x61, 0x8b, 0xa6, 0x2f, 0xc2, 0x60, 0xbd, 0x61, 0x73, 0x07, 0x51, 0x7a, - 0x2a, 0x6e, 0x85, 0x81, 0x4d, 0xca, 0x92, 0x5a, 0x6b, 0xb1, 0x0f, 0xad, 0xf5, 0xf1, 0xd0, 0xe8, - 0x60, 0x69, 0xc8, 0x46, 0x0d, 0xf7, 0xc0, 0xdb, 0x73, 0xa3, 0x43, 0xd9, 0x60, 0xd5, 0xfa, 0x21, - 0xcc, 0x68, 0xcd, 0xf3, 0xaf, 0x38, 0xd7, 0xb3, 0x15, 0xdd, 0x84, 0x91, 0x7a, 0x95, 0x5e, 0x3b, - 0xd2, 0x63, 0xfd, 0x04, 0x93, 0x37, 0x2d, 0xa7, 0x49, 0x5f, 0x2f, 0xd8, 0xa2, 0xd2, 0xfa, 0xa7, - 0x43, 0x0a, 0x75, 0xc5, 0x5f, 0x38, 0x67, 0x74, 0xf7, 0x01, 0xd8, 0x0a, 0x51, 0x06, 0x47, 0x14, - 0xc0, 0x71, 0x7e, 0x53, 0xc2, 0x44, 0xb2, 0xad, 0x00, 0xf5, 0xeb, 0x4f, 0xcc, 0x5d, 0x98, 0x18, - 0x92, 0xb8, 0x4e, 0x94, 0x2e, 0x4c, 0x9c, 0x74, 0x68, 0xab, 0x40, 0xe8, 0x47, 0x49, 0xb7, 0xb7, - 0x61, 0x7a, 0x7b, 0xff, 0x96, 0x30, 0xf9, 0xa6, 0xc7, 0x76, 0x36, 0xcf, 0xb7, 0x97, 0x30, 0x47, - 0x70, 0xdd, 0x67, 0xd4, 0xb7, 0x6d, 0xe5, 0xeb, 0x08, 0x7b, 0x4c, 0xb6, 0x9f, 0xa3, 0xed, 0xdc, - 0xc8, 0x69, 0x27, 0x06, 0x66, 0x66, 0xe6, 0x56, 0x4c, 0xa7, 0x89, 0x65, 0x9d, 0x6d, 0xa6, 0x4f, - 0x17, 0x91, 0xbd, 0xbe, 0xe2, 0xb5, 0xbb, 0xbe, 0x2b, 0x0f, 0x15, 0x6c, 0x11, 0x05, 0x9d, 0x26, - 0xe6, 0xe5, 0xb6, 0x0a, 0x64, 0xdd, 0xcc, 0x75, 0x37, 0x1b, 0x85, 0xa1, 0xed, 0xfa, 0xf6, 0x7a, - 0xa9, 0x60, 0xdd, 0x05, 0x50, 0x5a, 0x02, 0x38, 0xb7, 0xb1, 0x69, 0x3f, 0xad, 0xae, 0x97, 0x06, - 0xd0, 0x1c, 0x9c, 0xdf, 0x5b, 0xdb, 0x58, 0xde, 0xdc, 0x6b, 0x34, 0x1b, 0x4f, 0xab, 0xf6, 0x76, - 0xbd, 0x6a, 0x2f, 0x97, 0x0a, 0xd6, 0x57, 0x30, 0xab, 0x8f, 0xf0, 0x8d, 0x2e, 0xc2, 0x08, 0x66, - 0xa4, 0x3e, 0xf3, 0x78, 0x6f, 0x5b, 0x71, 0xc1, 0xe1, 0x07, 0xa4, 0xe4, 0xad, 0x20, 0x3f, 0x4a, - 0xf1, 0xcf, 0x48, 0x01, 0xd2, 0xee, 0x72, 0x8b, 0xb9, 0x77, 0xb9, 0xd6, 0x77, 0x60, 0x56, 0x6f, - 0xb5, 0x5f, 0x4b, 0xce, 0x5b, 0xd4, 0x37, 0x49, 0x71, 0x18, 0x25, 0x27, 0xf5, 0xb8, 0x8b, 0x5c, - 0xb2, 0x7e, 0x07, 0x4a, 0x1c, 0x2a, 0xde, 0x79, 0xaf, 0x0b, 0x53, 0x5b, 0xc1, 0xe0, 0xdc, 0x2e, - 0x3c, 0x25, 0xde, 0x16, 0xc6, 0xfb, 0x5e, 0x2d, 0xfc, 0x79, 0x01, 0x16, 0x12, 0xbe, 0x97, 0xf5, - 0x43, 0xa7, 0xd3, 0xc1, 0xde, 0x01, 0x46, 0xb7, 0x60, 0x68, 0x7b, 0x73, 0x7b, 0x8b, 0x1b, 0xb7, - 0x66, 0xf9, 0x32, 0x25, 0x45, 0x12, 0xc6, 0xa6, 0x10, 0xe8, 0x09, 0x9c, 0x17, 0x9e, 0x38, 0xb2, - 0x8a, 0x1f, 0x4a, 0x2e, 0xe7, 0xfb, 0xf5, 0xa4, 0xf1, 0xd0, 0x7b, 0xdc, 0x51, 0xf4, 0xd7, 0x8f, - 0xdd, 0x00, 0xb7, 0xe9, 0x81, 0x7d, 0x6a, 0x09, 0xc5, 0x8e, 0xa2, 0xa2, 0xc6, 0x56, 0xc1, 0x98, - 0x13, 0xbf, 0xf5, 0x07, 0x05, 0x98, 0xcf, 0xf0, 0x25, 0x45, 0xef, 0x68, 0xc3, 0x99, 0x51, 0x86, - 0x23, 0x40, 0x56, 0x07, 0xf8, 0x78, 0xea, 0x8a, 0x7b, 0xd2, 0xe0, 0x19, 0xdc, 0x93, 0x56, 0x07, - 0x62, 0x97, 0xa4, 0x1a, 0xc0, 0xa8, 0x28, 0xb7, 0xa6, 0x61, 0x52, 0xe3, 0x9b, 0x65, 0xc1, 0x84, - 0xda, 0x32, 0x99, 0x9c, 0xba, 0xdf, 0x96, 0x93, 0x43, 0xfe, 0xb6, 0x7e, 0xaf, 0x00, 0xb3, 0x74, - 0x88, 0x07, 0x2e, 0xf9, 0x1a, 0x63, 0x0e, 0x2d, 0x69, 0x23, 0x59, 0xd4, 0x46, 0x92, 0x80, 0x95, - 0x43, 0xfa, 0x38, 0x35, 0xa4, 0x45, 0xd3, 0x90, 0xe8, 0x49, 0xd0, 0xf5, 0x3d, 0x6d, 0x24, 0xca, - 0x0d, 0xc2, 0x1f, 0x16, 0x60, 0x46, 0xe9, 0x93, 0xec, 0xff, 0x7d, 0xad, 0x4b, 0x97, 0x0c, 0x5d, - 0x4a, 0x31, 0xb9, 0x96, 0xea, 0xd1, 0x5b, 0x79, 0x3d, 0xea, 0xc9, 0xe3, 0xbf, 0x2a, 0xc0, 0x9c, - 0x91, 0x07, 0xe8, 0x02, 0x51, 0xb7, 0x5a, 0x01, 0x8e, 0x38, 0x7b, 0xf9, 0x2f, 0x52, 0xbe, 0x16, - 0x86, 0xc7, 0x38, 0xe0, 0x06, 0x2b, 0xfe, 0x0b, 0xbd, 0x05, 0x93, 0x5b, 0x38, 0x70, 0xfd, 0x36, - 0x73, 0x5c, 0x63, 0x97, 0xfb, 0x93, 0xb6, 0x5e, 0x88, 0x16, 0x61, 0xac, 0xda, 0x39, 0xf0, 0x03, - 0x37, 0x3a, 0x64, 0x97, 0x38, 0x63, 0x76, 0x5c, 0x40, 0x68, 0x2f, 0xbb, 0x07, 0xc2, 0x5f, 0x65, - 0xd2, 0xe6, 0xbf, 0xd0, 0x02, 0x8c, 0x08, 0x23, 0x0f, 0x35, 0x11, 0xd9, 0xe2, 0x27, 0xc1, 0xf8, - 0xc2, 0xa6, 0x8b, 0x80, 0x3e, 0x6e, 0xb2, 0xf9, 0x2f, 0xeb, 0x36, 0xcc, 0x9a, 0xf8, 0x68, 0x5c, - 0x32, 0x7f, 0xbb, 0x08, 0x33, 0xd5, 0x76, 0xfb, 0xe9, 0xc3, 0x2a, 0xbb, 0x56, 0x14, 0xdf, 0xfe, - 0x7b, 0x30, 0xb4, 0xe6, 0xb9, 0x11, 0xd7, 0x70, 0x84, 0xd7, 0xb5, 0x01, 0x92, 0x40, 0x91, 0x19, - 0x22, 0xff, 0x23, 0x1b, 0x66, 0x56, 0xbe, 0x76, 0xc3, 0xc8, 0xf5, 0x0e, 0x54, 0xd7, 0xed, 0x62, - 0x3f, 0xae, 0xdb, 0xab, 0x03, 0xb6, 0x09, 0x19, 0x6d, 0xc3, 0x85, 0x0d, 0xfc, 0xd2, 0xb0, 0x84, - 0xe4, 0x8b, 0x16, 0xe5, 0x43, 0x4f, 0xad, 0x9c, 0x0c, 0x5c, 0x75, 0x85, 0xfe, 0x4e, 0x91, 0x3e, - 0x78, 0x53, 0x06, 0xc6, 0x5b, 0xde, 0x81, 0x59, 0xa5, 0x43, 0xb1, 0x9c, 0x62, 0x3c, 0xa9, 0x98, - 0x87, 0xa3, 0x7e, 0x48, 0x46, 0x74, 0xb4, 0x07, 0xf3, 0x7a, 0xa7, 0x62, 0xca, 0xfa, 0xc7, 0x60, - 0x02, 0x59, 0x1d, 0xb0, 0xb3, 0xb0, 0xd1, 0x12, 0x0c, 0x56, 0x5b, 0xcf, 0x39, 0x5b, 0xcc, 0x53, - 0xc6, 0x46, 0x56, 0x6d, 0x3d, 0x5f, 0x1d, 0xb0, 0x09, 0xb0, 0xf6, 0x3d, 0xfc, 0xbb, 0x02, 0xcc, - 0x67, 0xcc, 0x30, 0xba, 0x02, 0xc0, 0x0a, 0x95, 0x1d, 0x41, 0x29, 0x21, 0x0a, 0x1a, 0xbf, 0x97, - 0x7e, 0xd5, 0x65, 0x33, 0x33, 0x25, 0x1f, 0x87, 0xc4, 0x15, 0xb6, 0x02, 0x84, 0xb6, 0xc4, 0xb5, - 0x39, 0x7b, 0xa3, 0xa2, 0x8b, 0x6d, 0xa5, 0x46, 0xbb, 0x2f, 0x4f, 0xbe, 0x4d, 0x51, 0x49, 0x70, - 0x93, 0x66, 0x3d, 0x39, 0x0a, 0x39, 0x68, 0x74, 0x0b, 0xce, 0xb1, 0x42, 0x3e, 0x87, 0xe2, 0xc1, - 0x68, 0x0c, 0xcc, 0xeb, 0xad, 0x7f, 0x5c, 0x80, 0x0b, 0x6c, 0x47, 0x4c, 0x7d, 0x1a, 0xdf, 0xd1, - 0x3e, 0x8d, 0x6b, 0xb2, 0xc3, 0x26, 0x60, 0xed, 0xeb, 0xa8, 0xe9, 0x0f, 0x1a, 0xfa, 0xfd, 0x2a, - 0x54, 0x24, 0x75, 0xdd, 0xfe, 0x93, 0x82, 0xb0, 0xf0, 0xa4, 0x97, 0xee, 0x0a, 0x4c, 0xbc, 0xde, - 0x92, 0xd5, 0xd0, 0xd0, 0xfb, 0x6c, 0x45, 0x15, 0xf3, 0x47, 0x9a, 0xbb, 0xa8, 0x3e, 0x85, 0x72, - 0x36, 0x6b, 0x7a, 0x2d, 0x2b, 0xeb, 0xa1, 0x01, 0xfb, 0x75, 0xa6, 0xf3, 0x38, 0x45, 0xa7, 0xf1, - 0xca, 0x6b, 0x89, 0x19, 0xbd, 0x99, 0x74, 0xf1, 0xcc, 0x74, 0x9b, 0x53, 0x7b, 0x5b, 0x8c, 0xaf, - 0x12, 0xf8, 0xe2, 0xa4, 0xca, 0x9e, 0xda, 0xfd, 0x7f, 0x5d, 0xd4, 0xd7, 0xe2, 0xeb, 0x34, 0x5a, - 0x87, 0xc9, 0x0d, 0xfc, 0x32, 0xd5, 0x2e, 0x75, 0x09, 0xf2, 0xf0, 0xcb, 0xa6, 0xd2, 0xb6, 0xea, - 0x2b, 0xaf, 0xe1, 0xa0, 0x7d, 0x98, 0x12, 0x52, 0xa3, 0x5f, 0xe1, 0xc9, 0x1e, 0xe8, 0x91, 0x16, - 0x32, 0x9e, 0xd3, 0x24, 0x28, 0xbe, 0xf9, 0xef, 0xd9, 0xda, 0x82, 0x85, 0x34, 0xf7, 0x78, 0x6b, - 0xef, 0xf5, 0x9a, 0x7b, 0x66, 0x0a, 0x69, 0xeb, 0xeb, 0x60, 0x95, 0x9a, 0xa7, 0x24, 0x8c, 0xb4, - 0x37, 0xdc, 0x4b, 0x4e, 0x06, 0x75, 0x2d, 0x12, 0x93, 0xa1, 0x7a, 0xd0, 0x08, 0x8f, 0xdf, 0x3a, - 0xb5, 0xf0, 0xa9, 0x94, 0x78, 0xc7, 0x6e, 0xc3, 0x08, 0x2f, 0x4a, 0xbc, 0x4a, 0x8f, 0x57, 0xa5, - 0x00, 0xb0, 0xfe, 0xa8, 0x00, 0x17, 0xa9, 0xbd, 0xd1, 0xf5, 0x0e, 0x3a, 0x78, 0x27, 0xd4, 0x9d, - 0x76, 0xdf, 0xd5, 0x04, 0xcd, 0x7c, 0xc6, 0xa3, 0xaa, 0x5f, 0x96, 0x78, 0xf9, 0x93, 0x02, 0x94, - 0x4d, 0x7d, 0x7b, 0xb3, 0x12, 0xe6, 0x0e, 0x3f, 0xcc, 0x15, 0xb9, 0x25, 0x85, 0xa1, 0xcb, 0x36, - 0xc5, 0x60, 0xc9, 0x20, 0xc9, 0xff, 0x9a, 0x68, 0xf9, 0x3f, 0x05, 0x98, 0x5d, 0x0b, 0x55, 0x05, - 0x9f, 0x33, 0xee, 0x8e, 0xe9, 0x8d, 0x27, 0x9d, 0xd7, 0xd5, 0x01, 0xd3, 0x1b, 0xce, 0xf7, 0x94, - 0x47, 0x43, 0xc5, 0xbc, 0xc7, 0x9b, 0x44, 0x95, 0x94, 0x8f, 0x9f, 0x6e, 0xc2, 0xd0, 0x06, 0x51, - 0xa7, 0x06, 0xf9, 0xfa, 0x63, 0x18, 0xa4, 0x88, 0xbe, 0xef, 0x21, 0x5d, 0x26, 0x3f, 0xd0, 0xc3, - 0xd4, 0x2b, 0xa2, 0xa1, 0xde, 0x8f, 0x13, 0x57, 0x07, 0x92, 0x0f, 0x8a, 0x6a, 0xa3, 0x70, 0x6e, - 0xdb, 0x09, 0x0e, 0x70, 0x64, 0xfd, 0x00, 0xca, 0xdc, 0x21, 0x87, 0x59, 0x70, 0xa9, 0xdb, 0x4e, - 0x18, 0xfb, 0x5c, 0xe5, 0x39, 0xd1, 0x5c, 0x01, 0x68, 0x44, 0x4e, 0x10, 0xad, 0x79, 0x6d, 0xfc, - 0x35, 0x73, 0xf8, 0xb2, 0x95, 0x12, 0xeb, 0x7d, 0x18, 0x93, 0x43, 0xa0, 0x27, 0x40, 0x45, 0x63, - 0xa4, 0xc3, 0x99, 0xd5, 0xde, 0x35, 0x89, 0xc7, 0x4c, 0x0f, 0x60, 0x2e, 0x31, 0x15, 0xf1, 0x3b, - 0x3b, 0x79, 0x32, 0xa3, 0xbe, 0x69, 0xb6, 0xfc, 0x6d, 0xd5, 0xe1, 0x7c, 0x6a, 0xa6, 0x11, 0xa2, - 0x4f, 0xe0, 0xd8, 0xe9, 0x9e, 0x6c, 0x28, 0x8d, 0xc6, 0x2a, 0x29, 0xdb, 0x5e, 0x6f, 0x30, 0xbf, - 0x74, 0x52, 0xb6, 0xbd, 0xde, 0xa8, 0x9d, 0x63, 0x2b, 0xc7, 0xfa, 0xe7, 0x45, 0x7a, 0xe8, 0x4d, - 0xf1, 0x20, 0x61, 0x3f, 0x54, 0x6d, 0x98, 0x35, 0x18, 0xa3, 0x23, 0x5e, 0x16, 0x2f, 0x2f, 0xf2, - 0x7d, 0x48, 0x46, 0x7f, 0x76, 0x52, 0x19, 0xa0, 0x8e, 0x23, 0x31, 0x1a, 0xfa, 0x0c, 0x46, 0x56, - 0xbc, 0x36, 0xa5, 0x30, 0x78, 0x06, 0x0a, 0x02, 0x89, 0xcc, 0x03, 0xed, 0x32, 0x51, 0x85, 0xb8, - 0xd9, 0xc9, 0x56, 0x4a, 0x28, 0x9b, 0xa9, 0x4f, 0xde, 0x30, 0x9d, 0x22, 0xf6, 0x83, 0xbe, 0x5a, - 0x24, 0x5d, 0x10, 0x91, 0x10, 0xc6, 0x6c, 0xf9, 0x1b, 0x59, 0x30, 0xbc, 0x19, 0xb4, 0xf9, 0x6b, - 0xe6, 0xa9, 0xa5, 0x09, 0xbe, 0xbc, 0x68, 0x99, 0xcd, 0xaa, 0xac, 0xff, 0x55, 0x80, 0xf9, 0x47, - 0x38, 0x32, 0xae, 0x1b, 0x8d, 0x2b, 0x85, 0x6f, 0xcc, 0x95, 0xe2, 0xeb, 0x70, 0x45, 0x8e, 0x7a, - 0x30, 0x6b, 0xd4, 0x43, 0x59, 0xa3, 0x1e, 0xce, 0x1e, 0xf5, 0x23, 0x38, 0xc7, 0x86, 0x8a, 0xae, - 0xc3, 0xf0, 0x5a, 0x84, 0x8f, 0x62, 0x63, 0x88, 0xea, 0x01, 0x67, 0xb3, 0x3a, 0x72, 0xe2, 0x5a, - 0x77, 0xc2, 0x48, 0xbc, 0x84, 0x18, 0xb3, 0xc5, 0x4f, 0xeb, 0xc7, 0xf4, 0xcd, 0xd6, 0xba, 0xdf, - 0x7a, 0xae, 0x58, 0xaa, 0x47, 0xd8, 0x57, 0x99, 0xbc, 0xd9, 0x20, 0x50, 0xac, 0xc6, 0x16, 0x10, - 0xe8, 0x2a, 0x8c, 0xaf, 0x79, 0x0f, 0xfd, 0xa0, 0x85, 0x37, 0xbd, 0x0e, 0xa3, 0x3e, 0x6a, 0xab, - 0x45, 0xdc, 0x82, 0xc3, 0x5b, 0x88, 0x2d, 0x38, 0xb4, 0x20, 0x61, 0xc1, 0x21, 0x65, 0xbb, 0x4b, - 0x36, 0xab, 0xe3, 0x06, 0x22, 0xf2, 0x77, 0x9e, 0xf9, 0x46, 0xda, 0x79, 0x7a, 0x01, 0xee, 0xc3, - 0x45, 0x1b, 0x77, 0x3b, 0x0e, 0x51, 0xb8, 0x8e, 0x7c, 0x06, 0x2f, 0xc7, 0x7c, 0xd5, 0xe0, 0x3a, - 0xaf, 0xfb, 0x43, 0xc8, 0x2e, 0x17, 0x73, 0xba, 0x7c, 0x04, 0xd7, 0x1e, 0xe1, 0x48, 0x97, 0x72, - 0xb1, 0x1d, 0x9c, 0x0f, 0x7e, 0x15, 0x46, 0x43, 0xdd, 0x86, 0x7f, 0x45, 0x5c, 0x1d, 0x99, 0x10, - 0x77, 0x1f, 0x88, 0x5b, 0x2e, 0x4e, 0x47, 0xfe, 0x65, 0x7d, 0x0e, 0x95, 0xac, 0xe6, 0xfa, 0x73, - 0x57, 0x75, 0xe1, 0x6a, 0x36, 0x01, 0xb9, 0x2d, 0x0a, 0x7b, 0xbf, 0x3c, 0x3a, 0xe7, 0xf7, 0x56, - 0xbf, 0x22, 0xe0, 0x7f, 0x58, 0x35, 0xe1, 0xb8, 0xf7, 0x0d, 0xba, 0xdb, 0xa4, 0x57, 0xe9, 0x3a, - 0x81, 0x98, 0xaf, 0x55, 0x18, 0x15, 0x65, 0x9c, 0xaf, 0xf3, 0xc6, 0x9e, 0x0a, 0x86, 0xb6, 0x05, - 0x01, 0x89, 0x66, 0xfd, 0x58, 0x5c, 0x2b, 0xe9, 0x18, 0xfd, 0xbd, 0x07, 0xea, 0xe7, 0x1e, 0xc9, - 0xf2, 0xe1, 0xa2, 0x4e, 0x5b, 0xbd, 0x2e, 0x28, 0x29, 0xd7, 0x05, 0xec, 0x96, 0xe0, 0xaa, 0x6e, - 0xbe, 0x2e, 0xf2, 0x75, 0x19, 0x17, 0xa1, 0x2b, 0xea, 0xa5, 0xc0, 0x44, 0xfa, 0x01, 0xd5, 0x3d, - 0x28, 0x9b, 0x1a, 0x54, 0x0c, 0x28, 0xd2, 0xf2, 0xcc, 0xc3, 0x79, 0xfc, 0x56, 0x01, 0x2c, 0xcd, - 0x3b, 0x8a, 0xce, 0xd0, 0x56, 0xe0, 0xbf, 0x70, 0xdb, 0xca, 0x85, 0xd6, 0x3b, 0x42, 0xb0, 0x51, - 0x7f, 0x2c, 0xf6, 0x2c, 0x20, 0xe9, 0x54, 0xcd, 0xa5, 0xdd, 0x3d, 0x18, 0xd9, 0xc0, 0x5f, 0xc7, - 0xe2, 0x87, 0xe9, 0xa2, 0xd4, 0x63, 0xea, 0x39, 0x56, 0xdf, 0xc3, 0x0a, 0x30, 0xa2, 0x08, 0x5d, - 0xcf, 0xed, 0x03, 0xef, 0xff, 0x3e, 0x94, 0x92, 0x75, 0x7c, 0xee, 0x2b, 0x8a, 0x27, 0x48, 0x9a, - 0xc2, 0xee, 0x7d, 0xe6, 0x50, 0x2e, 0xbc, 0x8a, 0xba, 0x92, 0x72, 0x8a, 0xde, 0xd9, 0x7b, 0x8f, - 0x3e, 0x02, 0xd8, 0xf6, 0x23, 0xa7, 0x53, 0xa7, 0x36, 0x2e, 0x2a, 0xf8, 0xa9, 0xa7, 0xce, 0x5c, - 0x44, 0x4a, 0x9b, 0xc9, 0x87, 0xbb, 0x0a, 0xb0, 0xf5, 0x3d, 0xfa, 0x45, 0x9a, 0x3b, 0xdd, 0xdf, - 0x47, 0x52, 0x87, 0xeb, 0x09, 0x6f, 0x84, 0xd7, 0x20, 0x12, 0xc1, 0x1c, 0x61, 0x3f, 0x51, 0x61, - 0x1e, 0x05, 0xfe, 0x71, 0xf7, 0x57, 0x33, 0xeb, 0xff, 0xb1, 0xc0, 0x5c, 0x28, 0xd5, 0x66, 0xf9, - 0x44, 0xd7, 0x01, 0xe2, 0xd2, 0x84, 0x2b, 0xbd, 0xac, 0xd8, 0xbd, 0xcf, 0x0e, 0xaf, 0xf4, 0x9e, - 0xe2, 0x80, 0x11, 0x50, 0xd0, 0x7e, 0xb5, 0x33, 0xf9, 0x80, 0xba, 0x20, 0xc8, 0xd6, 0xfb, 0xe3, - 0xfb, 0x07, 0xc2, 0x46, 0x73, 0x46, 0xbc, 0x43, 0x98, 0x25, 0xdf, 0x2e, 0x39, 0xa1, 0xf8, 0x81, - 0x1b, 0xbd, 0x12, 0x58, 0x5b, 0xfc, 0x0d, 0x26, 0xc3, 0xfa, 0xf4, 0x17, 0x27, 0x95, 0x0f, 0xcf, - 0xf2, 0xa0, 0x4d, 0xd0, 0xdc, 0x96, 0xef, 0x36, 0xad, 0x79, 0x18, 0xac, 0xdb, 0xeb, 0x54, 0x54, - 0xd9, 0xeb, 0x52, 0x54, 0xd9, 0xeb, 0xd6, 0xff, 0x2c, 0x42, 0x85, 0x3d, 0xdb, 0xa6, 0x9e, 0x2b, - 0xf1, 0x59, 0x49, 0x71, 0x85, 0xe9, 0xd7, 0x42, 0x90, 0x78, 0x96, 0x5d, 0xec, 0xe7, 0x59, 0xf6, - 0x6f, 0xbc, 0xbe, 0x55, 0xb5, 0xf6, 0xf6, 0xe9, 0x49, 0xe5, 0x7a, 0x6c, 0x18, 0x60, 0xb5, 0x26, - 0x0b, 0x41, 0x46, 0x13, 0x69, 0x93, 0xc6, 0xd0, 0x6b, 0x98, 0x34, 0xee, 0xc1, 0x08, 0x3d, 0x7a, - 0xac, 0x6d, 0x71, 0x7f, 0x4b, 0xba, 0x3c, 0x69, 0x84, 0x85, 0xa6, 0xab, 0x86, 0x6e, 0x11, 0x60, - 0xd6, 0x3f, 0x2a, 0xc2, 0xd5, 0x6c, 0x9e, 0xf3, 0xbe, 0x2d, 0x03, 0xc4, 0x3e, 0x33, 0x79, 0x3e, - 0x3a, 0xf4, 0xdb, 0x79, 0x89, 0xf7, 0xa5, 0x8f, 0x9c, 0x82, 0x47, 0xb4, 0x16, 0xf1, 0xd8, 0x29, - 0x71, 0x1b, 0xa6, 0xbd, 0x81, 0xe2, 0x91, 0xc1, 0x78, 0x91, 0x16, 0x19, 0x8c, 0x97, 0xa1, 0x7d, - 0x98, 0xdf, 0x0a, 0xdc, 0x17, 0x4e, 0x84, 0x9f, 0xe0, 0x57, 0x5b, 0x7e, 0xc7, 0x6d, 0xbd, 0x5a, - 0xe1, 0x4f, 0x7d, 0xd8, 0x0b, 0xb6, 0x5b, 0xa7, 0x27, 0x95, 0xb7, 0xba, 0x0c, 0x84, 0x7c, 0x98, - 0xcd, 0x2e, 0x05, 0x6a, 0xa6, 0x5f, 0xff, 0x64, 0x11, 0xb2, 0xfe, 0x43, 0x01, 0x2e, 0x51, 0x85, - 0x9a, 0xdf, 0x2c, 0x88, 0xc6, 0x5f, 0xcb, 0x55, 0x53, 0x1d, 0x20, 0x5f, 0x8b, 0xd4, 0x55, 0x53, - 0x7b, 0x0c, 0x66, 0x6b, 0x60, 0x68, 0x0d, 0xc6, 0xf9, 0x6f, 0xc5, 0x7c, 0x3c, 0xa7, 0x08, 0x2c, - 0xba, 0xd4, 0x99, 0xf5, 0x88, 0x2e, 0x6c, 0x4e, 0xac, 0x49, 0x9f, 0x48, 0xab, 0xb8, 0xd6, 0xcf, - 0x8b, 0xb0, 0xb8, 0x8b, 0x03, 0xf7, 0xd9, 0xab, 0x8c, 0xc1, 0x6c, 0xc2, 0xac, 0x28, 0xa2, 0x63, - 0xd6, 0x3f, 0x31, 0x16, 0x7b, 0x48, 0x74, 0x35, 0x24, 0x00, 0x4d, 0xf9, 0xc5, 0x19, 0x11, 0xcf, - 0xe0, 0x84, 0xf9, 0x1e, 0x8c, 0x26, 0x82, 0x27, 0xd0, 0xf9, 0x17, 0x5f, 0x68, 0x3c, 0x55, 0xab, - 0x03, 0xb6, 0x84, 0x44, 0xbf, 0x9d, 0x7d, 0x45, 0xc9, 0x2d, 0x09, 0xbd, 0x82, 0xe2, 0xd0, 0x0f, - 0x96, 0x7c, 0xac, 0x8e, 0x52, 0x6b, 0xf8, 0x60, 0x57, 0x07, 0xec, 0xac, 0x96, 0x6a, 0xe3, 0x30, - 0x56, 0xa5, 0xd7, 0xae, 0xe4, 0xe0, 0xfe, 0xbf, 0x8b, 0x70, 0x45, 0x3c, 0xb7, 0xc9, 0x60, 0xf3, - 0x97, 0x30, 0x2f, 0x8a, 0xaa, 0x5d, 0xa2, 0x30, 0xe0, 0xb6, 0xce, 0x69, 0x16, 0xff, 0x4b, 0x70, - 0xda, 0xe1, 0x30, 0x31, 0xb3, 0xb3, 0xd0, 0xdf, 0x8c, 0x41, 0xf4, 0x33, 0x53, 0x28, 0x0b, 0x6a, - 0x98, 0x54, 0x65, 0xa6, 0xc6, 0x1a, 0x4d, 0x7e, 0xb6, 0x53, 0x06, 0xd5, 0xa1, 0x6f, 0x6a, 0x50, - 0x5d, 0x1d, 0x48, 0x9a, 0x54, 0x6b, 0x53, 0x30, 0xb1, 0x81, 0x5f, 0xc6, 0x7c, 0xff, 0x7b, 0x85, - 0xc4, 0x6b, 0x4b, 0xa2, 0x61, 0xb0, 0x67, 0x97, 0x85, 0x38, 0x1a, 0x02, 0x7d, 0x6d, 0xa9, 0x6a, - 0x18, 0x0c, 0x74, 0x0d, 0x46, 0x98, 0xdb, 0x6e, 0xbb, 0x8f, 0xb3, 0xb9, 0x7c, 0x37, 0xd3, 0x62, - 0x28, 0xec, 0x98, 0xce, 0xf1, 0xad, 0x27, 0x70, 0x8d, 0x7b, 0x8d, 0xeb, 0x93, 0x4f, 0x1b, 0x3a, - 0xe3, 0xf6, 0x65, 0x39, 0x70, 0xe5, 0x11, 0x4e, 0x8a, 0x1e, 0xed, 0x5d, 0xd1, 0xe7, 0x30, 0xad, - 0x95, 0x4b, 0x8a, 0x54, 0x2b, 0x95, 0x6b, 0x48, 0x92, 0x4e, 0x42, 0x5b, 0x57, 0x4d, 0x4d, 0xa8, - 0x9d, 0xb5, 0x30, 0x0d, 0xe4, 0x15, 0xc4, 0xb7, 0xc8, 0xe1, 0x19, 0xa4, 0xde, 0x2d, 0xe5, 0xbb, - 0x66, 0x12, 0x8f, 0x85, 0x34, 0x12, 0x3b, 0xaf, 0xac, 0xb5, 0x26, 0x61, 0xbc, 0xee, 0x7b, 0x11, - 0xfe, 0x9a, 0xaa, 0x3a, 0xd6, 0x14, 0x4c, 0x88, 0xaa, 0x0e, 0x0e, 0x43, 0xeb, 0x8f, 0x07, 0xc1, - 0xe2, 0x8c, 0x35, 0x59, 0x4f, 0x05, 0x3f, 0xf6, 0x53, 0x9d, 0xe5, 0x1b, 0xd5, 0x05, 0xd5, 0x46, - 0x1c, 0xd7, 0xb2, 0x95, 0x47, 0xf5, 0xbc, 0x56, 0x5c, 0xaa, 0xad, 0xbc, 0xd4, 0xe8, 0x7f, 0x98, - 0x21, 0x26, 0xd9, 0xc7, 0x76, 0xe3, 0xf4, 0xa4, 0x72, 0x2d, 0x43, 0x4c, 0x6a, 0x74, 0xcd, 0x22, - 0xd3, 0xd6, 0xd8, 0xc0, 0x55, 0x0e, 0x24, 0x9f, 0x45, 0xca, 0x1a, 0xee, 0xc3, 0xc4, 0x0a, 0x9a, - 0x64, 0x00, 0xfa, 0x17, 0xa9, 0x80, 0xa2, 0x1d, 0x9d, 0x97, 0xfc, 0x7b, 0x14, 0x5e, 0x1b, 0x6a, - 0x15, 0xa3, 0xda, 0x55, 0x4a, 0x34, 0xaa, 0x1a, 0x19, 0xd5, 0x22, 0xfe, 0xfb, 0xd2, 0x77, 0x9f, - 0x6c, 0xa4, 0x6e, 0x07, 0xf3, 0x87, 0x2a, 0x62, 0x5a, 0x8e, 0xcd, 0xb7, 0xdf, 0x85, 0xbe, 0x64, - 0x34, 0x8d, 0xab, 0x8a, 0x39, 0x7a, 0xd6, 0x95, 0x8b, 0x89, 0xbe, 0x75, 0x52, 0x10, 0x2f, 0x16, - 0x52, 0x57, 0xc2, 0x67, 0xd5, 0x24, 0x6b, 0xda, 0x2d, 0x6e, 0x31, 0xe3, 0x16, 0x57, 0xbb, 0xf3, - 0x8a, 0x7a, 0x5c, 0xeb, 0x0e, 0x7e, 0xf3, 0x6b, 0xa0, 0x7f, 0x35, 0x02, 0xe7, 0xb7, 0x9c, 0x03, - 0xd7, 0x23, 0xb2, 0xc7, 0xc6, 0xa1, 0x7f, 0x1c, 0xb4, 0x30, 0xaa, 0xc2, 0x94, 0xee, 0x3c, 0xda, - 0xc3, 0x35, 0x96, 0x88, 0x57, 0xbd, 0x0c, 0x2d, 0xc1, 0x98, 0x7c, 0xd4, 0xc9, 0x65, 0xa2, 0xe1, - 0xb1, 0xe7, 0xea, 0x80, 0x1d, 0x83, 0xa1, 0x8f, 0x34, 0xab, 0xff, 0xb4, 0x7c, 0x9f, 0x4c, 0x61, - 0x97, 0x98, 0x77, 0x9f, 0xe7, 0xb7, 0x75, 0xb9, 0xce, 0x2c, 0xe7, 0x3f, 0x4e, 0x5d, 0x04, 0x0c, - 0x6b, 0x3d, 0x4e, 0x19, 0x5e, 0xe8, 0x96, 0x96, 0x19, 0x93, 0x30, 0x7d, 0x45, 0x80, 0x7e, 0x00, - 0xe3, 0x4f, 0x8e, 0xf7, 0xb1, 0xb8, 0xf2, 0x38, 0xc7, 0xc5, 0x7c, 0xd2, 0x25, 0x9a, 0xd7, 0xef, - 0x3e, 0x60, 0x73, 0xf0, 0xfc, 0x78, 0x1f, 0xa7, 0x83, 0x5d, 0x92, 0xef, 0x4b, 0x21, 0x86, 0x0e, - 0xa1, 0x94, 0xf4, 0x5e, 0xa6, 0x96, 0xe6, 0x5c, 0x9f, 0x6b, 0x1a, 0x06, 0x51, 0x09, 0xa9, 0xc9, - 0x7c, 0x2a, 0xb5, 0x46, 0x52, 0x54, 0xd1, 0x6f, 0xc2, 0x9c, 0xd1, 0xec, 0xc5, 0x03, 0x6b, 0xf6, - 0xb2, 0xa8, 0x51, 0xd9, 0x94, 0xe0, 0x9a, 0x78, 0x10, 0xa5, 0xb5, 0x6c, 0x6e, 0x05, 0xb5, 0x61, - 0x3a, 0xe1, 0x95, 0xcb, 0xe3, 0xfa, 0x66, 0xfb, 0xf9, 0x52, 0xf9, 0x2a, 0x62, 0xcc, 0x19, 0xdb, - 0x4a, 0x92, 0x44, 0xeb, 0x30, 0x26, 0x4f, 0xad, 0x34, 0xc0, 0x82, 0xf9, 0x84, 0xbe, 0x70, 0x7a, - 0x52, 0x99, 0x8d, 0x4f, 0xe8, 0x1a, 0xcd, 0x98, 0x00, 0xfa, 0x09, 0x5c, 0x93, 0x4b, 0x74, 0x33, - 0x30, 0xdb, 0x32, 0x78, 0xc8, 0xce, 0xdb, 0xc9, 0x15, 0x9e, 0x05, 0xbf, 0x7b, 0x7f, 0x75, 0xc0, - 0xee, 0x4d, 0xb6, 0x06, 0x30, 0x1a, 0xf0, 0x8f, 0xf2, 0xf1, 0xd0, 0xe8, 0x50, 0x69, 0x98, 0xad, - 0x1b, 0xe1, 0xed, 0xfc, 0x6f, 0x47, 0x60, 0x76, 0xdd, 0x0d, 0x23, 0xf1, 0xe1, 0x86, 0xf1, 0xae, - 0x3e, 0x21, 0xca, 0x94, 0x53, 0x37, 0x57, 0xc0, 0x59, 0x39, 0x95, 0x2e, 0x5a, 0xf0, 0x4f, 0x05, - 0x01, 0xbd, 0xaf, 0x5e, 0xf2, 0x14, 0x95, 0xa0, 0xa8, 0xa2, 0x50, 0x0d, 0x83, 0x15, 0xdf, 0xfe, - 0xbc, 0xa3, 0xdd, 0x31, 0xe4, 0x1a, 0x65, 0x1e, 0x24, 0x2f, 0x1e, 0x78, 0xcc, 0x32, 0xba, 0xdf, - 0xe9, 0x46, 0x90, 0xf8, 0x46, 0x62, 0x07, 0xce, 0xd1, 0x00, 0x43, 0xe2, 0x51, 0xf1, 0xdb, 0x5c, - 0xf6, 0x99, 0x98, 0xc0, 0x42, 0x11, 0xf1, 0x17, 0xc5, 0x34, 0x1e, 0x57, 0x87, 0x16, 0xa8, 0x71, - 0x84, 0x18, 0x08, 0xda, 0x86, 0x99, 0xad, 0x00, 0xb7, 0xb9, 0xcb, 0x6e, 0x37, 0xe0, 0x27, 0x54, - 0xf6, 0x6c, 0x90, 0x06, 0x08, 0xed, 0x8a, 0xea, 0x26, 0x96, 0xf5, 0xea, 0xe6, 0x61, 0x40, 0x47, - 0x2b, 0x30, 0xd5, 0xc0, 0x4e, 0xd0, 0x3a, 0x7c, 0x82, 0x5f, 0x91, 0x3d, 0x2f, 0x5c, 0x18, 0x89, - 0xa3, 0xea, 0x86, 0xb4, 0x86, 0x0c, 0x94, 0x56, 0xa9, 0x77, 0xff, 0x3a, 0x12, 0xfa, 0x1e, 0x9c, - 0x6b, 0xf8, 0x41, 0x54, 0x7b, 0xc5, 0xbf, 0x51, 0x61, 0xf0, 0x67, 0x85, 0xb5, 0x8b, 0x22, 0xb2, - 0x70, 0xe8, 0x07, 0x51, 0x73, 0x5f, 0xe5, 0x1b, 0xc7, 0x43, 0x0f, 0x89, 0x42, 0x4d, 0x94, 0x7c, - 0x69, 0x3f, 0x62, 0x41, 0x49, 0xb8, 0xd2, 0x4c, 0x4f, 0x06, 0x26, 0x23, 0x52, 0x02, 0x0b, 0xbd, - 0x82, 0x59, 0xfd, 0xb3, 0x7e, 0xe8, 0x76, 0x88, 0x2c, 0x04, 0xee, 0x5a, 0x65, 0x92, 0x1d, 0x0c, - 0xa4, 0x76, 0x8b, 0xf7, 0xf2, 0x6a, 0x52, 0x78, 0x3c, 0xa3, 0xf5, 0x6a, 0x7c, 0x73, 0x13, 0x3e, - 0x7a, 0x4a, 0x03, 0x3b, 0x33, 0xce, 0x54, 0x43, 0x11, 0xa8, 0x9b, 0x0c, 0x82, 0x46, 0xd4, 0x38, - 0xa6, 0xa2, 0x81, 0x72, 0xd4, 0x09, 0x93, 0xf1, 0xba, 0xed, 0x14, 0x2a, 0xda, 0x82, 0xf3, 0x3b, - 0x21, 0xde, 0x0a, 0xf0, 0x0b, 0x17, 0xbf, 0x14, 0xf4, 0x26, 0x28, 0x3d, 0x3a, 0xdd, 0x84, 0x5e, - 0x97, 0xd5, 0x9a, 0x08, 0xa6, 0x91, 0xcb, 0x1f, 0xc1, 0xb8, 0xb2, 0xde, 0x0c, 0xcf, 0xd3, 0x67, - 0xd5, 0xe7, 0xe9, 0x63, 0xea, 0x33, 0xf4, 0xbf, 0x2a, 0x30, 0x1b, 0xa7, 0xb2, 0x80, 0xb9, 0xc1, - 0x64, 0x13, 0xc6, 0x64, 0xa1, 0x7c, 0x65, 0x21, 0x94, 0xae, 0xc4, 0xa6, 0xcd, 0x3e, 0x1f, 0xf1, - 0x75, 0xab, 0xbd, 0x8d, 0x69, 0xfc, 0x6a, 0xed, 0x8e, 0xbf, 0x1d, 0xbf, 0xc7, 0xe4, 0x6f, 0x47, - 0x03, 0xa7, 0xf5, 0x3c, 0x36, 0xfc, 0xfe, 0x88, 0x7c, 0x1f, 0x6a, 0x05, 0x8d, 0x55, 0x13, 0xef, - 0xd9, 0x7a, 0xe5, 0xee, 0x7d, 0xf1, 0xe1, 0xf0, 0x67, 0xa9, 0xac, 0x58, 0xff, 0x70, 0x54, 0x04, - 0xea, 0x85, 0x3c, 0x6d, 0xd9, 0xec, 0x39, 0xa1, 0xb1, 0x07, 0x1f, 0xa4, 0x1f, 0xc4, 0xd1, 0x4d, - 0x21, 0x7e, 0x10, 0xa7, 0xb2, 0x31, 0x7e, 0x1a, 0xb7, 0x03, 0x97, 0x6c, 0x7c, 0xe4, 0xbf, 0xc0, - 0x6f, 0x96, 0xec, 0x0f, 0xe1, 0xa2, 0x4e, 0x70, 0xa7, 0xdb, 0xa6, 0xe1, 0x3e, 0xd8, 0xf5, 0xaf, - 0x31, 0x9c, 0x21, 0x47, 0x60, 0xe1, 0x0c, 0x59, 0x80, 0x2b, 0xf2, 0xa7, 0x2a, 0x6f, 0x69, 0x9d, - 0xe5, 0xc3, 0xa2, 0x4e, 0xbc, 0xda, 0x6e, 0xd3, 0xd4, 0x0a, 0x2d, 0xb7, 0xeb, 0x78, 0x11, 0xda, - 0x84, 0x71, 0xe5, 0x67, 0x42, 0x65, 0x53, 0x6a, 0xd8, 0xec, 0x77, 0xe3, 0x02, 0x55, 0xb5, 0x54, - 0xe0, 0x2c, 0x0c, 0x95, 0x24, 0x7b, 0x08, 0xcb, 0xd4, 0x36, 0x6b, 0x30, 0xa9, 0xfc, 0x94, 0x27, - 0x20, 0x1a, 0xaa, 0x54, 0x69, 0x41, 0x67, 0x98, 0x8e, 0x62, 0xb5, 0xa0, 0x6c, 0x62, 0x1a, 0x0d, - 0x43, 0xf1, 0x0a, 0xad, 0xc4, 0x01, 0x2d, 0x7a, 0x5f, 0xbb, 0x4f, 0x67, 0x05, 0xb3, 0xb0, 0x7e, - 0x77, 0x08, 0x2e, 0xf1, 0xc9, 0x78, 0x93, 0x33, 0x8e, 0x7e, 0x0c, 0xe3, 0xca, 0x1c, 0x73, 0xa6, - 0x5f, 0x15, 0x9e, 0x3a, 0x59, 0x6b, 0x81, 0xa9, 0x96, 0xc7, 0xb4, 0xa0, 0x99, 0x98, 0x6e, 0xa2, - 0x5a, 0xaa, 0xcb, 0xa6, 0x03, 0x53, 0xfa, 0x44, 0x73, 0xed, 0xfa, 0xba, 0xb1, 0x11, 0x1d, 0x54, - 0xc4, 0xc6, 0x6a, 0x37, 0x8d, 0xd3, 0x4d, 0x94, 0xe4, 0xc4, 0x22, 0xfa, 0x1a, 0xce, 0xa7, 0x66, - 0x99, 0x9f, 0x16, 0x6f, 0x1a, 0x1b, 0x4c, 0x41, 0xb3, 0x48, 0xf1, 0x01, 0x2d, 0xce, 0x6c, 0x36, - 0xdd, 0x08, 0x6a, 0xc3, 0x84, 0x3a, 0xf1, 0x5c, 0xfd, 0xbf, 0x96, 0xc3, 0x4a, 0x06, 0xc8, 0x94, - 0x22, 0xce, 0x4b, 0x3a, 0xf7, 0xaf, 0xf4, 0x13, 0xab, 0x06, 0x3c, 0x0a, 0xe7, 0xd8, 0x6f, 0x22, - 0x02, 0xb6, 0x02, 0x1c, 0x62, 0xaf, 0x85, 0x55, 0xa7, 0xab, 0x6f, 0x2a, 0x02, 0xfe, 0x7d, 0x01, - 0x16, 0x4c, 0x74, 0x1b, 0xd8, 0x6b, 0xa3, 0x2d, 0x28, 0x25, 0x1b, 0xe2, 0xab, 0xda, 0x12, 0xbb, - 0x42, 0x76, 0x97, 0xc8, 0x71, 0x20, 0xd5, 0xcd, 0x0d, 0x38, 0xaf, 0x94, 0x9d, 0xd1, 0xbb, 0x2d, - 0x8d, 0xaa, 0x9e, 0xe8, 0x57, 0xa9, 0x13, 0xdf, 0xb2, 0x7f, 0xe4, 0xb8, 0x1e, 0x51, 0x10, 0x95, - 0x98, 0x16, 0x10, 0x97, 0x72, 0xde, 0xb0, 0x53, 0x2f, 0x2d, 0x15, 0x9e, 0x9e, 0x12, 0xc4, 0xfa, - 0x94, 0x4a, 0x70, 0x7e, 0x56, 0x62, 0x6f, 0x8c, 0x24, 0xb1, 0xab, 0x30, 0xbc, 0xbd, 0xde, 0xa8, - 0x57, 0xf9, 0x8b, 0x25, 0xf6, 0xce, 0xb5, 0x13, 0x36, 0x5b, 0x8e, 0xcd, 0x2a, 0xac, 0x4f, 0x68, - 0x04, 0x43, 0x1e, 0xff, 0x4e, 0xe2, 0xdd, 0x80, 0x11, 0x5e, 0xc4, 0x31, 0xe9, 0x1d, 0x79, 0x87, - 0x43, 0x89, 0x3a, 0x6b, 0x4b, 0xe8, 0xd7, 0x1d, 0xec, 0x84, 0xca, 0xc6, 0xfc, 0x21, 0xd1, 0xcb, - 0x59, 0x19, 0xdf, 0x97, 0xa7, 0x64, 0x78, 0x59, 0x5a, 0xcc, 0xac, 0x00, 0x02, 0xc6, 0x96, 0x7f, - 0x59, 0xeb, 0xf4, 0x4d, 0xfa, 0xe6, 0xda, 0x72, 0x9d, 0x70, 0x95, 0x33, 0x4b, 0x4c, 0xc7, 0x5d, - 0xea, 0x40, 0x16, 0x61, 0xf5, 0xbd, 0x12, 0x65, 0x0d, 0xfd, 0xc8, 0x79, 0x24, 0x06, 0x05, 0xc4, - 0x7a, 0x20, 0x5f, 0xb8, 0x1b, 0xa8, 0x65, 0x45, 0x65, 0xdd, 0xa0, 0x6f, 0xf7, 0x1f, 0xd1, 0xdb, - 0xb7, 0x37, 0xd1, 0x89, 0x3f, 0x2a, 0xb0, 0x60, 0x00, 0x8d, 0x4d, 0x25, 0x80, 0xbd, 0xf7, 0xcc, - 0x57, 0x4c, 0x8c, 0x4a, 0x33, 0x4f, 0x5c, 0xaf, 0xad, 0x9a, 0x18, 0x9d, 0xe3, 0xe8, 0x50, 0xc4, - 0xdc, 0x6f, 0x3e, 0x77, 0xbd, 0xb6, 0x9d, 0x84, 0x46, 0x1f, 0xc1, 0xa4, 0x52, 0x24, 0xb7, 0x09, - 0x16, 0xff, 0x4f, 0x45, 0x77, 0xdb, 0xb6, 0x0e, 0x69, 0xfd, 0x7e, 0x11, 0x2e, 0xec, 0x74, 0x43, - 0xea, 0xd1, 0xb7, 0xe6, 0xbd, 0xc0, 0x5e, 0xe4, 0x07, 0xaf, 0xa8, 0x47, 0x12, 0x7a, 0x1f, 0x86, - 0x57, 0x71, 0xa7, 0xe3, 0xf3, 0x4f, 0xe8, 0xb2, 0xb0, 0xef, 0x25, 0xa1, 0x29, 0xd0, 0xea, 0x80, - 0xcd, 0xa0, 0xd1, 0x47, 0x30, 0xb6, 0x8a, 0x9d, 0x20, 0xda, 0xc7, 0x8e, 0xd8, 0x25, 0x45, 0x3c, - 0x3a, 0x05, 0x85, 0x03, 0x90, 0x93, 0xa4, 0xfc, 0x81, 0x96, 0x60, 0x68, 0xcb, 0xf7, 0x0e, 0xe4, - 0x93, 0x9f, 0x8c, 0x06, 0x09, 0xcc, 0xea, 0x80, 0x4d, 0x61, 0xd1, 0x53, 0x98, 0xac, 0x1e, 0x60, - 0x2f, 0x7a, 0x8a, 0x23, 0x87, 0x9c, 0x82, 0xb9, 0x34, 0xbd, 0x91, 0x85, 0xac, 0x01, 0xaf, 0x0e, - 0xd8, 0x3a, 0x76, 0x6d, 0x18, 0x06, 0x9f, 0x86, 0x07, 0xd6, 0x49, 0x01, 0x16, 0x96, 0xfd, 0x97, - 0x9e, 0x91, 0x31, 0xdf, 0xd1, 0x19, 0x23, 0xfc, 0x4e, 0x0d, 0xf0, 0x09, 0xd6, 0xbc, 0x07, 0x43, - 0x5b, 0xae, 0x77, 0x90, 0x10, 0x20, 0x06, 0x3c, 0x02, 0x45, 0x47, 0xe8, 0x7a, 0x07, 0x68, 0x5d, - 0x48, 0x6e, 0x7e, 0xb2, 0x1b, 0xd4, 0xb6, 0x0b, 0x03, 0xb6, 0x0a, 0x1d, 0x4b, 0x68, 0xf6, 0x5b, - 0x0c, 0xf0, 0x1d, 0x98, 0xcf, 0x68, 0x17, 0x4d, 0xc9, 0xcf, 0x62, 0x88, 0x7e, 0x0e, 0x6f, 0xc3, - 0x9c, 0x71, 0x0a, 0x52, 0x80, 0xff, 0xa3, 0x60, 0x58, 0x4b, 0x6c, 0xe4, 0x0b, 0x30, 0x22, 0xc2, - 0x96, 0x32, 0x95, 0x5f, 0xfc, 0xa4, 0xfe, 0x75, 0xf4, 0x8c, 0x1f, 0x07, 0x56, 0x13, 0xbf, 0xd1, - 0xae, 0xf2, 0x86, 0x7a, 0x90, 0x1e, 0x0d, 0x3f, 0xfe, 0x06, 0xb9, 0x85, 0x24, 0x2d, 0xd2, 0xe6, - 0xaa, 0x1f, 0x46, 0x9e, 0xbc, 0xfe, 0xb5, 0xe5, 0x6f, 0x74, 0x1b, 0x4a, 0x2b, 0x5f, 0x47, 0x38, - 0xf0, 0x9c, 0x0e, 0x0f, 0xe0, 0xc8, 0x93, 0xc6, 0xd8, 0xa9, 0x72, 0xeb, 0x2f, 0x8b, 0x34, 0xa0, - 0x5b, 0xce, 0x02, 0x23, 0x3c, 0xda, 0x6c, 0xf0, 0x31, 0x17, 0x37, 0x1b, 0x68, 0x11, 0xc6, 0x36, - 0x1b, 0x5a, 0x04, 0x57, 0x3b, 0x2e, 0x20, 0x8d, 0x93, 0x8e, 0x54, 0x83, 0xd6, 0xa1, 0x1b, 0xe1, - 0x56, 0x74, 0x1c, 0xf0, 0x77, 0xec, 0x76, 0xaa, 0x1c, 0x59, 0x30, 0xf1, 0xa8, 0xe3, 0xee, 0xb7, - 0x04, 0x31, 0x36, 0x10, 0xad, 0x0c, 0xdd, 0x84, 0xa9, 0x35, 0x2f, 0x8c, 0x9c, 0x4e, 0x87, 0x05, - 0xb8, 0xe5, 0xb9, 0xaf, 0xec, 0x44, 0x29, 0x69, 0xb7, 0xee, 0x7b, 0x91, 0xe3, 0x7a, 0x38, 0xb0, - 0x8f, 0xbd, 0xc8, 0x3d, 0xc2, 0xdc, 0xc5, 0x33, 0x55, 0x8e, 0xde, 0x83, 0x39, 0x59, 0xb6, 0x19, - 0xb4, 0x0e, 0x71, 0x18, 0x05, 0x34, 0x18, 0x38, 0x7d, 0xb1, 0x6b, 0x9b, 0x2b, 0x69, 0x0b, 0x1d, - 0xff, 0xb8, 0xbd, 0xe2, 0xbd, 0x70, 0x03, 0xdf, 0xa3, 0x19, 0x2d, 0x46, 0x79, 0x0b, 0x89, 0x72, - 0x6b, 0xcb, 0xf8, 0xed, 0x7d, 0x83, 0x85, 0x64, 0x9d, 0x16, 0x60, 0xd1, 0xf8, 0x79, 0x08, 0x11, - 0xac, 0x22, 0x17, 0x12, 0xab, 0xf0, 0x36, 0x0c, 0x51, 0x99, 0xcc, 0xce, 0x15, 0xe2, 0x9a, 0x83, - 0xe2, 0x33, 0x52, 0xa4, 0xd6, 0xa6, 0x30, 0xe8, 0x91, 0xb4, 0xbf, 0x0c, 0xd2, 0x5d, 0xef, 0x6e, - 0x52, 0xf2, 0x19, 0x1a, 0x57, 0xed, 0x30, 0xc2, 0xe2, 0xf2, 0x4d, 0x8e, 0xcb, 0x7f, 0x59, 0x80, - 0x4a, 0x0f, 0xa9, 0x20, 0xc7, 0x54, 0xe8, 0x63, 0x4c, 0x8f, 0xe5, 0x98, 0x98, 0x43, 0xe5, 0x52, - 0x7f, 0x92, 0xe7, 0x4d, 0x0f, 0xab, 0x0e, 0x28, 0xbd, 0x7f, 0xa0, 0x77, 0x61, 0xac, 0xd1, 0x58, - 0xd5, 0x8c, 0xf0, 0x49, 0xbb, 0xb8, 0x1d, 0x43, 0x58, 0x1f, 0xc0, 0x05, 0x49, 0x84, 0x45, 0x94, - 0x54, 0xbc, 0xb6, 0x79, 0xa2, 0x19, 0xe9, 0x2c, 0x1e, 0x17, 0x58, 0x7f, 0x31, 0x94, 0x42, 0x6c, - 0x1c, 0x1f, 0x1d, 0x39, 0xc1, 0x2b, 0x54, 0xd5, 0x11, 0x07, 0x7b, 0x6e, 0x95, 0xb5, 0xa1, 0x9f, - 0x9d, 0x54, 0x06, 0x14, 0xea, 0xe8, 0x2d, 0x98, 0xa4, 0x1f, 0xa4, 0xd7, 0xc2, 0xcc, 0x8c, 0x50, - 0x64, 0x4f, 0x38, 0xb5, 0x42, 0xb4, 0x0b, 0x93, 0x7c, 0xad, 0xd3, 0xdf, 0x62, 0x89, 0xdd, 0x4b, - 0x2e, 0x31, 0xad, 0x7b, 0x77, 0x34, 0x14, 0x36, 0x19, 0x3a, 0x19, 0xf4, 0x7d, 0x98, 0x12, 0x12, - 0x8d, 0x13, 0x1e, 0xa2, 0x84, 0xef, 0xe7, 0x13, 0xd6, 0x71, 0x18, 0xe5, 0x04, 0x21, 0xd2, 0x65, - 0x2e, 0x74, 0x39, 0xe5, 0xe1, 0x7e, 0xba, 0xac, 0xa1, 0xf0, 0x2e, 0x6b, 0x65, 0xe5, 0xef, 0x01, - 0x4a, 0x8f, 0xab, 0xd7, 0x6a, 0x9a, 0x54, 0x56, 0x53, 0xb9, 0x0a, 0x33, 0x86, 0x01, 0x9c, 0x89, - 0xc4, 0xf7, 0x00, 0xa5, 0x7b, 0x7a, 0x16, 0x0a, 0xd6, 0x2d, 0xb8, 0x29, 0x59, 0x20, 0x57, 0x83, - 0x46, 0x53, 0x1c, 0x16, 0x7e, 0xab, 0x08, 0x95, 0x1e, 0xa0, 0xe8, 0x8f, 0x0b, 0x49, 0x6e, 0xb3, - 0xd5, 0xf8, 0x51, 0x92, 0xdb, 0x66, 0x7c, 0x03, 0xdb, 0x6b, 0x1f, 0xff, 0x9d, 0xbf, 0x7e, 0xed, - 0xed, 0x36, 0x3d, 0x65, 0x67, 0xe7, 0xd6, 0x90, 0xca, 0x2d, 0x1b, 0x66, 0x35, 0x45, 0xa5, 0x1f, - 0xd9, 0x7d, 0x05, 0x80, 0x47, 0xba, 0x5e, 0xf7, 0x0f, 0xb8, 0xcf, 0xbb, 0x52, 0x62, 0x3d, 0x84, - 0xb9, 0x04, 0x4d, 0x7e, 0x80, 0x79, 0x17, 0xa4, 0x77, 0x2e, 0x25, 0x3a, 0x58, 0x3b, 0xff, 0x8b, - 0x93, 0xca, 0x24, 0xd9, 0x01, 0xef, 0xc4, 0x81, 0xdb, 0xc4, 0x5f, 0xd6, 0x53, 0xf5, 0x08, 0x56, - 0xed, 0x68, 0xaf, 0x95, 0xee, 0xc3, 0x39, 0x56, 0x92, 0x08, 0x8f, 0xa4, 0x42, 0x73, 0x99, 0xc0, - 0x01, 0xad, 0x39, 0xea, 0x91, 0x48, 0x7f, 0x54, 0x63, 0xdf, 0x77, 0x6b, 0x87, 0x85, 0xd4, 0x8c, - 0x8b, 0x65, 0x08, 0xa6, 0xa1, 0x6a, 0xec, 0xa3, 0x2f, 0xec, 0xcc, 0x02, 0xce, 0xf3, 0x5f, 0x76, - 0x70, 0xfb, 0x80, 0xa6, 0x7e, 0xaa, 0x4d, 0x70, 0x3b, 0xf3, 0x90, 0x43, 0x08, 0x50, 0x34, 0xeb, - 0x73, 0x98, 0xab, 0x77, 0xb0, 0x13, 0x24, 0xdb, 0x43, 0x37, 0x61, 0x84, 0x96, 0xe9, 0x57, 0xb7, - 0x0e, 0x29, 0xa2, 0x57, 0xb7, 0xbc, 0x92, 0x9c, 0xd9, 0x58, 0xd4, 0x1a, 0x75, 0x48, 0xf1, 0x71, - 0x69, 0x98, 0xfe, 0x4e, 0xf8, 0xb3, 0x19, 0x46, 0xcf, 0xe0, 0xac, 0xcf, 0xa8, 0xc3, 0x84, 0x29, - 0xeb, 0x57, 0x7f, 0x1e, 0x96, 0x7f, 0x0b, 0x16, 0xab, 0xdd, 0x2e, 0xf6, 0xda, 0x31, 0xe2, 0x76, - 0xe0, 0xf4, 0xe9, 0xb9, 0x8e, 0xaa, 0x30, 0x4c, 0xa1, 0xa5, 0xad, 0x89, 0x77, 0xd7, 0xd0, 0x1d, - 0x0a, 0xc7, 0xe3, 0x65, 0xd0, 0x06, 0x18, 0xa6, 0xd5, 0x86, 0xf9, 0xc6, 0xf1, 0xfe, 0x91, 0xcb, - 0x12, 0x6c, 0xd1, 0xd7, 0x1f, 0xa2, 0xed, 0x35, 0x11, 0x05, 0x99, 0x31, 0xe3, 0x56, 0x9c, 0xcd, - 0x4b, 0x4d, 0x35, 0xfb, 0xe2, 0xfe, 0x9d, 0x18, 0x95, 0x9e, 0x39, 0x58, 0x2b, 0xb4, 0x9a, 0x47, - 0x4a, 0xb6, 0x66, 0xe0, 0xbc, 0x7a, 0x6e, 0x67, 0x2b, 0x64, 0x0e, 0x66, 0xf4, 0xf3, 0x38, 0x2b, - 0xfe, 0x0a, 0x66, 0x99, 0xa1, 0x99, 0xc5, 0xbb, 0x5a, 0x8a, 0x43, 0x3b, 0x15, 0x77, 0x97, 0x12, - 0x77, 0xd7, 0xf4, 0x6e, 0x4d, 0x46, 0x32, 0xdc, 0x5d, 0x62, 0x4e, 0x6f, 0x2f, 0x96, 0x34, 0xab, - 0x4f, 0x71, 0x77, 0xa9, 0x36, 0xc2, 0xe3, 0x86, 0x10, 0xea, 0x6c, 0xfa, 0x7f, 0x29, 0xd4, 0x97, - 0xa8, 0x9f, 0xf5, 0x2a, 0x76, 0xa8, 0x4f, 0x84, 0xd9, 0x5b, 0x75, 0x0a, 0x8a, 0x6e, 0x5b, 0x68, - 0xd9, 0x6e, 0xdb, 0xfa, 0xb3, 0x02, 0xdc, 0x62, 0xba, 0x88, 0x19, 0x8f, 0x1e, 0xce, 0x33, 0x90, - 0xd1, 0x87, 0xc0, 0x32, 0xde, 0x70, 0x85, 0xcf, 0xe2, 0x3d, 0xcf, 0xa3, 0xc4, 0x10, 0x50, 0x15, - 0x26, 0x54, 0xe7, 0x89, 0xc4, 0x6b, 0xf8, 0x0c, 0x43, 0x91, 0x3d, 0x7e, 0xf4, 0xcc, 0x91, 0x0e, - 0x15, 0xcf, 0xe1, 0xd2, 0xca, 0xd7, 0x64, 0x41, 0xf0, 0xdd, 0x89, 0x5f, 0xf6, 0xc4, 0xde, 0x90, - 0xd3, 0xdb, 0x7c, 0xc5, 0xe8, 0x6a, 0x70, 0xb2, 0x98, 0x1c, 0x0f, 0xc4, 0x06, 0x27, 0xb5, 0xd7, - 0x31, 0x5b, 0x2b, 0xb3, 0xfe, 0xa2, 0x00, 0x8b, 0xe6, 0xd6, 0xb8, 0x60, 0x59, 0x83, 0xf3, 0x75, - 0xc7, 0xf3, 0x3d, 0xb7, 0xe5, 0x74, 0x1a, 0xad, 0x43, 0xdc, 0x3e, 0xee, 0x08, 0x9f, 0x12, 0x29, - 0x65, 0xc8, 0x71, 0x87, 0xa3, 0x0b, 0x10, 0x3b, 0x8d, 0x85, 0x3e, 0x80, 0x0b, 0xf4, 0x46, 0x9f, - 0xc9, 0xde, 0x0e, 0x0e, 0x24, 0x3d, 0xd6, 0xb3, 0x8c, 0x5a, 0x74, 0x0f, 0x66, 0xd8, 0xa6, 0xd2, - 0xde, 0xf1, 0xdc, 0x48, 0x22, 0xb1, 0x53, 0x91, 0xa9, 0xea, 0x76, 0x53, 0x75, 0x24, 0x41, 0x97, - 0x60, 0x7e, 0x79, 0x65, 0x77, 0xad, 0xbe, 0xd2, 0xdc, 0xfe, 0xfe, 0xd6, 0x4a, 0x53, 0x8f, 0x10, - 0x34, 0x0b, 0x25, 0xb5, 0x72, 0x7b, 0x73, 0x7b, 0x8b, 0xe5, 0xa1, 0x53, 0x4b, 0xf7, 0x56, 0x6a, - 0xd5, 0x9d, 0xed, 0xd5, 0x8d, 0xd2, 0xa0, 0x35, 0x34, 0x5a, 0x2c, 0x15, 0x6f, 0xff, 0x58, 0xf3, - 0x32, 0x41, 0x8b, 0xb0, 0xc0, 0xc1, 0x77, 0x1a, 0xd5, 0x47, 0xd9, 0x4d, 0xb0, 0xda, 0xa7, 0x0f, - 0xab, 0xa5, 0x02, 0xba, 0x0c, 0x17, 0xb5, 0xd2, 0xad, 0x6a, 0xa3, 0xb1, 0xb7, 0x69, 0x2f, 0xaf, - 0xaf, 0x34, 0x1a, 0xa5, 0xe2, 0xed, 0x5d, 0x2d, 0xaa, 0x0c, 0x69, 0xe1, 0xe9, 0xc3, 0x6a, 0xd3, - 0x5e, 0xf9, 0x62, 0x67, 0xcd, 0x5e, 0x59, 0x4e, 0xb7, 0xa0, 0xd5, 0x7e, 0x7f, 0xa5, 0x51, 0x2a, - 0xa0, 0x19, 0x98, 0xd6, 0x4a, 0x37, 0x36, 0x4b, 0xc5, 0xdb, 0x37, 0xf9, 0x83, 0x35, 0x34, 0x05, - 0xb0, 0xbc, 0xd2, 0xa8, 0xaf, 0x6c, 0x2c, 0xaf, 0x6d, 0x3c, 0x2a, 0x0d, 0xa0, 0x49, 0x18, 0xab, - 0xca, 0x9f, 0x85, 0xdb, 0x1f, 0xc3, 0x74, 0xe2, 0x2c, 0x40, 0x20, 0xa4, 0x1a, 0x5d, 0x1a, 0x20, - 0x3c, 0x92, 0x3f, 0xe9, 0x01, 0x8e, 0xa9, 0xf5, 0xa5, 0xc2, 0xd2, 0xef, 0xfd, 0x6e, 0x01, 0xc6, - 0xc9, 0x42, 0x17, 0xde, 0x06, 0x5f, 0x29, 0xaa, 0x33, 0x9f, 0x60, 0x1e, 0x72, 0x3d, 0x53, 0x4f, - 0xa6, 0x32, 0xaf, 0x9c, 0x63, 0x58, 0xa1, 0x00, 0xb7, 0x0a, 0xf7, 0x0a, 0xc8, 0xa6, 0xe6, 0xcb, - 0x84, 0x26, 0x29, 0x29, 0x9b, 0x95, 0xfd, 0xf2, 0xe5, 0x5c, 0x05, 0x14, 0xfd, 0x06, 0x58, 0x2a, - 0xcd, 0x0c, 0x7d, 0xeb, 0xdd, 0xfe, 0xf4, 0x2a, 0xd1, 0xe6, 0xcd, 0xfe, 0xc0, 0xd1, 0x63, 0x98, - 0x24, 0x9a, 0x88, 0x04, 0x43, 0x97, 0x92, 0x88, 0x8a, 0xf2, 0x53, 0x5e, 0x34, 0x57, 0xca, 0x88, - 0x8f, 0x13, 0x74, 0x20, 0xec, 0x18, 0x11, 0x22, 0xe1, 0xd6, 0x2c, 0x4a, 0xd8, 0xe5, 0x71, 0xf9, - 0x7c, 0xa2, 0x78, 0xf7, 0xfe, 0xbd, 0x02, 0x6a, 0xd0, 0xd7, 0x80, 0x9a, 0x4a, 0x83, 0x84, 0xfb, - 0x4b, 0x5a, 0xd7, 0x61, 0xbd, 0xa9, 0xc8, 0x18, 0xe6, 0x19, 0xba, 0xd0, 0x06, 0xa0, 0xb4, 0xa6, - 0x80, 0xae, 0xc6, 0xeb, 0xc0, 0xac, 0x44, 0x94, 0x2f, 0xa4, 0x6e, 0xa5, 0x56, 0xc8, 0x5e, 0x81, - 0x56, 0x60, 0x8a, 0xfb, 0x2c, 0x72, 0xdd, 0x05, 0xe5, 0x69, 0x3f, 0x99, 0x64, 0x1e, 0x51, 0x3e, - 0x49, 0xfd, 0x07, 0x95, 0xe3, 0x71, 0x24, 0x95, 0xa2, 0xf2, 0x25, 0x63, 0x1d, 0x1f, 0xdf, 0x43, - 0x98, 0xd2, 0x55, 0x29, 0x24, 0x26, 0xc8, 0xa8, 0x61, 0x65, 0x76, 0xa8, 0x09, 0xf3, 0x4f, 0x1d, - 0x97, 0x1a, 0x52, 0xf8, 0xdd, 0x87, 0xb8, 0xb9, 0x40, 0x95, 0x9c, 0xab, 0x8c, 0x06, 0xf6, 0xda, - 0xe5, 0x5e, 0xef, 0xe0, 0xe9, 0x67, 0xd3, 0x10, 0x1a, 0x81, 0x7e, 0xf3, 0x83, 0x2c, 0x3d, 0x2f, - 0x85, 0xe9, 0x32, 0xaf, 0x9c, 0x75, 0xff, 0x8c, 0x9e, 0x52, 0x95, 0x24, 0x41, 0x51, 0x59, 0x13, - 0x67, 0x26, 0xb7, 0x40, 0x3d, 0x67, 0x23, 0x37, 0x79, 0x91, 0x1c, 0xa2, 0x0c, 0xc6, 0x65, 0x12, - 0xbb, 0x57, 0x40, 0x5f, 0xd1, 0xaf, 0xda, 0x48, 0x6e, 0xcf, 0x8d, 0x0e, 0xb9, 0x23, 0xc5, 0x25, - 0x23, 0x01, 0xfe, 0xa1, 0xe4, 0x50, 0xb7, 0x61, 0xd6, 0x74, 0xe5, 0x2d, 0x19, 0x9a, 0x73, 0x1f, - 0x9e, 0xb9, 0x0a, 0x6c, 0xa2, 0x58, 0xb5, 0xb3, 0x27, 0x29, 0xe7, 0xc6, 0x35, 0x93, 0xe6, 0xa7, - 0x30, 0x45, 0x56, 0xc9, 0x13, 0x8c, 0xbb, 0xd5, 0x8e, 0xfb, 0x02, 0x87, 0x48, 0xc4, 0x88, 0x90, - 0x45, 0x59, 0xb8, 0xb7, 0x0a, 0xe8, 0x5b, 0x30, 0xbe, 0xe7, 0x44, 0xad, 0x43, 0xfe, 0xa4, 0x59, - 0xbc, 0x78, 0xa6, 0x65, 0x65, 0xf1, 0x8b, 0x56, 0xde, 0x2b, 0xa0, 0xef, 0xc2, 0xc8, 0x23, 0x1c, - 0x51, 0xf7, 0xc3, 0x6b, 0xf2, 0xf6, 0x87, 0x79, 0x5a, 0xac, 0x79, 0xd2, 0x27, 0x4a, 0x74, 0x38, - 0x69, 0xb6, 0x41, 0x77, 0x01, 0x98, 0x40, 0xa0, 0x14, 0x92, 0xd5, 0xe5, 0x54, 0xb7, 0xd1, 0x23, - 0xb2, 0xf1, 0x77, 0x70, 0x84, 0xfb, 0x6d, 0x32, 0x8b, 0x47, 0xeb, 0x30, 0x25, 0x83, 0x64, 0x6e, - 0x50, 0xff, 0x75, 0x2b, 0x41, 0x2c, 0x3c, 0x03, 0xb5, 0x8f, 0xc9, 0x57, 0xc1, 0xb2, 0x28, 0xc8, - 0xf8, 0x19, 0x28, 0x2b, 0xa2, 0x86, 0x64, 0x22, 0x03, 0x53, 0x70, 0x57, 0xfd, 0x30, 0xd2, 0x71, - 0x65, 0x89, 0x19, 0x17, 0x43, 0x59, 0x6d, 0x57, 0x8f, 0xa5, 0x11, 0xcb, 0xdc, 0xac, 0x10, 0x20, - 0xe5, 0x6b, 0x39, 0x10, 0x4c, 0xdc, 0x51, 0x49, 0xb2, 0x4c, 0xce, 0xaa, 0xac, 0x19, 0x35, 0x11, - 0xbd, 0xb8, 0xf3, 0x49, 0xe7, 0xe6, 0x2f, 0xa3, 0x74, 0x15, 0xd9, 0xf5, 0xb4, 0x18, 0x0e, 0xf1, - 0xae, 0x67, 0x08, 0xb2, 0x11, 0xef, 0x7a, 0xc6, 0xb0, 0x0f, 0x4f, 0xd8, 0xe9, 0x59, 0x4b, 0x47, - 0xbd, 0xbb, 0x84, 0x84, 0x2f, 0xaa, 0x56, 0xc1, 0x3f, 0xec, 0x0b, 0xa6, 0xba, 0xdd, 0x07, 0xf7, - 0x0a, 0x68, 0x05, 0x66, 0xe4, 0x73, 0x83, 0xb8, 0x0a, 0x65, 0x20, 0xe4, 0xec, 0x30, 0x73, 0x06, - 0x32, 0xbb, 0x4b, 0x39, 0x84, 0x8c, 0xe5, 0xe8, 0x73, 0x98, 0xe1, 0x6b, 0x53, 0xeb, 0x4f, 0x49, - 0x8a, 0x19, 0x7e, 0xb3, 0x97, 0xd9, 0x93, 0xc7, 0x30, 0xd7, 0x48, 0x70, 0x87, 0x79, 0x42, 0x5c, - 0xd4, 0x49, 0x28, 0x29, 0x44, 0x33, 0x69, 0x3d, 0x01, 0xc4, 0x4e, 0xba, 0x82, 0xdc, 0x0b, 0x17, - 0xbf, 0x44, 0x97, 0x13, 0x5d, 0x27, 0x85, 0x14, 0x8c, 0xca, 0xa9, 0xcc, 0x91, 0x6d, 0xb3, 0x54, - 0x1f, 0x2c, 0x3d, 0x99, 0xd3, 0x75, 0xf6, 0xdd, 0x8e, 0x1b, 0xb9, 0x98, 0x2c, 0x55, 0x15, 0x41, - 0xad, 0x12, 0xeb, 0xe1, 0x62, 0x26, 0x04, 0xfa, 0x0c, 0x26, 0x1f, 0xe1, 0x28, 0xce, 0x92, 0x8a, - 0xe6, 0x53, 0x79, 0x55, 0xf9, 0x1a, 0x10, 0xaf, 0xe4, 0xf4, 0xd4, 0xac, 0x6b, 0x50, 0x62, 0x62, - 0x56, 0x21, 0x71, 0x39, 0x45, 0x82, 0x83, 0x38, 0x81, 0x73, 0x14, 0x66, 0x72, 0xeb, 0x2e, 0xbb, - 0xfe, 0x43, 0x62, 0xfd, 0xab, 0x7a, 0xdc, 0x8c, 0x56, 0x26, 0xa3, 0x1a, 0xcd, 0x19, 0xd3, 0x83, - 0xa2, 0xeb, 0xf1, 0x9e, 0x9a, 0x99, 0xf3, 0xb3, 0x8c, 0x92, 0x6f, 0xd8, 0x76, 0x1f, 0x20, 0x99, - 0x72, 0xc1, 0x40, 0xf4, 0xa6, 0xb6, 0xf5, 0x9f, 0x8d, 0xee, 0x67, 0x30, 0x26, 0xf3, 0x4c, 0x4a, - 0xf9, 0x94, 0xcc, 0xd2, 0x59, 0x5e, 0x48, 0x57, 0xf0, 0x91, 0x7e, 0xca, 0xb2, 0xca, 0xea, 0xf8, - 0xc9, 0x54, 0x8c, 0x99, 0x8c, 0xfd, 0x08, 0xc6, 0x95, 0x24, 0x8c, 0x72, 0x21, 0xa7, 0x13, 0x33, - 0x96, 0x27, 0x95, 0xbe, 0xef, 0x2e, 0xdd, 0x2b, 0xa0, 0xbb, 0x74, 0x8f, 0xa2, 0x8f, 0x38, 0xe6, - 0x62, 0x34, 0x25, 0x8d, 0x58, 0x02, 0x05, 0x7d, 0x87, 0xc6, 0xba, 0xa8, 0x1f, 0x07, 0x01, 0x39, - 0xf7, 0x12, 0xbc, 0x2c, 0x55, 0x24, 0x81, 0xf8, 0x19, 0x95, 0x4a, 0x0a, 0x22, 0xf3, 0xb4, 0xec, - 0x85, 0xcd, 0x62, 0xa5, 0xde, 0x2b, 0xa0, 0x07, 0x30, 0x2a, 0x72, 0x36, 0xa3, 0x0b, 0x7a, 0x57, - 0xb3, 0x87, 0xf7, 0x00, 0x80, 0x31, 0x9b, 0xf6, 0x54, 0xaf, 0xce, 0x64, 0xe7, 0x03, 0xb2, 0xf1, - 0xb6, 0xcf, 0x88, 0xf4, 0x99, 0xd8, 0x7c, 0x29, 0xd2, 0x82, 0x36, 0x85, 0x2a, 0x3b, 0xb3, 0xf0, - 0x89, 0xe6, 0xac, 0xa5, 0x92, 0x8e, 0x35, 0x67, 0x53, 0x86, 0xe9, 0x4c, 0x3a, 0x6b, 0x50, 0xaa, - 0xb6, 0xe8, 0x86, 0x20, 0x53, 0xb3, 0xc9, 0x63, 0x4b, 0xb2, 0x42, 0xd0, 0x9a, 0x4b, 0x66, 0x7a, - 0x5b, 0xc7, 0x0e, 0x0d, 0xff, 0x31, 0x2f, 0x95, 0x8b, 0x44, 0x95, 0x19, 0x23, 0xe7, 0x98, 0x32, - 0x5b, 0x27, 0x07, 0xab, 0xce, 0x37, 0x23, 0xf3, 0x31, 0x95, 0x65, 0x4a, 0xda, 0xba, 0x0b, 0x49, - 0x7c, 0x79, 0xa0, 0x13, 0x6e, 0x5d, 0x12, 0xb4, 0x0a, 0xd3, 0x3c, 0xd8, 0x80, 0x64, 0x4b, 0x16, - 0x76, 0x56, 0xf3, 0xdf, 0x81, 0xa9, 0x15, 0x22, 0xeb, 0x8f, 0xdb, 0x2e, 0x0b, 0x79, 0x84, 0xf4, - 0x18, 0x36, 0x99, 0x88, 0xab, 0x22, 0x0b, 0xad, 0x92, 0xcf, 0x4d, 0x7e, 0xa5, 0xe9, 0x94, 0x79, - 0xe5, 0x59, 0x41, 0x56, 0x4d, 0xfd, 0xc6, 0x4f, 0xfb, 0xf3, 0x19, 0x19, 0xd4, 0xd0, 0x0d, 0xed, - 0x10, 0x99, 0x95, 0x06, 0xcd, 0xa0, 0x36, 0x7e, 0xa9, 0x24, 0xab, 0xc8, 0xa0, 0x99, 0x9f, 0x5a, - 0x2d, 0x73, 0xdc, 0x32, 0x48, 0x89, 0x31, 0x05, 0x1a, 0x7a, 0x47, 0xa7, 0x9e, 0x93, 0x26, 0x2d, - 0xb3, 0x05, 0x7a, 0x48, 0xd7, 0x33, 0x74, 0xa1, 0x2b, 0xf9, 0x89, 0xc4, 0x94, 0x43, 0x7a, 0x46, - 0x6a, 0xaf, 0xc7, 0x74, 0x99, 0xc5, 0x19, 0x2d, 0x90, 0x7a, 0xe4, 0x4d, 0x26, 0xf4, 0x90, 0xba, - 0x98, 0x39, 0x4d, 0xd7, 0x16, 0x4c, 0x27, 0x12, 0x60, 0x49, 0xdb, 0x8c, 0x39, 0x05, 0x57, 0xf9, - 0x4a, 0x56, 0x35, 0xa7, 0xd8, 0x10, 0xe9, 0x9f, 0x95, 0x0e, 0x5e, 0xd1, 0x36, 0x96, 0x74, 0x1f, - 0x2b, 0x99, 0xf5, 0x72, 0xc8, 0xa5, 0x64, 0xc2, 0x12, 0x49, 0x34, 0x23, 0x93, 0x49, 0x8e, 0x24, - 0x9b, 0x55, 0x67, 0x54, 0x8e, 0x3b, 0x4b, 0xd2, 0x67, 0xd1, 0xd9, 0x86, 0x39, 0x63, 0x7e, 0x11, - 0xb9, 0xfb, 0xe7, 0x65, 0x1f, 0xc9, 0xa4, 0x8a, 0xe1, 0x82, 0x39, 0xc5, 0x10, 0x7a, 0x4b, 0x3f, - 0xfa, 0x9b, 0x13, 0xae, 0x94, 0x6f, 0xf4, 0x80, 0xe2, 0x0c, 0xfd, 0x8a, 0xee, 0x76, 0xa9, 0x36, - 0xae, 0x29, 0xc6, 0x80, 0x8c, 0x06, 0xac, 0x3c, 0x10, 0xb9, 0x06, 0x66, 0x4d, 0x29, 0xce, 0x32, - 0x59, 0x7c, 0x3d, 0x9b, 0x66, 0xbc, 0xb0, 0x76, 0x45, 0x44, 0x8f, 0x4c, 0xce, 0xe4, 0xa6, 0xa2, - 0xc9, 0x39, 0x4d, 0x96, 0xe5, 0x7a, 0xe8, 0xbf, 0xcb, 0xd9, 0x96, 0xa1, 0x59, 0x53, 0x02, 0xa4, - 0xa4, 0xe1, 0xc6, 0x94, 0xdf, 0x46, 0xb2, 0x21, 0x37, 0x83, 0xd2, 0x2e, 0x33, 0xe2, 0xe8, 0xd4, - 0x55, 0x23, 0x8e, 0x91, 0xf4, 0xd5, 0x6c, 0x80, 0x78, 0x45, 0x18, 0x32, 0xb9, 0xc9, 0x15, 0x91, - 0x9d, 0x53, 0x4e, 0xae, 0x88, 0xbc, 0x44, 0x70, 0xb6, 0xf8, 0xe8, 0x32, 0xd8, 0x92, 0x93, 0xf6, - 0x27, 0xe7, 0xa4, 0xb4, 0x10, 0x4f, 0x5c, 0xa2, 0xdb, 0x67, 0x9d, 0xb6, 0xaf, 0xe0, 0x62, 0x66, - 0x8a, 0x1f, 0xf4, 0x76, 0xea, 0x83, 0xce, 0xe0, 0x44, 0x76, 0x4f, 0x27, 0xb5, 0xec, 0x3c, 0xd2, - 0x8a, 0x95, 0x48, 0x04, 0x94, 0x92, 0xd8, 0x86, 0x2c, 0x41, 0x8f, 0xa8, 0x82, 0xab, 0x64, 0xfa, - 0xc9, 0x1c, 0xeb, 0x65, 0x13, 0x9d, 0x30, 0x2d, 0x53, 0x95, 0x7e, 0x09, 0x4d, 0x2c, 0x59, 0x71, - 0x16, 0x99, 0xda, 0x4f, 0xd7, 0xb2, 0xe8, 0x2c, 0xd3, 0x93, 0x82, 0x48, 0xfc, 0x83, 0x2e, 0x6a, - 0x6c, 0xd2, 0x76, 0xc9, 0xb2, 0x36, 0x38, 0x7d, 0x83, 0xac, 0x53, 0x73, 0xb1, 0x4c, 0x34, 0x94, - 0xd9, 0x8b, 0x4b, 0x69, 0x1a, 0x9a, 0xa9, 0x58, 0x72, 0x81, 0xf5, 0x66, 0x31, 0xc9, 0x1c, 0xad, - 0x43, 0xd9, 0x43, 0x42, 0x2a, 0x6b, 0x7a, 0x74, 0x29, 0x5b, 0x43, 0x9d, 0x61, 0x3a, 0x3f, 0x0b, - 0xb9, 0x27, 0x1e, 0xde, 0x5e, 0x90, 0x76, 0x2f, 0xa5, 0x34, 0xc7, 0xcc, 0xb1, 0x45, 0x3d, 0x40, - 0x0d, 0x39, 0x93, 0xa4, 0x0c, 0xcd, 0x4d, 0xa9, 0x64, 0xd0, 0xce, 0xa4, 0x54, 0xce, 0xa4, 0x98, - 0x9b, 0x44, 0x29, 0xb3, 0xa7, 0x3f, 0x52, 0xa4, 0x72, 0x2a, 0x33, 0x12, 0xba, 0x95, 0x54, 0xcd, - 0xb2, 0x92, 0x27, 0xe5, 0x48, 0xfd, 0x59, 0x53, 0x52, 0x25, 0xc5, 0x76, 0x9b, 0x99, 0x71, 0xc9, - 0xc0, 0x05, 0x29, 0xde, 0x32, 0xa8, 0xe5, 0xa4, 0x58, 0xca, 0xec, 0xe1, 0x0f, 0x14, 0xf1, 0x96, - 0x48, 0x85, 0x24, 0x6d, 0x01, 0x3d, 0x72, 0x25, 0x65, 0xd2, 0xde, 0xa0, 0x3e, 0xc3, 0xe9, 0x3c, - 0x46, 0x52, 0x77, 0xc9, 0xcb, 0x72, 0x64, 0x34, 0xed, 0xce, 0xa5, 0x87, 0x48, 0xe8, 0x5d, 0x48, - 0x18, 0x66, 0x7b, 0x75, 0x4c, 0xca, 0x61, 0x43, 0xfe, 0xa3, 0x84, 0x1c, 0xce, 0xce, 0x90, 0x94, - 0x73, 0xd0, 0x99, 0x6e, 0xb8, 0x07, 0x9e, 0x92, 0xbe, 0x48, 0x1e, 0x73, 0xd2, 0x19, 0x95, 0xa4, - 0x88, 0x31, 0x65, 0x3b, 0xda, 0x24, 0x1a, 0x0e, 0xd3, 0xcf, 0xd5, 0x44, 0x34, 0xa8, 0x9c, 0x9d, - 0x7f, 0x47, 0x8a, 0x1b, 0x63, 0xe6, 0x1a, 0x85, 0xa0, 0x9a, 0x05, 0x46, 0x12, 0x34, 0x24, 0xa4, - 0x91, 0x04, 0x8d, 0x69, 0x63, 0x98, 0xe5, 0xc4, 0xf6, 0x3b, 0x58, 0xb5, 0x9c, 0x28, 0x39, 0x5c, - 0x12, 0x26, 0x0c, 0xf4, 0x09, 0x35, 0x60, 0xe4, 0x5b, 0x3d, 0xe6, 0x75, 0x4a, 0xb1, 0xb4, 0x7c, - 0x20, 0x2e, 0x03, 0x68, 0x83, 0x3a, 0xe5, 0xde, 0x36, 0x09, 0x8a, 0xa4, 0xdb, 0x24, 0xd4, 0x8e, - 0x66, 0x9b, 0x37, 0x27, 0xd4, 0x18, 0xe0, 0x92, 0x57, 0x86, 0x44, 0x05, 0x92, 0x57, 0xa6, 0xf0, - 0xff, 0xf4, 0xe8, 0xba, 0x2d, 0x0e, 0xe0, 0x31, 0xbd, 0xcb, 0xb9, 0xf1, 0xfb, 0xcb, 0x57, 0xf2, - 0x83, 0xde, 0xf3, 0x7b, 0xbc, 0x52, 0x32, 0x4c, 0x39, 0x32, 0xa5, 0x5f, 0x50, 0xa2, 0xbf, 0xcb, - 0xd3, 0x50, 0x66, 0x7c, 0xf3, 0x2d, 0x61, 0x63, 0xd6, 0xe9, 0x66, 0x04, 0xe1, 0x57, 0x49, 0xe7, - 0x2b, 0x28, 0x71, 0xc4, 0x72, 0xf5, 0x48, 0x99, 0x8a, 0x88, 0xae, 0x2a, 0x28, 0x86, 0x20, 0xe7, - 0xae, 0x78, 0x35, 0x6b, 0x4e, 0x04, 0xf4, 0x8e, 0x7e, 0xd6, 0xcb, 0x89, 0x65, 0xd3, 0xf3, 0xa6, - 0x14, 0xfd, 0x9a, 0x48, 0x02, 0x9c, 0x4e, 0x78, 0x71, 0x23, 0x61, 0x2d, 0x35, 0x47, 0x3f, 0x29, - 0xe7, 0xe5, 0xd3, 0x40, 0x4f, 0xe9, 0x15, 0xfb, 0xe6, 0xda, 0x72, 0x9d, 0xfb, 0x02, 0xf8, 0x41, - 0xea, 0xda, 0x6a, 0xcf, 0x8d, 0x0e, 0x59, 0x06, 0x18, 0x45, 0xfa, 0x30, 0x10, 0x0d, 0x71, 0xf7, - 0x01, 0x6a, 0x50, 0xcd, 0x5d, 0x2b, 0x35, 0xdc, 0x5c, 0x19, 0x08, 0x96, 0xcd, 0x04, 0x69, 0x1e, - 0x3b, 0xaa, 0x18, 0x90, 0x0f, 0x4f, 0xef, 0x66, 0x46, 0x1f, 0xf2, 0xf4, 0x0b, 0xb6, 0x6c, 0xcc, - 0x64, 0xfa, 0x15, 0xdf, 0xf2, 0x1a, 0x25, 0xf1, 0x40, 0x4c, 0xeb, 0x8f, 0x52, 0x5e, 0xce, 0x28, - 0x47, 0x1b, 0xd4, 0x6d, 0x24, 0x59, 0xaa, 0x9c, 0x62, 0xcc, 0x2f, 0xd0, 0x32, 0xe9, 0xb1, 0xa9, - 0x24, 0x6a, 0xfb, 0x6b, 0x4d, 0xa5, 0x86, 0xb8, 0xbb, 0xc4, 0xa7, 0x52, 0x2b, 0x3d, 0xdb, 0x54, - 0x26, 0x08, 0xea, 0x53, 0xa9, 0x77, 0x33, 0xa3, 0x0f, 0xbd, 0xa7, 0xd2, 0x4c, 0xe6, 0xcc, 0x53, - 0x99, 0x78, 0x9d, 0xa7, 0xf5, 0xc7, 0x34, 0x95, 0x49, 0x78, 0x36, 0x95, 0xc9, 0xd2, 0xc4, 0x81, - 0x34, 0x67, 0x2a, 0x93, 0x98, 0x5f, 0x50, 0x7a, 0xec, 0xf9, 0xdf, 0x99, 0x26, 0x53, 0xc4, 0x68, - 0x49, 0xa0, 0xee, 0x3e, 0x40, 0x7b, 0xd4, 0x1a, 0x92, 0x28, 0xef, 0x6f, 0x42, 0x17, 0xb3, 0x88, - 0xd2, 0x29, 0x5d, 0x13, 0x7a, 0x56, 0xb2, 0xbb, 0x99, 0x7d, 0xc9, 0x9b, 0x0f, 0x36, 0xad, 0x49, - 0x52, 0x67, 0x9d, 0xd8, 0xa7, 0x42, 0x68, 0xa6, 0x5e, 0x50, 0x26, 0x7a, 0xa5, 0x4e, 0x6e, 0x66, - 0x0d, 0xda, 0xa6, 0xb6, 0x9e, 0x74, 0xb9, 0x62, 0x27, 0xca, 0x7a, 0xaa, 0xd9, 0x93, 0x6a, 0xea, - 0x49, 0xa6, 0x4a, 0x35, 0xeb, 0xbd, 0xa6, 0xa4, 0x9a, 0xc6, 0xfe, 0x9c, 0x9e, 0x9d, 0xb9, 0xf7, - 0xb9, 0xf7, 0xcc, 0xcf, 0x56, 0x74, 0x66, 0x34, 0x77, 0x06, 0x02, 0x4b, 0xbd, 0x48, 0x3e, 0xe5, - 0x16, 0x7e, 0x51, 0x98, 0xc9, 0x7c, 0x13, 0x3e, 0xfa, 0x1c, 0x4a, 0xfc, 0x03, 0x8f, 0x09, 0x98, - 0x00, 0x33, 0xa7, 0xae, 0x26, 0x8e, 0xec, 0x7d, 0xf4, 0xa0, 0x9f, 0xa3, 0x7a, 0x3f, 0x9c, 0xc8, - 0x3e, 0xd7, 0x12, 0x11, 0xb8, 0x1d, 0x90, 0x23, 0x67, 0x3b, 0x7d, 0x1e, 0xd5, 0x3b, 0x23, 0x2e, - 0x75, 0x75, 0xf0, 0xdd, 0x25, 0xb4, 0x46, 0x3f, 0x66, 0xbd, 0x38, 0xef, 0xc0, 0x6e, 0x26, 0x43, - 0xbf, 0xb5, 0x55, 0xe9, 0xe6, 0xac, 0xf7, 0x29, 0xab, 0xed, 0xec, 0x4e, 0x49, 0x16, 0xf5, 0x39, - 0xba, 0x2c, 0x16, 0x31, 0x8d, 0x9a, 0x19, 0x0f, 0x7a, 0x71, 0x26, 0xe9, 0x78, 0x8d, 0xbe, 0x07, - 0x63, 0x02, 0xb9, 0x37, 0x43, 0x92, 0xd8, 0x94, 0x21, 0xcb, 0x30, 0xa9, 0x79, 0x95, 0x4b, 0xa5, - 0xce, 0xe4, 0x6b, 0x9e, 0x33, 0xcf, 0x93, 0x9a, 0xf7, 0xb8, 0xa4, 0x62, 0xf2, 0x29, 0xcf, 0xa4, - 0xf2, 0x5d, 0x18, 0xe7, 0x2c, 0xcd, 0xe5, 0x46, 0xf6, 0x69, 0x7d, 0x4e, 0xf1, 0x59, 0x3c, 0x6e, - 0xbb, 0x51, 0xdd, 0xf7, 0x9e, 0xb9, 0x07, 0x3d, 0x19, 0x93, 0x46, 0xd9, 0x5d, 0x42, 0xbb, 0x34, - 0xfa, 0xbd, 0xc8, 0x49, 0x80, 0xa3, 0x97, 0x7e, 0xf0, 0xdc, 0xf5, 0x0e, 0x7a, 0x90, 0xbc, 0xaa, - 0x93, 0x4c, 0xe2, 0x31, 0xba, 0x8d, 0x6c, 0xba, 0x3d, 0xf1, 0x73, 0x4e, 0xeb, 0x8b, 0xf4, 0xba, - 0xff, 0xac, 0x3d, 0xce, 0xbe, 0xb9, 0xb8, 0x18, 0x7b, 0xfb, 0xd9, 0xb8, 0xe5, 0x07, 0xed, 0xde, - 0xc4, 0x2a, 0xba, 0x6f, 0x5d, 0x02, 0x6d, 0x77, 0x89, 0x50, 0x6d, 0x64, 0x52, 0xed, 0x85, 0x9d, - 0xb3, 0x5b, 0x5d, 0xa2, 0x63, 0x3f, 0x63, 0x6f, 0xf3, 0xa5, 0x16, 0xd9, 0x69, 0xb6, 0x02, 0xfc, - 0x0c, 0x07, 0xd4, 0x65, 0xb3, 0x97, 0xb3, 0xa2, 0x0e, 0xbe, 0xbb, 0x44, 0xa8, 0x34, 0x52, 0x54, - 0xb2, 0xa0, 0xf3, 0x14, 0x35, 0x3a, 0xb4, 0x3e, 0x7b, 0x93, 0x45, 0xe6, 0x43, 0x6a, 0x33, 0xdd, - 0x59, 0xeb, 0xc1, 0x11, 0xe1, 0x44, 0x2c, 0x00, 0x77, 0xef, 0x13, 0xcc, 0x86, 0x82, 0x99, 0x86, - 0xc8, 0x6c, 0xf3, 0x7b, 0xc2, 0x38, 0xda, 0xb3, 0xd9, 0x6c, 0x27, 0x86, 0x31, 0x99, 0x99, 0x07, - 0x29, 0x66, 0x05, 0x2d, 0xef, 0x4c, 0x79, 0x52, 0x75, 0x59, 0x0c, 0x51, 0x95, 0x69, 0xf1, 0x6a, - 0x86, 0x1a, 0xe5, 0x3a, 0xd5, 0x98, 0xba, 0x26, 0x49, 0x82, 0x99, 0x45, 0xd6, 0xfd, 0xd6, 0x73, - 0xd5, 0x2c, 0xa2, 0xa4, 0x3c, 0x29, 0xeb, 0x09, 0x49, 0xb8, 0x10, 0xa7, 0x59, 0x49, 0x54, 0xbf, - 0x0e, 0x35, 0xe9, 0x89, 0x6a, 0x16, 0xd1, 0xd3, 0xb3, 0x48, 0xb3, 0x08, 0x6d, 0x50, 0xa7, 0xdc, - 0xdb, 0x2c, 0x42, 0x91, 0x74, 0xb3, 0x88, 0xda, 0xd1, 0x6c, 0x71, 0x81, 0xd2, 0xf9, 0x59, 0xa4, - 0xc2, 0x9d, 0x99, 0xba, 0x25, 0xc7, 0x65, 0x63, 0xc6, 0x90, 0x52, 0x4a, 0x9a, 0x1b, 0xb2, 0xd3, - 0x4d, 0x95, 0x75, 0xff, 0x83, 0x7b, 0x05, 0xb4, 0x01, 0x17, 0x1e, 0xe1, 0x88, 0x0b, 0x30, 0x1b, - 0x87, 0x51, 0xe0, 0xd2, 0x47, 0x5b, 0xd9, 0x3b, 0x9c, 0xd0, 0xaf, 0x0d, 0x38, 0xbb, 0xef, 0x11, - 0x7a, 0x0d, 0x33, 0xbd, 0x5c, 0xbc, 0x1c, 0x8b, 0x12, 0x37, 0x3f, 0x9e, 0xa5, 0x8b, 0xd9, 0x4b, - 0x7c, 0x84, 0x5d, 0xba, 0x67, 0xa3, 0x96, 0xe2, 0x80, 0x8b, 0xfc, 0xc4, 0x70, 0x07, 0xce, 0x31, - 0xa4, 0xcc, 0x3d, 0x72, 0x42, 0xc5, 0x41, 0xf7, 0x85, 0x67, 0x17, 0x41, 0xd1, 0xaa, 0x32, 0xfb, - 0x75, 0x1f, 0xc6, 0xd8, 0x5d, 0x42, 0xff, 0x28, 0x9f, 0x08, 0xff, 0xaf, 0xbc, 0x8e, 0x65, 0x21, - 0x7f, 0x0e, 0x93, 0xea, 0x85, 0xfb, 0xd9, 0x19, 0xf9, 0x5d, 0x7a, 0x9f, 0x23, 0xcc, 0xa6, 0xd9, - 0xf8, 0x73, 0x89, 0x20, 0x9c, 0x9c, 0xa5, 0x4c, 0x40, 0xca, 0xdc, 0x6c, 0x59, 0xdd, 0x3f, 0x9f, - 0xc2, 0x46, 0x9f, 0x88, 0xe7, 0x0b, 0x12, 0x39, 0x0d, 0x94, 0xc3, 0xb3, 0x29, 0xc6, 0xe6, 0xd7, - 0x41, 0x96, 0x02, 0xb6, 0x67, 0xb7, 0xfb, 0xb9, 0x77, 0xea, 0xcd, 0xba, 0x2c, 0x2a, 0x9b, 0x54, - 0xf1, 0x4a, 0x85, 0x87, 0xcd, 0x26, 0x74, 0x25, 0x3b, 0xa2, 0x2c, 0x9d, 0x8c, 0xc7, 0xf4, 0x60, - 0x97, 0xce, 0xb3, 0x97, 0x35, 0xbc, 0x9c, 0x08, 0xb5, 0xf1, 0x49, 0x36, 0x4d, 0x2e, 0x07, 0x2d, - 0xef, 0x60, 0xcc, 0x1f, 0x54, 0xbd, 0x11, 0x72, 0x6b, 0xc2, 0x6f, 0xa9, 0xff, 0xc1, 0xe6, 0x28, - 0x41, 0x86, 0x9b, 0xae, 0x9e, 0x73, 0x91, 0x45, 0xee, 0xd7, 0xa8, 0xfe, 0x67, 0x4e, 0xae, 0x95, - 0x49, 0xec, 0x96, 0x72, 0x59, 0x9a, 0x9f, 0x96, 0xeb, 0x39, 0x7d, 0x17, 0x62, 0x0e, 0xa0, 0x7b, - 0xb3, 0x07, 0x15, 0xc1, 0x89, 0xb7, 0x7b, 0xc2, 0xc9, 0x7b, 0x93, 0x4b, 0x6c, 0x87, 0x35, 0xb7, - 0xd7, 0x23, 0x20, 0xb0, 0xe1, 0x2a, 0x2b, 0x23, 0x73, 0x95, 0x20, 0xa8, 0x3b, 0x85, 0xe5, 0x8e, - 0x21, 0x8b, 0xfd, 0x5f, 0x40, 0x25, 0xbe, 0x11, 0x3e, 0xdb, 0x24, 0x64, 0x6b, 0xf4, 0x28, 0x9d, - 0xcf, 0x0b, 0xe5, 0x45, 0x34, 0x2d, 0x5f, 0xcb, 0xe2, 0x70, 0xa8, 0xb8, 0x1a, 0x70, 0x5f, 0x96, - 0x44, 0x28, 0xe9, 0xac, 0xa0, 0xd4, 0x39, 0xb6, 0x2b, 0xfe, 0x50, 0xe6, 0x8d, 0x10, 0x4a, 0xcf, - 0xf6, 0xd9, 0x09, 0xc9, 0x0b, 0xdb, 0x04, 0x21, 0x2b, 0x67, 0x7a, 0xcf, 0xe2, 0x8f, 0x92, 0x9c, - 0x8a, 0xb3, 0x4e, 0xa8, 0x13, 0x3f, 0x0e, 0x49, 0x27, 0x1d, 0x93, 0xba, 0x5c, 0x66, 0x02, 0x34, - 0x39, 0xbb, 0x39, 0x19, 0xcb, 0xea, 0xe4, 0x33, 0x65, 0x4d, 0x68, 0x19, 0x8f, 0xea, 0xf6, 0x7a, - 0x6c, 0x29, 0x30, 0xa4, 0x42, 0x2a, 0x83, 0xa8, 0xb4, 0xd7, 0x51, 0x03, 0xca, 0x6c, 0x89, 0x98, - 0x1e, 0xe0, 0x4b, 0x47, 0x7c, 0x53, 0x65, 0xce, 0xe9, 0xa2, 0x01, 0x65, 0xb6, 0x5c, 0xde, 0x24, - 0xd1, 0x26, 0xcd, 0x6e, 0x69, 0xa4, 0x78, 0x43, 0x79, 0x16, 0x99, 0x1d, 0xd6, 0xa0, 0x9c, 0xdf, - 0x30, 0xfa, 0x21, 0xcc, 0x19, 0xe3, 0x1a, 0xc8, 0x3b, 0xf5, 0xbc, 0xa8, 0x07, 0xbd, 0x88, 0x3f, - 0x87, 0x85, 0xac, 0xf4, 0x44, 0xf1, 0xc3, 0x80, 0xfc, 0x9c, 0x51, 0x52, 0xa6, 0xf6, 0xcc, 0x73, - 0xb4, 0x01, 0xb3, 0xa6, 0x94, 0x3f, 0xf2, 0xe3, 0xc8, 0xc9, 0x07, 0x64, 0x7c, 0x7d, 0xb0, 0x05, - 0x73, 0xc6, 0xb4, 0x3b, 0x92, 0x33, 0x79, 0x49, 0x79, 0x8c, 0x14, 0xbf, 0x84, 0xf9, 0x8c, 0x1c, - 0x33, 0xf1, 0xc5, 0x5f, 0x6e, 0x0e, 0x9a, 0x1c, 0x07, 0x84, 0x72, 0x76, 0xfa, 0x12, 0xe9, 0x77, - 0xd2, 0x33, 0xc3, 0x49, 0xd9, 0x98, 0xd3, 0x09, 0x6d, 0xd3, 0x45, 0x68, 0xca, 0x67, 0xa2, 0x2e, - 0xc2, 0x9c, 0x7c, 0x27, 0x19, 0xaf, 0x46, 0xe6, 0x33, 0x52, 0x98, 0xe4, 0x50, 0xed, 0xa3, 0xb7, - 0x1b, 0x42, 0xfe, 0xeb, 0x39, 0x2d, 0x12, 0xbe, 0x8c, 0xc6, 0x84, 0x17, 0xc6, 0x7e, 0x2a, 0xcf, - 0x9d, 0x3b, 0x9d, 0x1c, 0x35, 0x08, 0xa9, 0xef, 0x9d, 0x09, 0x24, 0xb5, 0x9d, 0x4f, 0xaa, 0xb8, - 0x79, 0x12, 0x35, 0x85, 0x4c, 0x15, 0xcf, 0x8f, 0x61, 0xa2, 0xa1, 0x36, 0x6e, 0x68, 0x24, 0x73, - 0x51, 0x48, 0xe7, 0xfc, 0xde, 0x7d, 0xef, 0x79, 0x2b, 0x57, 0xed, 0x74, 0xfa, 0x1a, 0x45, 0xe6, - 0x45, 0xbd, 0x16, 0x6a, 0x5b, 0x4a, 0x6a, 0x53, 0x04, 0x79, 0x79, 0x51, 0x6f, 0x8e, 0xce, 0xbd, - 0x42, 0x59, 0x1a, 0x07, 0x2a, 0xcd, 0x39, 0x83, 0xcb, 0x45, 0x64, 0x88, 0x87, 0xfa, 0x44, 0x7d, - 0x88, 0xce, 0xc2, 0x9b, 0xe6, 0x18, 0x11, 0x93, 0x0f, 0xd0, 0x13, 0xf1, 0x50, 0x1f, 0x43, 0x29, - 0x19, 0x9b, 0x45, 0x5a, 0x78, 0x32, 0x82, 0xb6, 0xe4, 0xcc, 0x1a, 0xc4, 0x11, 0x58, 0xa4, 0x1d, - 0x25, 0x15, 0x94, 0xa5, 0x7c, 0xd1, 0x50, 0x23, 0x35, 0xa0, 0x09, 0x35, 0x5e, 0x8b, 0xf4, 0x30, - 0x31, 0x04, 0x71, 0x29, 0x5f, 0x32, 0xd6, 0x71, 0x42, 0x11, 0x5c, 0xca, 0xc9, 0xc2, 0x29, 0xb5, - 0xca, 0xde, 0xd9, 0x42, 0xcb, 0xb7, 0xfb, 0x01, 0xe5, 0xad, 0x62, 0x19, 0x5e, 0x35, 0x0d, 0x85, - 0xde, 0x36, 0xb8, 0x04, 0x9b, 0x12, 0x5c, 0x96, 0x7b, 0x25, 0x00, 0x45, 0x7b, 0xb0, 0x98, 0x70, - 0x59, 0xd6, 0x5b, 0xea, 0x45, 0x20, 0x73, 0x06, 0xf7, 0x60, 0x91, 0xbf, 0xa1, 0x7e, 0xc3, 0x84, - 0xf7, 0x61, 0x31, 0x2f, 0xb5, 0x27, 0xba, 0x6d, 0x76, 0x4b, 0x36, 0xb2, 0x27, 0x5b, 0xc5, 0xbc, - 0x9a, 0x76, 0x4f, 0x4e, 0xcc, 0xfb, 0x59, 0x3f, 0xff, 0xa7, 0x30, 0xa5, 0xa7, 0xf5, 0x44, 0xea, - 0x27, 0x9e, 0x4a, 0x32, 0x5a, 0xbe, 0x9c, 0x51, 0xcb, 0xd7, 0xc7, 0x67, 0x54, 0x20, 0xc7, 0xa9, - 0x42, 0xca, 0xfa, 0xbb, 0x35, 0x35, 0x6d, 0x66, 0xd9, 0x90, 0x81, 0x04, 0x7d, 0x17, 0xa6, 0xe3, - 0xe7, 0x6b, 0x8c, 0x84, 0x01, 0x2c, 0xc7, 0xae, 0x33, 0x1d, 0x3f, 0x64, 0x3b, 0x3b, 0xfa, 0xaa, - 0x90, 0xca, 0x31, 0xfa, 0xe5, 0x94, 0xa7, 0xb6, 0x36, 0x86, 0x7e, 0x84, 0xb3, 0xc2, 0xdb, 0xb3, - 0xce, 0x4e, 0x8b, 0x7e, 0x6e, 0xe6, 0x40, 0x44, 0xea, 0xe7, 0x96, 0x1b, 0x2c, 0x49, 0x6a, 0x82, - 0x19, 0x74, 0x9e, 0xc2, 0x75, 0xfa, 0x9c, 0x7f, 0x0b, 0x7b, 0x6d, 0xd7, 0x3b, 0x30, 0x43, 0x65, - 0xf7, 0x3d, 0x19, 0x04, 0xa0, 0x03, 0xd7, 0x7a, 0x46, 0x62, 0x42, 0x77, 0xb5, 0x80, 0x06, 0xbd, - 0x63, 0x36, 0xe5, 0xbd, 0x8e, 0x30, 0x05, 0x34, 0x92, 0x1a, 0x45, 0x4e, 0x6c, 0x25, 0xf9, 0x3a, - 0x22, 0x37, 0x22, 0xd2, 0x97, 0x34, 0x94, 0x33, 0xdf, 0x5b, 0x68, 0x88, 0x0e, 0xec, 0xb1, 0x10, - 0x8d, 0xb9, 0xb7, 0x14, 0xd7, 0xf4, 0xbb, 0xb9, 0x14, 0x22, 0x55, 0xef, 0xaf, 0xf0, 0x43, 0x49, - 0x16, 0xf1, 0xde, 0x44, 0xb2, 0xf8, 0x52, 0x5b, 0xfe, 0xd9, 0xdf, 0x5c, 0x29, 0xfc, 0xec, 0xe7, - 0x57, 0x0a, 0xff, 0xe9, 0xe7, 0x57, 0x0a, 0xff, 0xf5, 0xe7, 0x57, 0x0a, 0x3f, 0x58, 0xea, 0x2f, - 0xac, 0x5f, 0xab, 0xe3, 0x62, 0x2f, 0xba, 0xcb, 0xc8, 0x9d, 0xa3, 0xff, 0x3d, 0xf8, 0x7f, 0x01, - 0x00, 0x00, 0xff, 0xff, 0x36, 0x90, 0xee, 0xbd, 0xe6, 0xc4, 0x00, 0x00, + // 12680 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x7d, 0x5b, 0x6c, 0x1c, 0x4b, + 0x76, 0x18, 0x67, 0xf8, 0x3e, 0x7c, 0x8d, 0x8a, 0xa4, 0x48, 0x8d, 0x28, 0x8d, 0xd4, 0xda, 0xab, + 0xab, 0xab, 0xdd, 0xab, 0x07, 0x75, 0xef, 0xdd, 0xfb, 0xda, 0x7b, 0x77, 0x86, 0xa4, 0x44, 0x4a, + 0x14, 0xc9, 0xdb, 0xc3, 0xc7, 0xdd, 0xdd, 0xeb, 0x9d, 0x6d, 0xce, 0x94, 0xc8, 0x8e, 0x86, 0xdd, + 0xe3, 0xee, 0xa6, 0x74, 0xb5, 0x86, 0x93, 0x38, 0x4e, 0xfc, 0x11, 0x20, 0xb1, 0x03, 0xd8, 0x81, + 0x0d, 0x7f, 0x38, 0x41, 0xf2, 0x93, 0x00, 0x01, 0xf2, 0x63, 0xf8, 0x23, 0xfe, 0x09, 0x12, 0x20, + 0x9b, 0x00, 0x06, 0x02, 0xd8, 0x46, 0x80, 0x7c, 0xd0, 0xf1, 0x7e, 0x12, 0x49, 0x80, 0x24, 0x48, + 0x3e, 0x16, 0x08, 0x10, 0xd4, 0xb3, 0xab, 0xba, 0xab, 0x7b, 0x86, 0x12, 0x77, 0x9d, 0x1f, 0x89, + 0x53, 0x75, 0xce, 0xa9, 0xaa, 0x53, 0x55, 0xa7, 0xcf, 0x39, 0x75, 0xaa, 0x0e, 0xdc, 0x89, 0x70, + 0x1b, 0x77, 0xfc, 0x20, 0xba, 0xdb, 0xc6, 0x07, 0x4e, 0xf3, 0xd5, 0xdd, 0x66, 0xdb, 0xc5, 0x5e, + 0x74, 0xb7, 0x13, 0xf8, 0x91, 0x7f, 0xd7, 0x39, 0x8e, 0x0e, 0x43, 0x1c, 0xbc, 0x70, 0x9b, 0xf8, + 0x0e, 0x2d, 0x41, 0x83, 0xf4, 0xbf, 0xf2, 0xcc, 0x81, 0x7f, 0xe0, 0x33, 0x18, 0xf2, 0x17, 0xab, + 0x2c, 0x5f, 0x3e, 0xf0, 0xfd, 0x83, 0x36, 0x66, 0xc8, 0xfb, 0xc7, 0xcf, 0xee, 0xe2, 0xa3, 0x4e, + 0xf4, 0x8a, 0x57, 0x56, 0x92, 0x95, 0x91, 0x7b, 0x84, 0xc3, 0xc8, 0x39, 0xea, 0x70, 0x80, 0x77, + 0x64, 0x57, 0x9c, 0x28, 0x22, 0x35, 0x91, 0xeb, 0x7b, 0x77, 0x5f, 0xdc, 0x57, 0x7f, 0x72, 0xd0, + 0x5b, 0xb9, 0xbd, 0x6e, 0xe2, 0x20, 0x0a, 0x7b, 0x82, 0xc4, 0x2f, 0xb0, 0x17, 0xa5, 0x9a, 0xe7, + 0x90, 0xd1, 0xab, 0x0e, 0x0e, 0x19, 0x88, 0xf8, 0x8f, 0x83, 0x5e, 0x37, 0x83, 0xd2, 0x7f, 0x39, + 0xc8, 0xbb, 0x66, 0x90, 0x97, 0x78, 0x9f, 0xf0, 0xd4, 0x93, 0x7f, 0x74, 0x01, 0x0f, 0x9c, 0x4e, + 0x07, 0x07, 0xf1, 0x1f, 0xa9, 0xbe, 0x1e, 0x87, 0xce, 0x01, 0xe6, 0x7d, 0x7c, 0x71, 0x5f, 0xfd, + 0xc9, 0x40, 0xad, 0x3f, 0x28, 0xc0, 0xe0, 0x9e, 0x13, 0x35, 0x0f, 0xd1, 0xe7, 0x30, 0xf8, 0xc4, + 0xf5, 0x5a, 0xe1, 0x7c, 0xe1, 0x5a, 0xff, 0xad, 0xb1, 0xc5, 0xd2, 0x1d, 0xd6, 0x5f, 0x5a, 0x49, + 0x2a, 0x6a, 0x73, 0x3f, 0x39, 0xa9, 0xf4, 0x9d, 0x9e, 0x54, 0xa6, 0x9e, 0x13, 0xb0, 0x6f, 0xf9, + 0x47, 0x6e, 0x44, 0x27, 0xd0, 0x66, 0x78, 0x68, 0x07, 0xa6, 0xab, 0xed, 0xb6, 0xff, 0x72, 0xcb, + 0x09, 0x22, 0xd7, 0x69, 0xd7, 0x8f, 0x9b, 0x4d, 0x1c, 0x86, 0xf3, 0xc5, 0x6b, 0x85, 0x5b, 0x23, + 0xb5, 0x1b, 0xa7, 0x27, 0x95, 0x8a, 0x43, 0xaa, 0x1b, 0x1d, 0x56, 0xdf, 0x08, 0x19, 0x80, 0x42, + 0xc8, 0x84, 0x6f, 0xfd, 0xd7, 0x41, 0x28, 0xad, 0xfa, 0x61, 0xb4, 0x44, 0xa6, 0xcd, 0xc6, 0xbf, + 0x7c, 0x8c, 0xc3, 0x08, 0xdd, 0x80, 0x21, 0x52, 0xb6, 0xb6, 0x3c, 0x5f, 0xb8, 0x56, 0xb8, 0x35, + 0x5a, 0x1b, 0x3b, 0x3d, 0xa9, 0x0c, 0x1f, 0xfa, 0x61, 0xd4, 0x70, 0x5b, 0x36, 0xaf, 0x42, 0xef, + 0xc0, 0xc8, 0x86, 0xdf, 0xc2, 0x1b, 0xce, 0x11, 0xa6, 0xbd, 0x18, 0xad, 0x4d, 0x9c, 0x9e, 0x54, + 0x46, 0x3d, 0xbf, 0x85, 0x1b, 0x9e, 0x73, 0x84, 0x6d, 0x59, 0x8d, 0x76, 0x61, 0xc0, 0xf6, 0xdb, + 0x78, 0xbe, 0x9f, 0x82, 0xd5, 0x4e, 0x4f, 0x2a, 0x03, 0x81, 0xdf, 0xc6, 0x3f, 0x3b, 0xa9, 0x7c, + 0x70, 0xe0, 0x46, 0x87, 0xc7, 0xfb, 0x77, 0x9a, 0xfe, 0xd1, 0xdd, 0x83, 0xc0, 0x79, 0xe1, 0xb2, + 0x95, 0xe6, 0xb4, 0xef, 0xc6, 0xeb, 0xb1, 0xe3, 0xf2, 0xc9, 0xad, 0xbf, 0x0a, 0x23, 0x7c, 0x44, + 0x28, 0xd9, 0x94, 0x1e, 0xda, 0x83, 0x99, 0x6a, 0xab, 0xe5, 0x32, 0x8c, 0xad, 0xc0, 0xf5, 0x9a, + 0x6e, 0xc7, 0x69, 0x87, 0xf3, 0x03, 0xd7, 0xfa, 0x6f, 0x8d, 0x72, 0xa6, 0xc8, 0xfa, 0x46, 0x47, + 0x02, 0x28, 0x4c, 0x31, 0x12, 0x40, 0x0f, 0x60, 0x64, 0x79, 0xa3, 0x4e, 0xfa, 0x1e, 0xce, 0x0f, + 0x52, 0x62, 0x73, 0xa7, 0x27, 0x95, 0xe9, 0x96, 0x17, 0xd2, 0xa1, 0xa9, 0x04, 0x24, 0x20, 0xfa, + 0x00, 0xc6, 0xb7, 0x8e, 0xf7, 0xdb, 0x6e, 0x73, 0x7b, 0xbd, 0xfe, 0x04, 0xbf, 0x9a, 0x1f, 0xba, + 0x56, 0xb8, 0x35, 0x5e, 0x43, 0xa7, 0x27, 0x95, 0xc9, 0x0e, 0x2d, 0x6f, 0x44, 0xed, 0xb0, 0xf1, + 0x1c, 0xbf, 0xb2, 0x35, 0xb8, 0x18, 0xaf, 0x5e, 0x5f, 0x25, 0x78, 0xc3, 0x29, 0xbc, 0x30, 0x3c, + 0x54, 0xf1, 0x18, 0x1c, 0xba, 0x0b, 0x60, 0xe3, 0x23, 0x3f, 0xc2, 0xd5, 0x56, 0x2b, 0x98, 0x1f, + 0xa1, 0xbc, 0x9d, 0x3a, 0x3d, 0xa9, 0x8c, 0x05, 0xb4, 0xb4, 0xe1, 0xb4, 0x5a, 0x81, 0xad, 0x80, + 0xa0, 0x25, 0x18, 0xb1, 0x7d, 0xc6, 0xe0, 0xf9, 0xd1, 0x6b, 0x85, 0x5b, 0x63, 0x8b, 0x53, 0x7c, + 0x19, 0x8a, 0xe2, 0xda, 0xc5, 0xd3, 0x93, 0x0a, 0x0a, 0xf8, 0x2f, 0x75, 0x94, 0x02, 0x02, 0x55, + 0x60, 0x78, 0xc3, 0x5f, 0x72, 0x9a, 0x87, 0x78, 0x1e, 0xe8, 0xda, 0x1b, 0x3c, 0x3d, 0xa9, 0x14, + 0xde, 0xb5, 0x45, 0x29, 0x7a, 0x01, 0x63, 0xf1, 0x44, 0x85, 0xf3, 0x63, 0x94, 0x7d, 0xdb, 0xa7, + 0x27, 0x95, 0x8b, 0x21, 0x2d, 0x6e, 0x90, 0xa9, 0x57, 0x38, 0xf8, 0x06, 0xab, 0x40, 0x6d, 0xe8, + 0xf1, 0xc0, 0xc8, 0x78, 0x69, 0xc2, 0xbe, 0xb2, 0xe3, 0x85, 0x91, 0xb3, 0xdf, 0xc6, 0x71, 0x55, + 0x35, 0x0c, 0x71, 0x40, 0xe8, 0xad, 0x2d, 0x5b, 0xff, 0xb7, 0x00, 0x68, 0xb3, 0x83, 0xbd, 0x7a, + 0x7d, 0x95, 0xac, 0x78, 0xb1, 0xe0, 0xbf, 0x05, 0xa3, 0x8c, 0xb5, 0x84, 0xff, 0x45, 0xca, 0xff, + 0xc9, 0xd3, 0x93, 0x0a, 0x70, 0xfe, 0x13, 0xde, 0xc7, 0x00, 0xe8, 0x2d, 0xe8, 0xdf, 0xde, 0x5e, + 0xa7, 0xab, 0xb9, 0xbf, 0x36, 0x7d, 0x7a, 0x52, 0xe9, 0x8f, 0xa2, 0xf6, 0xcf, 0x4e, 0x2a, 0x23, + 0xcb, 0xc7, 0x01, 0xed, 0xb8, 0x4d, 0xea, 0xd1, 0x5b, 0x30, 0xbc, 0xd4, 0x3e, 0x0e, 0x23, 0x1c, + 0xcc, 0x0f, 0xc4, 0xdb, 0xa8, 0xc9, 0x8a, 0x6c, 0x51, 0x87, 0xbe, 0x09, 0x03, 0x3b, 0x21, 0x0e, + 0xe6, 0x07, 0xe9, 0x8c, 0x4c, 0xf0, 0x19, 0x21, 0x45, 0xbb, 0x8b, 0xb5, 0x11, 0xb2, 0x57, 0x8e, + 0x43, 0x1c, 0xd8, 0x14, 0x08, 0xdd, 0x81, 0x41, 0xc6, 0xd6, 0x21, 0x2a, 0x46, 0x26, 0xe4, 0xfc, + 0xb5, 0xf1, 0xee, 0x07, 0xb5, 0xd1, 0xd3, 0x93, 0xca, 0x20, 0x65, 0xaf, 0x3d, 0x28, 0x98, 0x52, + 0x28, 0x15, 0xed, 0x11, 0x82, 0x4b, 0x16, 0xae, 0xf5, 0x4d, 0x18, 0x53, 0x86, 0x8f, 0x16, 0x60, + 0x80, 0xfc, 0x4f, 0xb7, 0xf9, 0x38, 0x6b, 0x8c, 0xc8, 0x6f, 0x9b, 0x96, 0x5a, 0x7f, 0x38, 0x01, + 0x25, 0x82, 0xa9, 0xc9, 0x06, 0x8d, 0x55, 0x85, 0x6e, 0xac, 0xba, 0x05, 0xb2, 0x6d, 0x2e, 0x24, + 0xc6, 0x4f, 0x4f, 0x2a, 0x23, 0xc7, 0xbc, 0x2c, 0xee, 0x19, 0xaa, 0xc3, 0xf0, 0xca, 0xd7, 0x1d, + 0x37, 0xc0, 0x21, 0x65, 0xec, 0xd8, 0x62, 0xf9, 0x0e, 0xfb, 0x66, 0xdd, 0x11, 0xdf, 0xac, 0x3b, + 0xdb, 0xe2, 0x9b, 0x55, 0xbb, 0xc2, 0x85, 0xe5, 0x05, 0xcc, 0x50, 0xe2, 0xd5, 0xf4, 0x5b, 0x7f, + 0x51, 0x29, 0xd8, 0x82, 0x12, 0xfa, 0x16, 0x0c, 0x3d, 0xf4, 0x83, 0x23, 0x27, 0xe2, 0x33, 0x30, + 0x73, 0x7a, 0x52, 0x29, 0x3d, 0xa3, 0x25, 0xca, 0xe2, 0xe6, 0x30, 0xe8, 0x21, 0x4c, 0xda, 0xfe, + 0x71, 0x84, 0xb7, 0x7d, 0x31, 0x6f, 0x83, 0x14, 0xeb, 0xea, 0xe9, 0x49, 0xa5, 0x1c, 0x90, 0x9a, + 0x46, 0xe4, 0x37, 0xf8, 0x04, 0x2a, 0xf8, 0x09, 0x2c, 0xb4, 0x02, 0x93, 0x55, 0x2a, 0x5d, 0x39, + 0xcf, 0xd8, 0x6c, 0x8d, 0xd6, 0xae, 0x9c, 0x9e, 0x54, 0x2e, 0x39, 0xb4, 0xa6, 0x11, 0xf0, 0x2a, + 0x95, 0x8c, 0x8e, 0x84, 0x36, 0xe0, 0xc2, 0x93, 0xe3, 0x7d, 0x1c, 0x78, 0x38, 0xc2, 0xa1, 0xe8, + 0xd1, 0x30, 0xed, 0xd1, 0xb5, 0xd3, 0x93, 0xca, 0xc2, 0x73, 0x59, 0x69, 0xe8, 0x53, 0x1a, 0x15, + 0x61, 0x98, 0xe2, 0x1d, 0x5d, 0x76, 0x22, 0x67, 0xdf, 0x09, 0x31, 0x15, 0x1a, 0x63, 0x8b, 0x17, + 0x19, 0x8b, 0xef, 0x24, 0x6a, 0x6b, 0x37, 0x38, 0x97, 0x2f, 0xcb, 0xb1, 0xb7, 0x78, 0x95, 0xd2, + 0x50, 0x92, 0x26, 0x91, 0x9d, 0xf2, 0xbb, 0x30, 0x4a, 0x7b, 0x4b, 0x65, 0xa7, 0xfc, 0x2e, 0xa8, + 0x52, 0x45, 0x7e, 0x21, 0xd6, 0x61, 0x70, 0x87, 0x7c, 0x3d, 0xa9, 0x4c, 0x99, 0x5c, 0xbc, 0xce, + 0x7b, 0x94, 0x5c, 0x7d, 0x77, 0xc8, 0x0f, 0x0a, 0x48, 0xf7, 0xdd, 0x14, 0xfd, 0xe2, 0xaa, 0xdf, + 0x4a, 0x5a, 0x87, 0xbe, 0x00, 0xe0, 0xbd, 0xaa, 0x76, 0x3a, 0xf3, 0x63, 0x74, 0x90, 0x17, 0xf4, + 0x41, 0x56, 0x3b, 0x9d, 0xda, 0x55, 0x3e, 0xbe, 0x8b, 0x72, 0x7c, 0x4e, 0xa7, 0xa3, 0x50, 0x53, + 0x88, 0xa0, 0xcf, 0x61, 0x9c, 0x8a, 0x1c, 0x31, 0xa3, 0xe3, 0x74, 0x46, 0x2f, 0x9f, 0x9e, 0x54, + 0xe6, 0xc8, 0x86, 0x33, 0xcd, 0xa7, 0x86, 0x80, 0xfe, 0x3a, 0xcc, 0x72, 0x72, 0x7b, 0xae, 0xd7, + 0xf2, 0x5f, 0x86, 0xcb, 0x38, 0x7c, 0x1e, 0xf9, 0x9d, 0xf9, 0x09, 0xda, 0xbd, 0x05, 0xbd, 0x7b, + 0x3a, 0x4c, 0xed, 0x36, 0xef, 0xa9, 0x25, 0x7b, 0xfa, 0x92, 0x01, 0x34, 0x5a, 0x0c, 0x42, 0x69, + 0xd6, 0xdc, 0x0c, 0x5a, 0x83, 0xa9, 0x9d, 0x10, 0x6b, 0x63, 0x98, 0xa4, 0xf2, 0xbb, 0x42, 0x66, + 0xf8, 0x38, 0xc4, 0x8d, 0xac, 0x71, 0x24, 0xf1, 0x90, 0x0d, 0x68, 0x39, 0xf0, 0x3b, 0x89, 0x35, + 0x3e, 0x45, 0x39, 0x62, 0x9d, 0x9e, 0x54, 0xae, 0xb6, 0x02, 0xbf, 0xd3, 0xc8, 0x5e, 0xe8, 0x06, + 0x6c, 0xf4, 0x43, 0xb8, 0xb8, 0xe4, 0x7b, 0x1e, 0x6e, 0x12, 0xf9, 0xb9, 0xec, 0x3a, 0x07, 0x9e, + 0x1f, 0x46, 0x6e, 0x73, 0x6d, 0x79, 0xbe, 0x44, 0xd7, 0xd0, 0x4d, 0x32, 0xfa, 0xa6, 0x84, 0x68, + 0xb4, 0x24, 0x48, 0xc3, 0x6d, 0x29, 0xb4, 0x33, 0xa8, 0xa0, 0x1f, 0xc0, 0x04, 0x6f, 0x0b, 0x07, + 0x74, 0x69, 0x5e, 0xc8, 0x5f, 0x68, 0x12, 0x98, 0x7d, 0x88, 0x03, 0xf1, 0x93, 0xa9, 0x36, 0x3a, + 0x2d, 0xf4, 0x15, 0x8c, 0x3d, 0x7d, 0x58, 0xb5, 0x71, 0xd8, 0xf1, 0xbd, 0x10, 0xcf, 0x23, 0x3a, + 0xa3, 0x57, 0x39, 0xe9, 0xa7, 0x0f, 0xab, 0xd5, 0xe3, 0xe8, 0x10, 0x7b, 0x91, 0xdb, 0x74, 0x22, + 0x2c, 0xa0, 0x6a, 0x65, 0xb2, 0xf2, 0x8e, 0x9e, 0x39, 0x8d, 0x80, 0x97, 0x28, 0xa3, 0x50, 0xc9, + 0xa1, 0x32, 0x8c, 0xd4, 0xeb, 0xab, 0xeb, 0xfe, 0x81, 0xeb, 0xcd, 0x4f, 0x13, 0x66, 0xd8, 0xf2, + 0x37, 0xda, 0x87, 0x59, 0x45, 0x41, 0x6f, 0x90, 0xff, 0xf1, 0x11, 0xf6, 0xa2, 0xf9, 0x19, 0xda, + 0x87, 0x77, 0xa5, 0x85, 0x71, 0x47, 0xd5, 0xe3, 0x5f, 0xdc, 0xbf, 0x53, 0x8d, 0x7f, 0xd6, 0x05, + 0x92, 0x3d, 0xe3, 0x18, 0x4a, 0xad, 0x2f, 0x61, 0x54, 0x6e, 0x3b, 0x34, 0x0c, 0xfd, 0xd5, 0x76, + 0xbb, 0xd4, 0x47, 0xfe, 0xa8, 0xd7, 0x57, 0x4b, 0x05, 0x34, 0x09, 0x10, 0xcb, 0x9a, 0x52, 0x11, + 0x8d, 0xc3, 0x88, 0x90, 0x05, 0xa5, 0x7e, 0x0a, 0xdf, 0xe9, 0x94, 0x06, 0x10, 0x82, 0x49, 0x7d, + 0x45, 0x96, 0x06, 0xad, 0x1d, 0x18, 0x95, 0x8c, 0x44, 0x53, 0x30, 0xb6, 0xb3, 0x51, 0xdf, 0x5a, + 0x59, 0x5a, 0x7b, 0xb8, 0xb6, 0xb2, 0x5c, 0xea, 0x43, 0x57, 0xe0, 0xd2, 0x76, 0x7d, 0xb5, 0xb1, + 0x5c, 0x6b, 0xac, 0x6f, 0x2e, 0x55, 0xd7, 0x1b, 0x5b, 0xf6, 0xe6, 0x97, 0xdf, 0x6b, 0x6c, 0xef, + 0x6c, 0x6c, 0xac, 0xac, 0x97, 0x0a, 0x68, 0x1e, 0x66, 0x48, 0xf5, 0x93, 0x9d, 0xda, 0x8a, 0x0a, + 0x50, 0x2a, 0x5a, 0xff, 0xa9, 0x90, 0x92, 0x74, 0x68, 0x11, 0xc6, 0xea, 0xcc, 0x96, 0xa2, 0xb3, + 0xcf, 0xf4, 0xda, 0xd2, 0xe9, 0x49, 0x65, 0x9c, 0x9b, 0x58, 0x6c, 0x62, 0x55, 0x20, 0xf2, 0xf1, + 0xda, 0x22, 0x53, 0xd8, 0xf4, 0xdb, 0xea, 0xc7, 0xab, 0xc3, 0xcb, 0x6c, 0x59, 0x8b, 0x16, 0x95, + 0xcf, 0x1c, 0x53, 0x72, 0xa9, 0x22, 0x25, 0x3e, 0x73, 0xaa, 0xc8, 0x93, 0x1f, 0xbc, 0xc5, 0x98, + 0x4f, 0xfc, 0xeb, 0x44, 0x71, 0x0c, 0x22, 0x56, 0xc2, 0x59, 0xc7, 0x19, 0x42, 0x04, 0x7d, 0x92, + 0xe4, 0x2e, 0x1f, 0x21, 0x95, 0x92, 0x09, 0x59, 0x61, 0x27, 0x40, 0x51, 0x05, 0x06, 0xd9, 0xea, + 0x62, 0x83, 0xa4, 0x5a, 0x44, 0x9b, 0x14, 0xd8, 0xac, 0xdc, 0xfa, 0xfb, 0xfd, 0xaa, 0x40, 0x25, + 0x5a, 0x83, 0xc2, 0x44, 0xaa, 0x35, 0x50, 0xe6, 0xd1, 0x52, 0xa2, 0x20, 0xd4, 0x71, 0x18, 0x52, + 0x7d, 0x8b, 0x53, 0xa4, 0x0a, 0x42, 0xc8, 0x0a, 0x89, 0x09, 0x11, 0x03, 0x10, 0x25, 0x96, 0x69, + 0x0b, 0x54, 0x89, 0xed, 0x8f, 0x95, 0x58, 0xae, 0x4f, 0x30, 0x25, 0x36, 0x06, 0x21, 0x13, 0xc9, + 0x3f, 0x68, 0xb4, 0x0f, 0x03, 0xf1, 0x44, 0xf2, 0x8f, 0x20, 0x9f, 0x48, 0x05, 0x08, 0x7d, 0x0c, + 0x50, 0xdd, 0xab, 0x53, 0x5d, 0xd0, 0xde, 0xe0, 0x1f, 0x75, 0xba, 0xfd, 0x9c, 0x97, 0x21, 0x13, + 0x7b, 0x4e, 0xa0, 0x6a, 0xbb, 0x0a, 0x34, 0xaa, 0xc1, 0x44, 0xf5, 0xc7, 0xc7, 0x01, 0x5e, 0x6b, + 0x91, 0x1d, 0x1c, 0x31, 0xb5, 0x7e, 0xb4, 0xb6, 0x70, 0x7a, 0x52, 0x99, 0x77, 0x48, 0x45, 0xc3, + 0xe5, 0x35, 0x0a, 0x01, 0x1d, 0x05, 0x6d, 0xc2, 0x85, 0x47, 0x4b, 0x5b, 0x7c, 0x69, 0x55, 0x9b, + 0x4d, 0xff, 0xd8, 0x8b, 0xf8, 0x97, 0xfc, 0xfa, 0xe9, 0x49, 0xe5, 0xca, 0x41, 0xb3, 0xd3, 0x10, + 0xcb, 0xd0, 0x61, 0xd5, 0xea, 0xa7, 0x3c, 0x85, 0x6b, 0xb5, 0x61, 0xf2, 0x11, 0x8e, 0xc8, 0x52, + 0x12, 0x6a, 0x59, 0xfe, 0x9c, 0x7c, 0x0a, 0x63, 0x7b, 0x6e, 0x74, 0x58, 0xc7, 0xcd, 0x00, 0x47, + 0xc2, 0x68, 0xa4, 0x1c, 0x78, 0xe9, 0x46, 0x87, 0x8d, 0x90, 0x95, 0xab, 0x02, 0x48, 0x01, 0xb7, + 0x56, 0x60, 0x8a, 0xb7, 0x26, 0xb5, 0xc0, 0x45, 0x9d, 0x60, 0x81, 0x12, 0xa4, 0xb3, 0xa0, 0x12, + 0xd4, 0xc9, 0xfc, 0x61, 0x11, 0x66, 0x97, 0x0e, 0x1d, 0xef, 0x00, 0x6f, 0x39, 0x61, 0xf8, 0xd2, + 0x0f, 0x5a, 0x4a, 0xe7, 0xa9, 0x0a, 0x9c, 0xea, 0x3c, 0xd5, 0x79, 0x17, 0x61, 0x6c, 0xb3, 0xdd, + 0x12, 0x38, 0x5c, 0x3d, 0xa7, 0x6d, 0xf9, 0xed, 0x56, 0xa3, 0x23, 0x68, 0xa9, 0x40, 0x04, 0x67, + 0x03, 0xbf, 0x94, 0x38, 0xfd, 0x31, 0x8e, 0x87, 0x5f, 0x2a, 0x38, 0x0a, 0x10, 0x5a, 0x81, 0x0b, + 0x75, 0xdc, 0xf4, 0xbd, 0xd6, 0x43, 0xa7, 0x19, 0xf9, 0xc1, 0xb6, 0xff, 0x1c, 0x7b, 0x7c, 0x7d, + 0x51, 0x0d, 0x26, 0xa4, 0x95, 0x8d, 0x67, 0xb4, 0xb6, 0x11, 0x91, 0x6a, 0x3b, 0x8d, 0x81, 0x36, + 0x61, 0x64, 0x8f, 0xfb, 0x17, 0xb8, 0x4e, 0xff, 0xd6, 0x1d, 0xe9, 0x70, 0x58, 0x0a, 0x30, 0x5d, + 0x14, 0x4e, 0x5b, 0x5a, 0x25, 0xf2, 0x83, 0x40, 0x85, 0x8b, 0x80, 0xb4, 0x25, 0x11, 0x6b, 0x07, + 0x26, 0xb6, 0xda, 0xc7, 0x07, 0xae, 0x47, 0xc4, 0x40, 0x1d, 0xff, 0x32, 0x5a, 0x06, 0x88, 0x0b, + 0xb8, 0x43, 0x61, 0x9a, 0x5b, 0x02, 0x71, 0xc5, 0xee, 0x03, 0xbe, 0x91, 0x68, 0x09, 0x55, 0xdd, + 0x6c, 0x05, 0xcf, 0xfa, 0xbb, 0xfd, 0x80, 0xf8, 0x04, 0x50, 0x59, 0x5f, 0xc7, 0x11, 0x11, 0xc3, + 0x17, 0xa1, 0x28, 0xed, 0xfe, 0xa1, 0xd3, 0x93, 0x4a, 0xd1, 0x6d, 0xd9, 0xc5, 0xb5, 0x65, 0xf4, + 0x1e, 0x0c, 0x52, 0x30, 0xca, 0xff, 0x49, 0xd9, 0x9e, 0x4a, 0x81, 0x49, 0x0e, 0xfa, 0x0d, 0xb2, + 0x19, 0x30, 0x7a, 0x1f, 0x46, 0x97, 0x71, 0x1b, 0x1f, 0x38, 0x91, 0x2f, 0x76, 0x37, 0xb3, 0xa4, + 0x45, 0xa1, 0xb2, 0xe6, 0x62, 0x48, 0xa2, 0xb7, 0xdb, 0xd8, 0x09, 0x7d, 0x4f, 0xd5, 0xdb, 0x03, + 0x5a, 0xa2, 0xea, 0xed, 0x0c, 0x06, 0xfd, 0x4e, 0x01, 0xc6, 0xaa, 0x9e, 0xc7, 0x2d, 0xd4, 0x90, + 0x73, 0x7d, 0xf6, 0x8e, 0xf4, 0xdb, 0xac, 0x3b, 0xfb, 0xb8, 0xbd, 0xeb, 0xb4, 0x8f, 0x71, 0x58, + 0xfb, 0x8a, 0xa8, 0x52, 0xff, 0xf9, 0xa4, 0xf2, 0xc9, 0x19, 0x6c, 0xce, 0xd8, 0x03, 0xb4, 0x1d, + 0x38, 0x6e, 0x14, 0x9e, 0x9e, 0x54, 0x66, 0x9d, 0xb8, 0x41, 0x75, 0xdf, 0x28, 0xfd, 0x40, 0xef, + 0xa8, 0xc6, 0x1a, 0x97, 0xc5, 0x09, 0xe3, 0x97, 0xdb, 0x69, 0xd6, 0x0d, 0xf9, 0x25, 0x5c, 0x5b, + 0xce, 0x9a, 0x02, 0x6b, 0x09, 0x16, 0x1e, 0xe1, 0xc8, 0xc6, 0x21, 0x8e, 0xc4, 0xa2, 0xa5, 0x4b, + 0x2e, 0x76, 0xdb, 0x0c, 0xd3, 0xdf, 0x12, 0x99, 0xce, 0x07, 0x5b, 0xa8, 0xa2, 0xc6, 0xfa, 0xdb, + 0x05, 0xa8, 0x2c, 0x05, 0x98, 0x69, 0x22, 0x19, 0x84, 0xf2, 0x85, 0xc9, 0x02, 0x0c, 0x6c, 0xbf, + 0xea, 0x08, 0x7b, 0x8e, 0xd6, 0x12, 0x2e, 0xd9, 0xb4, 0xb4, 0x47, 0xe3, 0xd8, 0x7a, 0x06, 0xb3, + 0x36, 0xf6, 0xf0, 0x4b, 0x62, 0xaa, 0x6b, 0xf6, 0x65, 0x05, 0x06, 0xd9, 0xce, 0x4b, 0x0d, 0x81, + 0x95, 0x9f, 0xcd, 0x56, 0xb7, 0xfe, 0x59, 0x11, 0x4a, 0x6c, 0xb8, 0x35, 0x3f, 0xea, 0x6d, 0x7c, + 0x7c, 0x04, 0xc5, 0x2e, 0xe6, 0xfd, 0xcd, 0x98, 0xdb, 0xfd, 0xb1, 0x72, 0x40, 0xbb, 0x4a, 0xbe, + 0x71, 0xa2, 0x92, 0x0c, 0x88, 0xad, 0x02, 0xe6, 0x95, 0x4a, 0xd9, 0xe8, 0xe8, 0x37, 0x0a, 0x30, + 0xc4, 0xd6, 0x55, 0xfe, 0xca, 0xdd, 0x3b, 0x9f, 0x95, 0x5b, 0x8a, 0xe8, 0x5f, 0xea, 0x3e, 0x62, + 0x75, 0xd6, 0xbf, 0x28, 0xc2, 0x05, 0x85, 0x57, 0x5c, 0xfd, 0x7c, 0x87, 0xe9, 0x36, 0x0a, 0xc3, + 0xa8, 0x9f, 0x8f, 0xe8, 0x36, 0x8d, 0xd8, 0x86, 0xa7, 0x9c, 0x7b, 0x07, 0x46, 0xc8, 0x90, 0x92, + 0x2e, 0x41, 0xfa, 0x85, 0x65, 0xa0, 0xa2, 0xba, 0x67, 0xee, 0xdd, 0x85, 0x11, 0xfa, 0x27, 0x99, + 0x91, 0x81, 0xec, 0x19, 0x91, 0x40, 0xc8, 0x05, 0x78, 0xec, 0xbb, 0xde, 0x53, 0x1c, 0x1d, 0xfa, + 0x2d, 0xfe, 0xad, 0x5f, 0x23, 0x72, 0xf0, 0xaf, 0xf9, 0xae, 0xd7, 0x38, 0xa2, 0xc5, 0x67, 0x75, + 0x39, 0xc5, 0x04, 0x6d, 0x85, 0xb8, 0x75, 0x0f, 0x4a, 0x44, 0x64, 0xf5, 0xbe, 0xb4, 0xac, 0x19, + 0x40, 0x8f, 0x70, 0x54, 0xf3, 0xb5, 0x8f, 0xa9, 0x35, 0x01, 0x63, 0x5b, 0xae, 0x77, 0x20, 0x7e, + 0xfe, 0xcb, 0x7e, 0x18, 0x67, 0xbf, 0xf9, 0x0c, 0x24, 0x54, 0x9e, 0x42, 0x2f, 0x2a, 0xcf, 0x87, + 0x30, 0x41, 0x74, 0x06, 0x1c, 0xec, 0xe2, 0x80, 0xa8, 0x5a, 0x7c, 0x3e, 0xa8, 0x31, 0x13, 0xd2, + 0x8a, 0xc6, 0x0b, 0x56, 0x63, 0xeb, 0x80, 0x68, 0x1d, 0x26, 0x59, 0xc1, 0x43, 0xec, 0x44, 0xc7, + 0xb1, 0x3f, 0x66, 0x8a, 0xdb, 0x33, 0xa2, 0x98, 0xc9, 0x33, 0x4e, 0xeb, 0x19, 0x2f, 0xb4, 0x13, + 0xb8, 0xe8, 0x73, 0x98, 0xda, 0x0a, 0xfc, 0xaf, 0x5f, 0x29, 0x4a, 0x1e, 0x13, 0xe9, 0xb3, 0xa7, + 0x27, 0x95, 0x0b, 0x1d, 0x52, 0xd5, 0x50, 0x55, 0xbd, 0x24, 0x34, 0x59, 0x53, 0x6b, 0x61, 0xcd, + 0x0f, 0x5c, 0xef, 0x80, 0xce, 0xe6, 0x08, 0x5b, 0x53, 0x6e, 0xd8, 0xd8, 0xa7, 0x85, 0xb6, 0xac, + 0x4e, 0x38, 0x44, 0x87, 0xbb, 0x3b, 0x44, 0xef, 0x01, 0xac, 0xfb, 0x4e, 0xab, 0xda, 0x6e, 0x2f, + 0x55, 0x43, 0xea, 0x0c, 0xe1, 0x4a, 0x4c, 0xdb, 0x77, 0x5a, 0x0d, 0xa7, 0xdd, 0x6e, 0x34, 0x9d, + 0xd0, 0x56, 0x60, 0x1e, 0x0f, 0x8c, 0x0c, 0x95, 0x86, 0xed, 0xa9, 0x75, 0xb7, 0x89, 0xbd, 0x10, + 0xef, 0x39, 0x81, 0xe7, 0x7a, 0x07, 0xa1, 0xf5, 0x5b, 0x43, 0x30, 0x22, 0x87, 0x7c, 0x47, 0x35, + 0x88, 0xb8, 0x6a, 0x44, 0x25, 0x54, 0xec, 0xb0, 0xb1, 0x15, 0x08, 0x74, 0x89, 0x9a, 0x48, 0x5c, + 0x29, 0x1b, 0x26, 0xab, 0xdb, 0xe9, 0x74, 0x6c, 0x52, 0x46, 0xbe, 0x04, 0xcb, 0x35, 0xca, 0xff, + 0x11, 0xf6, 0x25, 0x68, 0xed, 0xdb, 0xc5, 0xe5, 0x1a, 0x59, 0x65, 0x9b, 0x6b, 0xcb, 0x4b, 0x94, + 0x95, 0x23, 0x6c, 0x95, 0xf9, 0x6e, 0xab, 0x69, 0xd3, 0x52, 0x52, 0x5b, 0xaf, 0x3e, 0x5d, 0xe7, + 0xec, 0xa2, 0xb5, 0xa1, 0x73, 0xd4, 0xb6, 0x69, 0x29, 0x31, 0x15, 0x98, 0xed, 0xbd, 0xe4, 0x7b, + 0x51, 0xe0, 0xb7, 0x43, 0xaa, 0xd1, 0x8e, 0xb0, 0xe9, 0xe4, 0x46, 0x7b, 0x93, 0x57, 0xd9, 0x09, + 0x50, 0xb4, 0x07, 0x73, 0xd5, 0xd6, 0x0b, 0xc7, 0x6b, 0xe2, 0x16, 0xab, 0xd9, 0xf3, 0x83, 0xe7, + 0xcf, 0xda, 0xfe, 0xcb, 0x90, 0xf2, 0x7b, 0x84, 0xfb, 0xb8, 0x38, 0x88, 0xf0, 0x01, 0xbc, 0x14, + 0x40, 0x76, 0x16, 0x36, 0x91, 0x92, 0x4b, 0x6d, 0xff, 0xb8, 0xc5, 0x67, 0x81, 0x4a, 0xc9, 0x26, + 0x29, 0xb0, 0x59, 0x39, 0xe1, 0xd2, 0x6a, 0xfd, 0x29, 0xf5, 0x28, 0x71, 0x2e, 0x1d, 0x86, 0x47, + 0x36, 0x29, 0x43, 0x6f, 0xc1, 0xb0, 0xb0, 0x7a, 0x98, 0x4b, 0x9a, 0x3a, 0x5a, 0x85, 0xb5, 0x23, + 0xea, 0xc8, 0x96, 0xb0, 0x71, 0xd3, 0x7f, 0x81, 0x83, 0x57, 0x4b, 0x7e, 0x0b, 0x0b, 0xff, 0x07, + 0xb7, 0xef, 0x59, 0x45, 0xa3, 0x49, 0x6a, 0x6c, 0x1d, 0x90, 0x34, 0xc0, 0x14, 0xa7, 0x70, 0x7e, + 0x2a, 0x6e, 0x80, 0x29, 0x56, 0xa1, 0x2d, 0xea, 0xd0, 0x32, 0x5c, 0xa8, 0x1e, 0x47, 0xfe, 0x91, + 0x13, 0xb9, 0xcd, 0x9d, 0xce, 0x41, 0xe0, 0x90, 0x46, 0x4a, 0x14, 0x81, 0x9a, 0x76, 0x8e, 0xa8, + 0x6c, 0x1c, 0xf3, 0x5a, 0x3b, 0x8d, 0x80, 0x3e, 0x80, 0xf1, 0xb5, 0x90, 0xf9, 0xb8, 0x9c, 0x10, + 0xb7, 0xa8, 0xa3, 0x82, 0xf7, 0xd2, 0x0d, 0x1b, 0xd4, 0xe3, 0xd5, 0x20, 0xc6, 0x60, 0xcb, 0xd6, + 0xe0, 0x90, 0x05, 0x43, 0xd5, 0x30, 0x74, 0xc3, 0x88, 0xfa, 0x1f, 0x46, 0x6a, 0x70, 0x7a, 0x52, + 0x19, 0x72, 0x68, 0x89, 0xcd, 0x6b, 0xd0, 0x1e, 0x8c, 0x2d, 0x63, 0x62, 0x48, 0x6c, 0x07, 0xc7, + 0x61, 0x44, 0xbd, 0x09, 0x63, 0x8b, 0x97, 0xf8, 0xc6, 0x56, 0x6a, 0xf8, 0x5a, 0x66, 0x26, 0x42, + 0x8b, 0x96, 0x37, 0x22, 0x52, 0xa1, 0xaa, 0x3a, 0x0a, 0xfc, 0xe3, 0x81, 0x91, 0xb1, 0xd2, 0x38, + 0x73, 0xc1, 0x3f, 0x1e, 0x18, 0x99, 0x28, 0x4d, 0x5a, 0xbf, 0x5b, 0x00, 0x94, 0xa6, 0x88, 0xee, + 0xc2, 0x30, 0xf6, 0xc8, 0x47, 0xbf, 0xc5, 0x77, 0x06, 0x95, 0x03, 0xbc, 0x48, 0xa1, 0x2e, 0xa0, + 0xd0, 0x17, 0x30, 0xcd, 0x3a, 0x20, 0xc6, 0xde, 0x76, 0x8f, 0xdc, 0x88, 0xee, 0x96, 0x41, 0x66, + 0x3d, 0x19, 0xaa, 0x55, 0xeb, 0x89, 0x57, 0x53, 0x4e, 0xad, 0x93, 0x4a, 0xeb, 0x3e, 0x5c, 0x60, + 0x72, 0xbb, 0x67, 0x03, 0xca, 0xda, 0x02, 0xa8, 0xe3, 0x23, 0xa7, 0x73, 0xe8, 0x93, 0x1d, 0x5e, + 0x53, 0x7f, 0x71, 0x05, 0x1c, 0x71, 0x85, 0x58, 0x56, 0xec, 0x3e, 0x10, 0x76, 0xaf, 0x80, 0xb4, + 0x15, 0x2c, 0xeb, 0x4f, 0x8a, 0x80, 0xaa, 0xc7, 0x2d, 0x37, 0xaa, 0x47, 0x01, 0x76, 0x8e, 0x44, + 0x37, 0x3e, 0x82, 0x71, 0xf6, 0x09, 0x66, 0xc5, 0xb4, 0x3b, 0x44, 0xbb, 0x67, 0x53, 0xa4, 0x56, + 0xad, 0xf6, 0xd9, 0x1a, 0x28, 0x41, 0xb5, 0x71, 0x78, 0x7c, 0x24, 0x50, 0x8b, 0x1a, 0xaa, 0x5a, + 0x45, 0x50, 0xd5, 0xdf, 0xe8, 0x73, 0x98, 0x5c, 0xf2, 0x8f, 0x3a, 0x84, 0x27, 0x1c, 0xb9, 0x9f, + 0x6b, 0x22, 0xbc, 0x5d, 0xad, 0x72, 0xb5, 0xcf, 0x4e, 0x80, 0xa3, 0x0d, 0x98, 0x7e, 0xd8, 0x3e, + 0x0e, 0x0f, 0xab, 0x5e, 0x6b, 0xa9, 0xed, 0x87, 0x82, 0xca, 0x00, 0xf7, 0xe4, 0xf3, 0x2f, 0x47, + 0x1a, 0x62, 0xb5, 0xcf, 0x36, 0x21, 0xa2, 0xb7, 0x60, 0x70, 0xe5, 0x05, 0xf6, 0x22, 0x79, 0x2a, + 0xc2, 0x8f, 0x55, 0x37, 0x3d, 0xbc, 0xf9, 0x6c, 0xb5, 0xcf, 0x66, 0xb5, 0xb5, 0x51, 0x18, 0x16, + 0x5f, 0xcd, 0xbb, 0x64, 0xf3, 0x49, 0x76, 0x12, 0xeb, 0xe3, 0x38, 0x44, 0x65, 0x18, 0xd9, 0xe9, + 0x10, 0x61, 0x2e, 0x54, 0x62, 0x5b, 0xfe, 0xb6, 0xbe, 0xa5, 0x73, 0x1a, 0x2d, 0xa8, 0x7e, 0x0b, + 0x06, 0x1c, 0x17, 0x58, 0xab, 0x3a, 0x73, 0xf3, 0xa1, 0xb5, 0x76, 0x8b, 0x89, 0x76, 0x4b, 0x49, + 0x5e, 0x5b, 0xb3, 0x46, 0xe6, 0x59, 0x5f, 0xc2, 0xd5, 0x9d, 0x0e, 0x31, 0x12, 0xab, 0x9d, 0x4e, + 0xdb, 0x6d, 0x32, 0xaf, 0x1c, 0xfd, 0xba, 0x8a, 0xc5, 0xf2, 0x01, 0x0c, 0xb1, 0x02, 0xbe, 0x4c, + 0xc4, 0x1a, 0xac, 0x76, 0x3a, 0xfc, 0x9b, 0xfe, 0x80, 0x89, 0x01, 0xf6, 0x95, 0xb6, 0x39, 0xb4, + 0xf5, 0x5b, 0x05, 0xb8, 0xca, 0x76, 0x40, 0x26, 0xe9, 0x6f, 0xc2, 0x28, 0x3d, 0xd5, 0xec, 0x38, + 0x4d, 0x4d, 0xed, 0xf3, 0x44, 0xa1, 0x1d, 0xd7, 0x2b, 0xe7, 0xc5, 0xc5, 0xec, 0xf3, 0x62, 0xb1, + 0xc1, 0xfa, 0x8d, 0x1b, 0xec, 0x0b, 0xb0, 0x78, 0x8f, 0xda, 0xed, 0x54, 0xa7, 0xc2, 0xd7, 0xe9, + 0x95, 0xf5, 0xdf, 0x8a, 0x30, 0xf7, 0x08, 0x7b, 0x38, 0x70, 0xe8, 0x38, 0x35, 0x0b, 0x47, 0x3d, + 0x97, 0x2a, 0xe4, 0x9e, 0x4b, 0x49, 0xf5, 0xbd, 0x98, 0xa1, 0xbe, 0x5f, 0x82, 0xfe, 0x1d, 0x7b, + 0x8d, 0x0f, 0x8b, 0x7e, 0x98, 0x8e, 0x03, 0xd7, 0x26, 0x65, 0x68, 0x2d, 0x3e, 0xd3, 0x1a, 0xe8, + 0x7a, 0xa6, 0x35, 0xcd, 0x7d, 0xfc, 0xc3, 0xfc, 0x4c, 0x4b, 0x3f, 0xc9, 0xda, 0x50, 0x6c, 0x04, + 0x22, 0x6e, 0x6e, 0xf3, 0x3d, 0x95, 0x31, 0x40, 0xae, 0xee, 0xaf, 0x78, 0x51, 0xf0, 0x8a, 0x2d, + 0x01, 0xa6, 0xf5, 0x0b, 0x5d, 0xbf, 0xfc, 0x05, 0x8c, 0x29, 0x20, 0xa8, 0x04, 0xfd, 0xcf, 0xf9, + 0x79, 0xde, 0xa8, 0x4d, 0xfe, 0x44, 0xdf, 0x82, 0xc1, 0x17, 0xc4, 0xee, 0xe0, 0x62, 0xe4, 0x62, + 0x6c, 0x93, 0xd4, 0x23, 0xa2, 0x6d, 0x31, 0xa3, 0xc4, 0x66, 0x40, 0x1f, 0x17, 0x3f, 0x2c, 0x58, + 0x9f, 0xc0, 0x7c, 0xba, 0x37, 0x5c, 0x85, 0xed, 0x66, 0xd5, 0x59, 0xcb, 0x30, 0xf3, 0x08, 0x47, + 0x74, 0xe1, 0xd2, 0x4d, 0xa4, 0x1c, 0x37, 0x26, 0xf6, 0x59, 0x8e, 0x37, 0xd1, 0xaa, 0xc3, 0x6c, + 0x82, 0x0a, 0x6f, 0xff, 0x63, 0x18, 0xe6, 0x45, 0x52, 0xa2, 0xf2, 0x00, 0x0c, 0xbc, 0xcf, 0x2b, + 0x76, 0x17, 0xd9, 0xba, 0xe5, 0x94, 0x6d, 0x81, 0x60, 0x1d, 0xc2, 0xc5, 0x75, 0x37, 0x54, 0xa8, + 0xca, 0xe5, 0x78, 0x19, 0x46, 0x3b, 0xe4, 0x9b, 0x13, 0xba, 0x3f, 0x66, 0xcb, 0x68, 0xd0, 0x1e, + 0x21, 0x05, 0x75, 0xf7, 0xc7, 0x18, 0x5d, 0x01, 0xa0, 0x95, 0x74, 0x98, 0x5c, 0x0a, 0x50, 0x70, + 0x66, 0xc6, 0x22, 0xa0, 0xe7, 0xba, 0x6c, 0xdd, 0xd8, 0xf4, 0x6f, 0x2b, 0x80, 0xb9, 0x54, 0x4b, + 0x7c, 0x00, 0x77, 0x61, 0x84, 0x77, 0x2c, 0x4c, 0x78, 0x7c, 0xd4, 0x11, 0xd8, 0x12, 0x08, 0xdd, + 0x84, 0x29, 0x0f, 0x7f, 0x1d, 0x35, 0x52, 0x7d, 0x98, 0x20, 0xc5, 0x5b, 0xa2, 0x1f, 0xd6, 0x2f, + 0x51, 0xa7, 0x42, 0xdd, 0xf3, 0x5f, 0x3e, 0x6b, 0x3b, 0xcf, 0x71, 0xaa, 0xe1, 0xef, 0xc0, 0x48, + 0xbd, 0x7b, 0xc3, 0x6c, 0xfb, 0x88, 0xc6, 0x6d, 0x89, 0x62, 0xb5, 0xa1, 0x4c, 0x86, 0x44, 0x34, + 0xcf, 0xb5, 0xd6, 0xd6, 0xcf, 0x9b, 0x81, 0x2f, 0xe0, 0xb2, 0xb1, 0xb5, 0x9f, 0x37, 0x13, 0xff, + 0xb2, 0x08, 0x73, 0xec, 0x63, 0x92, 0x5e, 0xc1, 0xbd, 0x8b, 0x9a, 0x5f, 0x88, 0x2f, 0xfc, 0x9e, + 0xc1, 0x17, 0x4e, 0x51, 0x54, 0x5f, 0xb8, 0xe6, 0x01, 0xff, 0xd0, 0xec, 0x01, 0xa7, 0x1a, 0xa9, + 0xee, 0x01, 0x4f, 0xfa, 0xbd, 0x57, 0xb2, 0xfd, 0xde, 0xd4, 0x0b, 0x68, 0xf0, 0x7b, 0x1b, 0xbc, + 0xdd, 0x8f, 0x07, 0x46, 0x8a, 0xa5, 0x7e, 0x6b, 0x17, 0xe6, 0xd3, 0x2c, 0x3e, 0x87, 0xed, 0xfd, + 0x47, 0x05, 0xb8, 0xc2, 0x15, 0x81, 0xc4, 0x26, 0x38, 0xfb, 0x0c, 0xbe, 0x0f, 0xe3, 0x1c, 0x77, + 0x3b, 0x5e, 0x2c, 0xb5, 0x0b, 0xa7, 0x27, 0x95, 0x09, 0x21, 0xb0, 0x98, 0xd4, 0xd3, 0xc0, 0xd0, + 0xfb, 0x8a, 0x93, 0x83, 0x39, 0xce, 0x2e, 0x91, 0xaf, 0x1a, 0xf3, 0x86, 0x64, 0xba, 0x3a, 0xac, + 0xaf, 0xe0, 0x6a, 0x56, 0xc7, 0xcf, 0x81, 0x2f, 0xff, 0xba, 0x00, 0x97, 0x39, 0x79, 0x6d, 0x3b, + 0xbd, 0x96, 0x64, 0x3e, 0x43, 0x20, 0xc8, 0x63, 0x18, 0x23, 0x0d, 0x8a, 0x7e, 0xf7, 0xf3, 0xcf, + 0x0f, 0xd7, 0xae, 0xe3, 0x9a, 0x65, 0x27, 0x72, 0xf8, 0x09, 0x9e, 0x73, 0xd4, 0x6e, 0x88, 0xfe, + 0xab, 0xc8, 0xd6, 0xf7, 0x61, 0xc1, 0x3c, 0x84, 0x73, 0xe0, 0xcf, 0x63, 0x28, 0x1b, 0x04, 0xe7, + 0xeb, 0x7d, 0xb7, 0xbe, 0x07, 0x97, 0x8d, 0xb4, 0xce, 0xa1, 0x9b, 0xab, 0xe4, 0xab, 0x1c, 0x9d, + 0xc3, 0x14, 0x5a, 0x7b, 0x70, 0xc9, 0x40, 0xe9, 0x1c, 0xba, 0xf8, 0x08, 0xe6, 0xa4, 0x36, 0xfa, + 0x46, 0x3d, 0x7c, 0x0a, 0x57, 0x18, 0xa1, 0xf3, 0x99, 0x95, 0x27, 0x70, 0x99, 0x93, 0x3b, 0x07, + 0xee, 0xad, 0xc2, 0x42, 0x6c, 0x74, 0x1a, 0x74, 0x89, 0x9e, 0x85, 0x8c, 0xb5, 0x0e, 0xd7, 0x62, + 0x4a, 0x19, 0x1f, 0xd6, 0xde, 0xa9, 0x31, 0x95, 0x29, 0x9e, 0xa5, 0x73, 0x99, 0xd1, 0x3d, 0xb8, + 0xa8, 0x11, 0x3d, 0x37, 0x75, 0x62, 0x0d, 0xa6, 0x19, 0x61, 0x5d, 0xbd, 0x5c, 0x54, 0xd5, 0xcb, + 0xb1, 0xc5, 0x0b, 0x31, 0x49, 0x5a, 0xbc, 0xfb, 0xc0, 0xa0, 0x71, 0x3e, 0xa5, 0x1a, 0xa7, 0x00, + 0x89, 0x7b, 0xf8, 0x3e, 0x0c, 0xb1, 0x12, 0xde, 0x3f, 0x03, 0x31, 0xa6, 0x50, 0x33, 0x34, 0x0e, + 0x6c, 0xfd, 0x10, 0xae, 0x30, 0x6b, 0x2d, 0xf6, 0xec, 0xe9, 0x16, 0xd5, 0x77, 0x12, 0xc6, 0xda, + 0x25, 0x4e, 0x37, 0x09, 0x9f, 0x61, 0xb3, 0xed, 0x8b, 0xb5, 0x9d, 0x45, 0xbf, 0xa7, 0xa0, 0x5d, + 0x61, 0x84, 0x15, 0x8d, 0x46, 0xd8, 0x0d, 0xb8, 0x2e, 0x8d, 0xb0, 0x64, 0x33, 0xd2, 0x5b, 0xfd, + 0x7d, 0xb8, 0xcc, 0x06, 0x2a, 0xa2, 0x12, 0xf4, 0x6e, 0x7c, 0x92, 0x18, 0xe6, 0x1c, 0x1f, 0xa6, + 0x0e, 0x9d, 0x31, 0xc8, 0xbf, 0x57, 0x10, 0x5b, 0xce, 0x4c, 0xfc, 0x17, 0x6d, 0x95, 0x6e, 0x40, + 0x45, 0x32, 0x44, 0xef, 0xd1, 0xeb, 0x99, 0xa4, 0x4f, 0x61, 0x56, 0x25, 0xe3, 0x36, 0xf1, 0xee, + 0x7d, 0xa2, 0xb0, 0xa2, 0xf7, 0xc8, 0xb6, 0xa0, 0x05, 0x62, 0xd9, 0xcd, 0x1b, 0xf8, 0x46, 0xe1, + 0x6d, 0x09, 0x69, 0x35, 0x60, 0x21, 0x3d, 0x15, 0x6e, 0x53, 0x44, 0x6a, 0xa1, 0xcf, 0xc9, 0x16, + 0xa6, 0x25, 0x7c, 0x32, 0x32, 0x89, 0x8a, 0x7d, 0xcc, 0xd0, 0x05, 0x96, 0x65, 0x09, 0x51, 0x93, + 0x18, 0x3f, 0x69, 0x5d, 0xac, 0x87, 0x5f, 0x05, 0x24, 0xaa, 0x96, 0xea, 0xb6, 0x68, 0xfa, 0x12, + 0xf4, 0x2f, 0xd5, 0x6d, 0x1e, 0x20, 0x4a, 0xad, 0xe2, 0x66, 0x18, 0xd8, 0xa4, 0x2c, 0xa9, 0xb5, + 0x16, 0x7b, 0xd0, 0x5a, 0x1f, 0x0f, 0x8c, 0xf4, 0x97, 0x06, 0x6c, 0x54, 0x77, 0x0f, 0xbc, 0x3d, + 0x37, 0x3a, 0x94, 0x0d, 0x56, 0xad, 0x1f, 0xc0, 0xb4, 0xd6, 0x3c, 0xdf, 0xc5, 0xb9, 0x91, 0xad, + 0xe8, 0x26, 0x0c, 0x2f, 0x55, 0xe9, 0xb1, 0x23, 0x35, 0xeb, 0xc7, 0x99, 0xbc, 0x69, 0x3a, 0x0d, + 0x7a, 0x7b, 0xc1, 0x16, 0x95, 0xd6, 0x3f, 0x1d, 0x50, 0xa8, 0x2b, 0xf1, 0xc2, 0x39, 0xa3, 0xbb, + 0x0f, 0xc0, 0x56, 0x88, 0x32, 0x38, 0xa2, 0x00, 0x8e, 0xf1, 0x93, 0x12, 0x26, 0x92, 0x6d, 0x05, + 0xa8, 0xd7, 0x78, 0x62, 0x1e, 0xc2, 0xc4, 0x90, 0xc4, 0x71, 0xa2, 0x0c, 0x61, 0xe2, 0xa4, 0x43, + 0x5b, 0x05, 0x42, 0x3f, 0x4c, 0x86, 0xbd, 0x0d, 0xd2, 0xd3, 0xfb, 0x6f, 0x08, 0x97, 0x6f, 0x7a, + 0x6c, 0x67, 0x8b, 0x7c, 0x7b, 0x09, 0xb3, 0x04, 0xd7, 0x7d, 0x46, 0x63, 0xdb, 0x56, 0xbe, 0x8e, + 0xb0, 0xc7, 0x64, 0xfb, 0x10, 0x6d, 0xe7, 0xad, 0x9c, 0x76, 0x62, 0x60, 0xe6, 0x66, 0x6e, 0xc6, + 0x74, 0x1a, 0x58, 0xd6, 0xd9, 0x66, 0xfa, 0x74, 0x11, 0xd9, 0xeb, 0x2b, 0x5e, 0xab, 0xe3, 0xbb, + 0xd2, 0xa8, 0x60, 0x8b, 0x28, 0x68, 0x37, 0x30, 0x2f, 0xb7, 0x55, 0x20, 0xeb, 0x66, 0x6e, 0xb8, + 0xd9, 0x08, 0x0c, 0x6c, 0x2f, 0x6d, 0xaf, 0x97, 0x0a, 0xd6, 0x5d, 0x00, 0xa5, 0x25, 0x80, 0xa1, + 0x8d, 0x4d, 0xfb, 0x69, 0x75, 0xbd, 0xd4, 0x87, 0x66, 0xe1, 0xc2, 0xde, 0xda, 0xc6, 0xf2, 0xe6, + 0x5e, 0xbd, 0x51, 0x7f, 0x5a, 0xb5, 0xb7, 0x97, 0xaa, 0xf6, 0x72, 0xa9, 0x60, 0x7d, 0x05, 0x33, + 0xfa, 0x08, 0xcf, 0x75, 0x11, 0x46, 0x30, 0x2d, 0xf5, 0x99, 0xc7, 0x7b, 0xdb, 0x4a, 0x08, 0x0e, + 0x37, 0x90, 0x92, 0xa7, 0x82, 0xdc, 0x94, 0xe2, 0xdb, 0x48, 0x01, 0xd2, 0xce, 0x72, 0x8b, 0xb9, + 0x67, 0xb9, 0xd6, 0xb7, 0x61, 0x46, 0x6f, 0xb5, 0x57, 0x4f, 0xce, 0x37, 0x68, 0x6c, 0x92, 0x12, + 0x30, 0x4a, 0x2c, 0xf5, 0xb8, 0x8b, 0x5c, 0xb2, 0x7e, 0x1b, 0x4a, 0x1c, 0x2a, 0xfe, 0xf2, 0xde, + 0x10, 0xae, 0xb6, 0x82, 0x21, 0xb8, 0x5d, 0x44, 0x4a, 0xbc, 0x2d, 0x9c, 0xf7, 0xdd, 0x5a, 0xf8, + 0x93, 0x02, 0xcc, 0x27, 0x62, 0x2f, 0x97, 0x0e, 0x9d, 0x76, 0x1b, 0x7b, 0x07, 0x18, 0xdd, 0x82, + 0x81, 0xed, 0xcd, 0xed, 0x2d, 0xee, 0xdc, 0x9a, 0xe1, 0xcb, 0x94, 0x14, 0x49, 0x18, 0x9b, 0x42, + 0xa0, 0x27, 0x70, 0x41, 0x44, 0xe2, 0xc8, 0x2a, 0x6e, 0x94, 0x5c, 0xc9, 0x8f, 0xeb, 0x49, 0xe3, + 0xa1, 0xf7, 0x78, 0xa0, 0xe8, 0x2f, 0x1f, 0xbb, 0x01, 0x6e, 0x51, 0x83, 0x7d, 0x72, 0x11, 0xc5, + 0x81, 0xa2, 0xa2, 0xc6, 0x56, 0xc1, 0x58, 0x10, 0xbf, 0xf5, 0x3b, 0x05, 0x98, 0xcb, 0x88, 0x25, + 0x45, 0xef, 0x68, 0xc3, 0x99, 0x56, 0x86, 0x23, 0x40, 0x56, 0xfb, 0xf8, 0x78, 0x96, 0x94, 0xf0, + 0xa4, 0xfe, 0x33, 0x84, 0x27, 0xad, 0xf6, 0xc5, 0x21, 0x49, 0x35, 0x80, 0x11, 0x51, 0x6e, 0x4d, + 0xc1, 0x84, 0xc6, 0x37, 0xcb, 0x82, 0x71, 0xb5, 0x65, 0x32, 0x39, 0x4b, 0x7e, 0x4b, 0x4e, 0x0e, + 0xf9, 0xdb, 0xfa, 0x07, 0x05, 0x98, 0xa1, 0x43, 0x3c, 0x70, 0xc9, 0x6e, 0x8c, 0x39, 0xb4, 0xa8, + 0x8d, 0x64, 0x41, 0x1b, 0x49, 0x02, 0x56, 0x0e, 0xe9, 0xe3, 0xd4, 0x90, 0x16, 0x4c, 0x43, 0xa2, + 0x96, 0xa0, 0xeb, 0x7b, 0xda, 0x48, 0x94, 0x13, 0x84, 0xdf, 0x2d, 0xc0, 0xb4, 0xd2, 0x27, 0xd9, + 0xff, 0xfb, 0x5a, 0x97, 0x2e, 0x1b, 0xba, 0x94, 0x62, 0x72, 0x2d, 0xd5, 0xa3, 0x6f, 0xe4, 0xf5, + 0xa8, 0x2b, 0x8f, 0xff, 0xbc, 0x00, 0xb3, 0x46, 0x1e, 0xa0, 0x8b, 0x44, 0xdd, 0x6a, 0x06, 0x38, + 0xe2, 0xec, 0xe5, 0xbf, 0x48, 0xf9, 0x5a, 0x18, 0x1e, 0xe3, 0x80, 0x3b, 0xac, 0xf8, 0x2f, 0xf4, + 0x0d, 0x98, 0xd8, 0xc2, 0x81, 0xeb, 0xb7, 0x58, 0xe0, 0x1a, 0x3b, 0xdc, 0x9f, 0xb0, 0xf5, 0x42, + 0xb4, 0x00, 0xa3, 0xd5, 0xf6, 0x81, 0x1f, 0xb8, 0xd1, 0x21, 0x3b, 0xc4, 0x19, 0xb5, 0xe3, 0x02, + 0x42, 0x7b, 0xd9, 0x3d, 0x10, 0xf1, 0x2a, 0x13, 0x36, 0xff, 0x85, 0xe6, 0x61, 0x58, 0x38, 0x79, + 0xa8, 0x8b, 0xc8, 0x16, 0x3f, 0x09, 0xc6, 0x17, 0x36, 0x5d, 0x04, 0xf4, 0x72, 0x93, 0xcd, 0x7f, + 0x59, 0xb7, 0x61, 0xc6, 0xc4, 0x47, 0xe3, 0x92, 0xf9, 0x9b, 0x45, 0x98, 0xae, 0xb6, 0x5a, 0x4f, + 0x1f, 0x56, 0xd9, 0xb1, 0xa2, 0xd8, 0xfb, 0xef, 0xc1, 0xc0, 0x9a, 0xe7, 0x46, 0x5c, 0xc3, 0x11, + 0x51, 0xd7, 0x06, 0x48, 0x02, 0x45, 0x66, 0x88, 0xfc, 0x8f, 0x6c, 0x98, 0x5e, 0xf9, 0xda, 0x0d, + 0x23, 0xd7, 0x3b, 0x50, 0x43, 0xb7, 0x8b, 0xbd, 0x84, 0x6e, 0xaf, 0xf6, 0xd9, 0x26, 0x64, 0xb4, + 0x0d, 0x17, 0x37, 0xf0, 0x4b, 0xc3, 0x12, 0x92, 0x37, 0x5a, 0x94, 0x8d, 0x9e, 0x5a, 0x39, 0x19, + 0xb8, 0xea, 0x0a, 0xfd, 0x8d, 0x22, 0xbd, 0xf0, 0xa6, 0x0c, 0x8c, 0xb7, 0xbc, 0x03, 0x33, 0x4a, + 0x87, 0x62, 0x39, 0xc5, 0x78, 0x52, 0x31, 0x0f, 0x47, 0xdd, 0x48, 0x46, 0x74, 0xb4, 0x07, 0x73, + 0x7a, 0xa7, 0x62, 0xca, 0xfa, 0x66, 0x30, 0x81, 0xac, 0xf6, 0xd9, 0x59, 0xd8, 0x68, 0x11, 0xfa, + 0xab, 0xcd, 0xe7, 0x9c, 0x2d, 0xe6, 0x29, 0x63, 0x23, 0xab, 0x36, 0x9f, 0xaf, 0xf6, 0xd9, 0x04, + 0x58, 0xdb, 0x0f, 0xff, 0xb6, 0x00, 0x73, 0x19, 0x33, 0x8c, 0xae, 0x02, 0xb0, 0x42, 0xe5, 0x8b, + 0xa0, 0x94, 0x10, 0x05, 0x8d, 0x9f, 0x4b, 0xbf, 0xea, 0xb0, 0x99, 0x99, 0x94, 0x97, 0x43, 0xe2, + 0x0a, 0x5b, 0x01, 0x42, 0x5b, 0xe2, 0xd8, 0x9c, 0xdd, 0x51, 0xd1, 0xc5, 0xb6, 0x52, 0xa3, 0x9d, + 0x97, 0x27, 0xef, 0xa6, 0xa8, 0x24, 0xb8, 0x4b, 0x73, 0x29, 0x39, 0x0a, 0x39, 0x68, 0x74, 0x0b, + 0x86, 0x58, 0x21, 0x9f, 0x43, 0x71, 0x61, 0x34, 0x06, 0xe6, 0xf5, 0xd6, 0x3f, 0x2a, 0xc0, 0x45, + 0xf6, 0x45, 0x4c, 0x6d, 0x8d, 0x6f, 0x6b, 0x5b, 0xe3, 0xba, 0xec, 0xb0, 0x09, 0x58, 0xdb, 0x1d, + 0x35, 0xfd, 0x42, 0x43, 0xaf, 0xbb, 0x42, 0x45, 0x52, 0xd7, 0xed, 0x3f, 0x29, 0x08, 0x0f, 0x4f, + 0x7a, 0xe9, 0xae, 0xc0, 0xf8, 0xeb, 0x2d, 0x59, 0x0d, 0x0d, 0xbd, 0xcf, 0x56, 0x54, 0x31, 0x7f, + 0xa4, 0xb9, 0x8b, 0xea, 0x53, 0x28, 0x67, 0xb3, 0xa6, 0xdb, 0xb2, 0xb2, 0x1e, 0x1a, 0xb0, 0x5f, + 0x67, 0x3a, 0x8f, 0x53, 0x74, 0xea, 0xaf, 0xbc, 0xa6, 0x98, 0xd1, 0x9b, 0xc9, 0x10, 0xcf, 0xcc, + 0xb0, 0x39, 0xb5, 0xb7, 0xc5, 0xf8, 0x28, 0x81, 0x2f, 0x4e, 0xaa, 0xec, 0xa9, 0xdd, 0xff, 0xe3, + 0xa2, 0xbe, 0x16, 0x5f, 0xa7, 0xd1, 0x25, 0x98, 0xd8, 0xc0, 0x2f, 0x53, 0xed, 0xd2, 0x90, 0x20, + 0x0f, 0xbf, 0x6c, 0x28, 0x6d, 0xab, 0xb1, 0xf2, 0x1a, 0x0e, 0xda, 0x87, 0x49, 0x21, 0x35, 0x7a, + 0x15, 0x9e, 0xec, 0x82, 0x1e, 0x69, 0x21, 0xe3, 0x3a, 0x4d, 0x82, 0xe2, 0xf9, 0xef, 0x67, 0x6b, + 0x0b, 0xe6, 0xd3, 0xdc, 0xe3, 0xad, 0xbd, 0xd7, 0x6d, 0xee, 0x99, 0x2b, 0xa4, 0xa5, 0xaf, 0x83, + 0x55, 0xea, 0x9e, 0x92, 0x30, 0xd2, 0xdf, 0x70, 0x2f, 0x39, 0x19, 0x34, 0xb4, 0x48, 0x4c, 0x86, + 0x1a, 0x41, 0x23, 0x22, 0x7e, 0x97, 0xa8, 0x87, 0x4f, 0xa5, 0xc4, 0x3b, 0x76, 0x1b, 0x86, 0x79, + 0x51, 0xe2, 0x56, 0x7a, 0xbc, 0x2a, 0x05, 0x80, 0xf5, 0x7b, 0x05, 0xb8, 0x44, 0xfd, 0x8d, 0xae, + 0x77, 0xd0, 0xc6, 0x3b, 0xa1, 0x1e, 0xb4, 0xfb, 0xae, 0x26, 0x68, 0xe6, 0x32, 0x2e, 0x55, 0xfd, + 0xbc, 0xc4, 0xcb, 0x1f, 0x14, 0xa0, 0x6c, 0xea, 0xdb, 0xf9, 0x4a, 0x98, 0x3b, 0xdc, 0x98, 0x2b, + 0x72, 0x4f, 0x0a, 0x43, 0x97, 0x6d, 0x8a, 0xc1, 0x92, 0x41, 0x92, 0xff, 0x35, 0xd1, 0xf2, 0x7f, + 0x0a, 0x30, 0xb3, 0x16, 0xaa, 0x0a, 0x3e, 0x67, 0xdc, 0x1d, 0xd3, 0x1d, 0x4f, 0x3a, 0xaf, 0xab, + 0x7d, 0xa6, 0x3b, 0x9c, 0xef, 0x29, 0x97, 0x86, 0x8a, 0x79, 0x97, 0x37, 0x89, 0x2a, 0x29, 0x2f, + 0x3f, 0xdd, 0x84, 0x81, 0x0d, 0xa2, 0x4e, 0xf5, 0xf3, 0xf5, 0xc7, 0x30, 0x48, 0x11, 0xbd, 0xdf, + 0x43, 0xba, 0x4c, 0x7e, 0xa0, 0x87, 0xa9, 0x5b, 0x44, 0x03, 0xdd, 0x2f, 0x27, 0xae, 0xf6, 0x25, + 0x2f, 0x14, 0xd5, 0x46, 0x60, 0x68, 0xdb, 0x09, 0x0e, 0x70, 0x64, 0x7d, 0x1f, 0xca, 0x3c, 0x20, + 0x87, 0x79, 0x70, 0x69, 0xd8, 0x4e, 0x18, 0xc7, 0x5c, 0xe5, 0x05, 0xd1, 0x5c, 0x05, 0xa8, 0x47, + 0x4e, 0x10, 0xad, 0x79, 0x2d, 0xfc, 0x35, 0x0b, 0xf8, 0xb2, 0x95, 0x12, 0xeb, 0x7d, 0x18, 0x95, + 0x43, 0xa0, 0x16, 0xa0, 0xa2, 0x31, 0xd2, 0xe1, 0xcc, 0x68, 0xf7, 0x9a, 0xc4, 0x65, 0xa6, 0x07, + 0x30, 0x9b, 0x98, 0x8a, 0xf8, 0x9e, 0x9d, 0xb4, 0xcc, 0x68, 0x6c, 0x9a, 0x2d, 0x7f, 0x5b, 0x4b, + 0x70, 0x21, 0x35, 0xd3, 0x08, 0xd1, 0x2b, 0x70, 0xcc, 0xba, 0x27, 0x1f, 0x94, 0x7a, 0x7d, 0x95, + 0x94, 0x6d, 0xaf, 0xd7, 0x59, 0x5c, 0x3a, 0x29, 0xdb, 0x5e, 0xaf, 0xd7, 0x86, 0xd8, 0xca, 0xb1, + 0xfe, 0x79, 0x91, 0x1a, 0xbd, 0x29, 0x1e, 0x24, 0xfc, 0x87, 0xaa, 0x0f, 0xb3, 0x06, 0xa3, 0x74, + 0xc4, 0xcb, 0xe2, 0xe6, 0x45, 0x7e, 0x0c, 0xc9, 0xc8, 0x4f, 0x4e, 0x2a, 0x7d, 0x34, 0x70, 0x24, + 0x46, 0x43, 0x9f, 0xc1, 0xf0, 0x8a, 0xd7, 0xa2, 0x14, 0xfa, 0xcf, 0x40, 0x41, 0x20, 0x91, 0x79, + 0xa0, 0x5d, 0x26, 0xaa, 0x10, 0x77, 0x3b, 0xd9, 0x4a, 0x09, 0x65, 0x33, 0x8d, 0xc9, 0x1b, 0xa4, + 0x53, 0xc4, 0x7e, 0xd0, 0x5b, 0x8b, 0xa4, 0x0b, 0xe2, 0x25, 0x84, 0x51, 0x5b, 0xfe, 0x46, 0x16, + 0x0c, 0x6e, 0x06, 0x2d, 0x7e, 0x9b, 0x79, 0x72, 0x71, 0x9c, 0x2f, 0x2f, 0x5a, 0x66, 0xb3, 0x2a, + 0xeb, 0x7f, 0x15, 0x60, 0xee, 0x11, 0x8e, 0x8c, 0xeb, 0x46, 0xe3, 0x4a, 0xe1, 0x8d, 0xb9, 0x52, + 0x7c, 0x1d, 0xae, 0xc8, 0x51, 0xf7, 0x67, 0x8d, 0x7a, 0x20, 0x6b, 0xd4, 0x83, 0xd9, 0xa3, 0x7e, + 0x04, 0x43, 0x6c, 0xa8, 0xe8, 0x06, 0x0c, 0xae, 0x45, 0xf8, 0x28, 0x76, 0x86, 0xa8, 0x11, 0x70, + 0x36, 0xab, 0x23, 0x16, 0xd7, 0xba, 0x13, 0x46, 0xe2, 0x26, 0xc4, 0xa8, 0x2d, 0x7e, 0x5a, 0x3f, + 0xa2, 0x77, 0xb6, 0xd6, 0xfd, 0xe6, 0x73, 0xc5, 0x53, 0x3d, 0xcc, 0x76, 0x65, 0xf2, 0x64, 0x83, + 0x40, 0xb1, 0x1a, 0x5b, 0x40, 0xa0, 0x6b, 0x30, 0xb6, 0xe6, 0x3d, 0xf4, 0x83, 0x26, 0xde, 0xf4, + 0xda, 0x8c, 0xfa, 0x88, 0xad, 0x16, 0x71, 0x0f, 0x0e, 0x6f, 0x21, 0xf6, 0xe0, 0xd0, 0x82, 0x84, + 0x07, 0x87, 0x94, 0xed, 0x2e, 0xda, 0xac, 0x8e, 0x3b, 0x88, 0xc8, 0xdf, 0x79, 0xee, 0x1b, 0xe9, + 0xe7, 0xe9, 0x06, 0xb8, 0x0f, 0x97, 0x6c, 0xdc, 0x69, 0x3b, 0x44, 0xe1, 0x3a, 0xf2, 0x19, 0xbc, + 0x1c, 0xf3, 0x35, 0x43, 0xe8, 0xbc, 0x1e, 0x0f, 0x21, 0xbb, 0x5c, 0xcc, 0xe9, 0xf2, 0x11, 0x5c, + 0x7f, 0x84, 0x23, 0x5d, 0xca, 0xc5, 0x7e, 0x70, 0x3e, 0xf8, 0x55, 0x18, 0x09, 0x75, 0x1f, 0xfe, + 0x55, 0x71, 0x74, 0x64, 0x42, 0xdc, 0x7d, 0x20, 0x4e, 0xb9, 0x38, 0x1d, 0xf9, 0x97, 0xf5, 0x39, + 0x54, 0xb2, 0x9a, 0xeb, 0x2d, 0x5c, 0xd5, 0x85, 0x6b, 0xd9, 0x04, 0xe4, 0x67, 0x51, 0xf8, 0xfb, + 0xa5, 0xe9, 0x9c, 0xdf, 0x5b, 0xfd, 0x88, 0x80, 0xff, 0x61, 0xd5, 0x44, 0xe0, 0xde, 0x1b, 0x74, + 0xb7, 0x41, 0x8f, 0xd2, 0x75, 0x02, 0x31, 0x5f, 0xab, 0x30, 0x22, 0xca, 0x38, 0x5f, 0xe7, 0x8c, + 0x3d, 0x15, 0x0c, 0x6d, 0x09, 0x02, 0x12, 0xcd, 0xfa, 0x91, 0x38, 0x56, 0xd2, 0x31, 0x7a, 0xbb, + 0x0f, 0xd4, 0xcb, 0x39, 0x92, 0xe5, 0xc3, 0x25, 0x9d, 0xb6, 0x7a, 0x5c, 0x50, 0x52, 0x8e, 0x0b, + 0xd8, 0x29, 0xc1, 0x35, 0xdd, 0x7d, 0x5d, 0xe4, 0xeb, 0x32, 0x2e, 0x42, 0x57, 0xd5, 0x43, 0x81, + 0xf1, 0xf4, 0x05, 0xaa, 0x7b, 0x50, 0x36, 0x35, 0xa8, 0x38, 0x50, 0xa4, 0xe7, 0x99, 0x3f, 0xe7, + 0xf1, 0x6b, 0x05, 0xb0, 0xb4, 0xe8, 0x28, 0x3a, 0x43, 0x5b, 0x81, 0xff, 0xc2, 0x6d, 0x29, 0x07, + 0x5a, 0xef, 0x08, 0xc1, 0x46, 0xe3, 0xb1, 0xd8, 0xb5, 0x80, 0x64, 0x50, 0x35, 0x97, 0x76, 0xf7, + 0x60, 0x78, 0x03, 0x7f, 0x1d, 0x8b, 0x1f, 0xa6, 0x8b, 0xd2, 0x88, 0xa9, 0xe7, 0x58, 0xbd, 0x0f, + 0x2b, 0xc0, 0x88, 0x22, 0x74, 0x23, 0xb7, 0x0f, 0xbc, 0xff, 0xfb, 0x50, 0x4a, 0xd6, 0xf1, 0xb9, + 0xaf, 0x28, 0x91, 0x20, 0x69, 0x0a, 0xbb, 0xf7, 0x59, 0x40, 0xb9, 0x88, 0x2a, 0xea, 0x48, 0xca, + 0x29, 0x7a, 0x67, 0xef, 0x3d, 0xfa, 0x08, 0x60, 0xdb, 0x8f, 0x9c, 0xf6, 0x12, 0xf5, 0x71, 0x51, + 0xc1, 0x4f, 0x23, 0x75, 0x66, 0x23, 0x52, 0xda, 0x48, 0x5e, 0xdc, 0x55, 0x80, 0xad, 0xef, 0xd2, + 0x1d, 0x69, 0xee, 0x74, 0x6f, 0x9b, 0x64, 0x09, 0x6e, 0x24, 0xa2, 0x11, 0x5e, 0x83, 0x48, 0x04, + 0xb3, 0x84, 0xfd, 0x44, 0x85, 0x79, 0x14, 0xf8, 0xc7, 0x9d, 0x5f, 0xcc, 0xac, 0xff, 0x87, 0x02, + 0x0b, 0xa1, 0x54, 0x9b, 0xe5, 0x13, 0xbd, 0x04, 0x10, 0x97, 0x26, 0x42, 0xe9, 0x65, 0xc5, 0xee, + 0x7d, 0x66, 0xbc, 0xd2, 0x73, 0x8a, 0x03, 0x46, 0x40, 0x41, 0xfb, 0xc5, 0xce, 0xe4, 0x03, 0x1a, + 0x82, 0x20, 0x5b, 0xef, 0x8d, 0xef, 0x1f, 0x08, 0x1f, 0xcd, 0x19, 0xf1, 0x0e, 0x61, 0x86, 0xec, + 0x5d, 0x62, 0xa1, 0xf8, 0x81, 0x1b, 0xbd, 0x12, 0x58, 0x5b, 0xfc, 0x0e, 0x26, 0xc3, 0xfa, 0xf4, + 0x67, 0x27, 0x95, 0x0f, 0xcf, 0x72, 0xa1, 0x4d, 0xd0, 0xdc, 0x96, 0xf7, 0x36, 0xad, 0x39, 0xe8, + 0x5f, 0xb2, 0xd7, 0xa9, 0xa8, 0xb2, 0xd7, 0xa5, 0xa8, 0xb2, 0xd7, 0xad, 0xff, 0x51, 0x84, 0x0a, + 0xbb, 0xb6, 0x4d, 0x23, 0x57, 0x62, 0x5b, 0x49, 0x09, 0x85, 0xe9, 0xd5, 0x43, 0x90, 0xb8, 0x96, + 0x5d, 0xec, 0xe5, 0x5a, 0xf6, 0xaf, 0xbc, 0xbe, 0x57, 0xb5, 0xf6, 0xf6, 0xe9, 0x49, 0xe5, 0x46, + 0xec, 0x18, 0x60, 0xb5, 0x26, 0x0f, 0x41, 0x46, 0x13, 0x69, 0x97, 0xc6, 0xc0, 0x6b, 0xb8, 0x34, + 0xee, 0xc1, 0x30, 0x35, 0x3d, 0xd6, 0xb6, 0x78, 0xbc, 0x25, 0x5d, 0x9e, 0xf4, 0x85, 0x85, 0x86, + 0xab, 0x3e, 0xdd, 0x22, 0xc0, 0xac, 0x7f, 0x58, 0x84, 0x6b, 0xd9, 0x3c, 0xe7, 0x7d, 0x5b, 0x06, + 0x88, 0x63, 0x66, 0xf2, 0x62, 0x74, 0xe8, 0xde, 0x79, 0x89, 0xf7, 0x65, 0x8c, 0x9c, 0x82, 0x47, + 0xb4, 0x16, 0x71, 0xd9, 0x29, 0x71, 0x1a, 0xa6, 0xdd, 0x81, 0xe2, 0x2f, 0x83, 0xf1, 0x22, 0xed, + 0x65, 0x30, 0x5e, 0x86, 0xf6, 0x61, 0x6e, 0x2b, 0x70, 0x5f, 0x38, 0x11, 0x7e, 0x82, 0x5f, 0x6d, + 0xf9, 0x6d, 0xb7, 0xf9, 0x6a, 0x85, 0x5f, 0xf5, 0x61, 0x37, 0xd8, 0x6e, 0x9d, 0x9e, 0x54, 0xbe, + 0xd1, 0x61, 0x20, 0x64, 0x63, 0x36, 0x3a, 0x14, 0xa8, 0x91, 0xbe, 0xfd, 0x93, 0x45, 0xc8, 0xfa, + 0xf7, 0x05, 0xb8, 0x4c, 0x15, 0x6a, 0x7e, 0xb2, 0x20, 0x1a, 0x7f, 0xad, 0x50, 0x4d, 0x75, 0x80, + 0x7c, 0x2d, 0xd2, 0x50, 0x4d, 0xed, 0x32, 0x98, 0xad, 0x81, 0xa1, 0x35, 0x18, 0xe3, 0xbf, 0x15, + 0xf7, 0xf1, 0xac, 0x22, 0xb0, 0xe8, 0x52, 0x67, 0xde, 0x23, 0xba, 0xb0, 0x39, 0xb1, 0x06, 0xbd, + 0x22, 0xad, 0xe2, 0x5a, 0x3f, 0x2d, 0xc2, 0xc2, 0x2e, 0x0e, 0xdc, 0x67, 0xaf, 0x32, 0x06, 0xb3, + 0x09, 0x33, 0xa2, 0x88, 0x8e, 0x59, 0xdf, 0x62, 0xec, 0xed, 0x21, 0xd1, 0xd5, 0x90, 0x00, 0x34, + 0xe4, 0x8e, 0x33, 0x22, 0x9e, 0x21, 0x08, 0xf3, 0x3d, 0x18, 0x49, 0x3c, 0x9e, 0x40, 0xe7, 0x5f, + 0xec, 0xd0, 0x78, 0xaa, 0x56, 0xfb, 0x6c, 0x09, 0x89, 0x7e, 0x3d, 0xfb, 0x88, 0x92, 0x7b, 0x12, + 0xba, 0x3d, 0x8a, 0x43, 0x37, 0x2c, 0xd9, 0xac, 0x8e, 0x52, 0x6b, 0xd8, 0xb0, 0xab, 0x7d, 0x76, + 0x56, 0x4b, 0xb5, 0x31, 0x18, 0xad, 0xd2, 0x63, 0x57, 0x62, 0xb8, 0xff, 0xef, 0x22, 0x5c, 0x15, + 0xd7, 0x6d, 0x32, 0xd8, 0xfc, 0x25, 0xcc, 0x89, 0xa2, 0x6a, 0x87, 0x28, 0x0c, 0xb8, 0xa5, 0x73, + 0x9a, 0xbd, 0xff, 0x25, 0x38, 0xed, 0x70, 0x98, 0x98, 0xd9, 0x59, 0xe8, 0xe7, 0xe3, 0x10, 0xfd, + 0xcc, 0xf4, 0x94, 0x05, 0x75, 0x4c, 0xaa, 0x32, 0x53, 0x63, 0x8d, 0x26, 0x3f, 0x5b, 0x29, 0x87, + 0xea, 0xc0, 0x9b, 0x3a, 0x54, 0x57, 0xfb, 0x92, 0x2e, 0xd5, 0xda, 0x24, 0x8c, 0x6f, 0xe0, 0x97, + 0x31, 0xdf, 0xff, 0x4e, 0x21, 0x71, 0xdb, 0x92, 0x68, 0x18, 0xec, 0xda, 0x65, 0x21, 0x7e, 0x0d, + 0x81, 0xde, 0xb6, 0x54, 0x35, 0x0c, 0x06, 0xba, 0x06, 0xc3, 0x2c, 0x6c, 0xb7, 0xd5, 0x83, 0x6d, + 0x2e, 0xef, 0xcd, 0x34, 0x19, 0x0a, 0x33, 0xd3, 0x39, 0xbe, 0xf5, 0x04, 0xae, 0xf3, 0xa8, 0x71, + 0x7d, 0xf2, 0x69, 0x43, 0x67, 0xfc, 0x7c, 0x59, 0x0e, 0x5c, 0x7d, 0x84, 0x93, 0xa2, 0x47, 0xbb, + 0x57, 0xf4, 0x39, 0x4c, 0x69, 0xe5, 0x92, 0x22, 0xd5, 0x4a, 0xe5, 0x1a, 0x92, 0xa4, 0x93, 0xd0, + 0xd6, 0x35, 0x53, 0x13, 0x6a, 0x67, 0x2d, 0x4c, 0x1f, 0xf2, 0x0a, 0xe2, 0x53, 0xe4, 0xf0, 0x0c, + 0x52, 0xef, 0x96, 0xb2, 0xaf, 0x99, 0xc4, 0x63, 0x4f, 0x1a, 0x89, 0x2f, 0xaf, 0xac, 0xb5, 0x26, + 0x60, 0x6c, 0xc9, 0xf7, 0x22, 0xfc, 0x35, 0x55, 0x75, 0xac, 0x49, 0x18, 0x17, 0x55, 0x6d, 0x1c, + 0x86, 0xd6, 0xef, 0xf7, 0x83, 0xc5, 0x19, 0x6b, 0xf2, 0x9e, 0x0a, 0x7e, 0xec, 0xa7, 0x3a, 0xcb, + 0x3f, 0x54, 0x17, 0x55, 0x1f, 0x71, 0x5c, 0xcb, 0x56, 0x1e, 0xd5, 0xf3, 0x9a, 0x71, 0xa9, 0xb6, + 0xf2, 0x52, 0xa3, 0xff, 0x41, 0x86, 0x98, 0x64, 0x9b, 0xed, 0xad, 0xd3, 0x93, 0xca, 0xf5, 0x0c, + 0x31, 0xa9, 0xd1, 0x35, 0x8b, 0x4c, 0x5b, 0x63, 0x03, 0x57, 0x39, 0x90, 0xbc, 0x16, 0x29, 0x6b, + 0x78, 0x0c, 0x13, 0x2b, 0x68, 0x90, 0x01, 0xe8, 0x3b, 0x52, 0x01, 0x45, 0x3b, 0x3a, 0x2f, 0xf9, + 0x7e, 0x14, 0x51, 0x1b, 0x6a, 0x15, 0xa3, 0xda, 0x51, 0x4a, 0x34, 0xaa, 0x1a, 0x19, 0xd5, 0x23, + 0xfe, 0xdb, 0x32, 0x76, 0x9f, 0x7c, 0x48, 0xdd, 0x36, 0xe6, 0x17, 0x55, 0xc4, 0xb4, 0x1c, 0x9b, + 0x4f, 0xbf, 0x0b, 0x3d, 0xc9, 0x68, 0xfa, 0xae, 0x2a, 0xe6, 0xe8, 0x59, 0x47, 0x2e, 0x26, 0xfa, + 0xd6, 0x49, 0x41, 0xdc, 0x58, 0x48, 0x1d, 0x09, 0x9f, 0x55, 0x93, 0xac, 0x69, 0xa7, 0xb8, 0xc5, + 0x8c, 0x53, 0x5c, 0xed, 0xcc, 0x2b, 0xea, 0x72, 0xac, 0xdb, 0xff, 0xe6, 0xc7, 0x40, 0xff, 0x6a, + 0x18, 0x2e, 0x6c, 0x39, 0x07, 0xae, 0x47, 0x64, 0x8f, 0x8d, 0x43, 0xff, 0x38, 0x68, 0x62, 0x54, + 0x85, 0x49, 0x3d, 0x78, 0xb4, 0x4b, 0x68, 0x2c, 0x11, 0xaf, 0x7a, 0x19, 0x5a, 0x84, 0x51, 0x79, + 0xa9, 0x93, 0xcb, 0x44, 0xc3, 0x65, 0xcf, 0xd5, 0x3e, 0x3b, 0x06, 0x43, 0x1f, 0x69, 0x5e, 0xff, + 0x29, 0x79, 0x3f, 0x99, 0xc2, 0x2e, 0xb2, 0xe8, 0x3e, 0xcf, 0x6f, 0xe9, 0x72, 0x9d, 0x79, 0xce, + 0x7f, 0x94, 0x3a, 0x08, 0x18, 0xd4, 0x7a, 0x9c, 0x72, 0xbc, 0xd0, 0x4f, 0x5a, 0xe6, 0x9b, 0x84, + 0xe9, 0x23, 0x02, 0xf4, 0x7d, 0x18, 0x7b, 0x72, 0xbc, 0x8f, 0xc5, 0x91, 0xc7, 0x10, 0x17, 0xf3, + 0xc9, 0x90, 0x68, 0x5e, 0xbf, 0xfb, 0x80, 0xcd, 0xc1, 0xf3, 0xe3, 0x7d, 0x9c, 0x7e, 0xec, 0x92, + 0xec, 0x2f, 0x85, 0x18, 0x3a, 0x84, 0x52, 0x32, 0x7a, 0x99, 0x7a, 0x9a, 0x73, 0x63, 0xae, 0xe9, + 0x33, 0x88, 0xca, 0x93, 0x9a, 0x2c, 0xa6, 0x52, 0x6b, 0x24, 0x45, 0x15, 0xfd, 0x2a, 0xcc, 0x1a, + 0xdd, 0x5e, 0xfc, 0x61, 0xcd, 0x6e, 0x1e, 0x35, 0x2a, 0x9b, 0x12, 0x5c, 0x13, 0x17, 0xa2, 0xb4, + 0x96, 0xcd, 0xad, 0xa0, 0x16, 0x4c, 0x25, 0xa2, 0x72, 0xf9, 0xbb, 0xbe, 0xd9, 0x71, 0xbe, 0x54, + 0xbe, 0x8a, 0x37, 0xe6, 0x8c, 0x6d, 0x25, 0x49, 0xa2, 0x75, 0x18, 0x95, 0x56, 0x2b, 0x7d, 0x60, + 0xc1, 0x6c, 0xa1, 0xcf, 0x9f, 0x9e, 0x54, 0x66, 0x62, 0x0b, 0x5d, 0xa3, 0x19, 0x13, 0x40, 0x3f, + 0x86, 0xeb, 0x72, 0x89, 0x6e, 0x06, 0x66, 0x5f, 0x06, 0x7f, 0xb2, 0xf3, 0x76, 0x72, 0x85, 0x67, + 0xc1, 0xef, 0xde, 0x5f, 0xed, 0xb3, 0xbb, 0x93, 0xad, 0x01, 0x8c, 0x04, 0x7c, 0x53, 0x3e, 0x1e, + 0x18, 0x19, 0x28, 0x0d, 0xb2, 0x75, 0x23, 0xa2, 0x9d, 0xff, 0x78, 0x88, 0x5d, 0x1f, 0xdc, 0xf1, + 0xdc, 0x67, 0x6e, 0xbc, 0x7f, 0x55, 0x1f, 0x49, 0xfc, 0x86, 0x37, 0xd7, 0x60, 0x32, 0x5e, 0xeb, + 0x96, 0xee, 0x94, 0x62, 0x57, 0x77, 0xca, 0x03, 0xe5, 0xc8, 0x40, 0x79, 0x21, 0x8b, 0x7d, 0xa9, + 0x74, 0xf7, 0x45, 0x7c, 0x96, 0xf0, 0x15, 0x0c, 0xd1, 0xa7, 0x81, 0xd8, 0x79, 0xcc, 0xd8, 0xe2, + 0x1d, 0x2e, 0xb5, 0x72, 0xba, 0xcf, 0xde, 0x12, 0xe2, 0x57, 0x82, 0xe9, 0x83, 0x5a, 0x6d, 0x5a, + 0xa0, 0x3e, 0x04, 0xc4, 0x40, 0xd0, 0x36, 0x4c, 0x6f, 0x05, 0xb8, 0xc5, 0x63, 0x6e, 0x3b, 0x01, + 0x37, 0x31, 0x99, 0xf1, 0x4a, 0x5f, 0xf8, 0xec, 0x88, 0xea, 0x06, 0x96, 0xf5, 0xaa, 0xf4, 0x37, + 0xa0, 0xa3, 0x15, 0x98, 0xac, 0x63, 0x27, 0x68, 0x1e, 0x3e, 0xc1, 0xaf, 0xc8, 0x47, 0x4b, 0x7b, + 0x16, 0x37, 0xa4, 0x35, 0x64, 0xbc, 0xb4, 0x4a, 0x3d, 0xbc, 0xd7, 0x91, 0xd0, 0x77, 0x61, 0xa8, + 0xee, 0x07, 0x51, 0xed, 0x15, 0xdf, 0xd3, 0xc2, 0x63, 0xcf, 0x0a, 0x6b, 0x97, 0xc4, 0xd3, 0xc0, + 0xa1, 0x1f, 0x44, 0x8d, 0x7d, 0x95, 0x7d, 0x1c, 0x0f, 0xbd, 0x82, 0x19, 0x7d, 0x3f, 0x3d, 0x74, + 0xdb, 0x44, 0x08, 0x8d, 0xf0, 0x98, 0x26, 0xd3, 0xa6, 0x65, 0x20, 0xb5, 0x5b, 0x9c, 0xfa, 0xb5, + 0xe4, 0xae, 0x7d, 0x46, 0xeb, 0xd5, 0x87, 0xc5, 0x4d, 0xf8, 0xe8, 0x29, 0x7d, 0x51, 0x99, 0x8d, + 0xa8, 0x1a, 0xb2, 0x68, 0x57, 0xf6, 0xa4, 0x09, 0x7d, 0xca, 0xe2, 0x98, 0xee, 0x49, 0xca, 0x09, + 0x27, 0x4c, 0x3e, 0x94, 0x6d, 0xa7, 0x50, 0xd1, 0x16, 0x5c, 0xd8, 0x09, 0xf1, 0x56, 0x80, 0x5f, + 0xb8, 0xf8, 0xa5, 0xa0, 0xc7, 0xde, 0x40, 0xa1, 0xd3, 0x44, 0xe8, 0x75, 0x58, 0xad, 0x89, 0x60, + 0x1a, 0xb9, 0xfc, 0x11, 0x8c, 0x29, 0xeb, 0xc4, 0x70, 0x2f, 0x7c, 0x46, 0xbd, 0x17, 0x3e, 0xaa, + 0xde, 0xff, 0xfe, 0xc7, 0x05, 0x58, 0x30, 0xaf, 0x3f, 0xee, 0xb0, 0xd8, 0x84, 0x51, 0x59, 0x28, + 0x6f, 0x39, 0x08, 0xa5, 0x27, 0xf1, 0xd1, 0x64, 0x9b, 0x40, 0xec, 0x56, 0xb5, 0xd3, 0x31, 0x8d, + 0xd7, 0xf0, 0x44, 0xfe, 0x9b, 0x61, 0x98, 0x21, 0x7d, 0x4c, 0xed, 0xed, 0xcf, 0xe9, 0xfb, 0x0e, + 0xb4, 0x4c, 0x71, 0xac, 0x71, 0x1b, 0x9b, 0x95, 0x53, 0x05, 0x42, 0x7b, 0xdf, 0x57, 0x41, 0x40, + 0xef, 0xab, 0xe7, 0xb8, 0x45, 0xe5, 0xdd, 0x63, 0x51, 0xa8, 0x0e, 0x21, 0x3e, 0xe0, 0x7d, 0x47, + 0x3b, 0x46, 0xec, 0x59, 0x50, 0x0c, 0xf4, 0x2a, 0x28, 0x76, 0xa4, 0xa0, 0x60, 0xef, 0x06, 0xbc, + 0xad, 0x08, 0x8a, 0xf3, 0x97, 0x10, 0x43, 0xe7, 0x2d, 0x21, 0x86, 0xdf, 0x4c, 0x42, 0x8c, 0xbc, + 0xa6, 0x84, 0x78, 0x48, 0x6c, 0x66, 0x62, 0xc7, 0x4b, 0x17, 0x31, 0xdb, 0xa4, 0xdc, 0x2e, 0xa6, + 0xc6, 0xbf, 0xc9, 0x4f, 0x9c, 0xc0, 0xca, 0x94, 0x34, 0xf0, 0x57, 0x23, 0x69, 0xc6, 0xce, 0x59, + 0xd2, 0x8c, 0xff, 0x15, 0x49, 0x9a, 0x3f, 0x2f, 0xb0, 0x63, 0x8c, 0xff, 0x1f, 0x45, 0xcc, 0x9b, + 0x1c, 0x2d, 0xfc, 0x7a, 0x7c, 0xe5, 0x9a, 0x5f, 0x0f, 0x0f, 0x9c, 0xe6, 0xf3, 0xf8, 0x6c, 0xe7, + 0x87, 0x64, 0x7f, 0xa8, 0x15, 0xf4, 0x39, 0xaa, 0x58, 0x2d, 0xd7, 0x2b, 0x77, 0xef, 0x8b, 0x8d, + 0xc3, 0x6f, 0x9e, 0xb3, 0x62, 0x7d, 0xe3, 0xa8, 0x08, 0xf4, 0xa2, 0xc1, 0x94, 0x65, 0xb3, 0x1b, + 0xc3, 0xc6, 0x1e, 0x7c, 0x90, 0xbe, 0xf3, 0x4a, 0xf5, 0xbe, 0xf8, 0xce, 0xab, 0xca, 0xc6, 0xf8, + 0xf6, 0xeb, 0x0e, 0x5c, 0xb6, 0xf1, 0x91, 0xff, 0x02, 0x9f, 0x2f, 0xd9, 0x1f, 0xc0, 0x25, 0x9d, + 0xe0, 0x4e, 0xa7, 0x45, 0x5f, 0xf4, 0x61, 0x11, 0x1e, 0xc6, 0x17, 0x4b, 0x39, 0x02, 0x7b, 0xb1, + 0x94, 0xbd, 0x61, 0x47, 0xfe, 0x54, 0xe5, 0x2d, 0xad, 0xb3, 0x7c, 0x58, 0xd0, 0x89, 0x57, 0x5b, + 0x2d, 0x9a, 0x3d, 0xa5, 0xe9, 0x76, 0x1c, 0x2f, 0x42, 0x9b, 0x30, 0xa6, 0xfc, 0x4c, 0x58, 0x65, + 0x4a, 0x0d, 0x9b, 0xfd, 0x4e, 0x5c, 0xa0, 0x5a, 0x8f, 0x0a, 0x9c, 0x85, 0xa1, 0x92, 0x64, 0x0f, + 0x61, 0x99, 0xda, 0x66, 0x0d, 0x26, 0x94, 0x9f, 0xd2, 0xc9, 0x41, 0x5f, 0x23, 0x56, 0x5a, 0xd0, + 0x19, 0xa6, 0xa3, 0x58, 0x4d, 0x28, 0x9b, 0x98, 0x46, 0x5f, 0x9a, 0x79, 0x85, 0x56, 0xe2, 0x37, + 0x6b, 0xba, 0x47, 0xd6, 0x4c, 0x65, 0xbd, 0x57, 0x63, 0xfd, 0xe6, 0x00, 0x5c, 0xe6, 0x93, 0x71, + 0x9e, 0x33, 0x8e, 0x7e, 0x04, 0x63, 0xca, 0x1c, 0x73, 0xa6, 0x5f, 0x13, 0xc1, 0x78, 0x59, 0x6b, + 0x81, 0x59, 0x8f, 0xc7, 0xb4, 0xa0, 0x91, 0x98, 0x6e, 0x62, 0x3d, 0xaa, 0xcb, 0xa6, 0x0d, 0x93, + 0xfa, 0x44, 0x73, 0x03, 0xfa, 0x86, 0xb1, 0x11, 0x1d, 0x54, 0x3c, 0x7f, 0xd7, 0x6a, 0x18, 0xa7, + 0x9b, 0xd8, 0xc1, 0x89, 0x45, 0xf4, 0x35, 0x5c, 0x48, 0xcd, 0x32, 0x77, 0x08, 0xdd, 0x34, 0x36, + 0x98, 0x82, 0x66, 0xc9, 0x20, 0x02, 0x5a, 0x9c, 0xd9, 0x6c, 0xba, 0x11, 0xd4, 0x82, 0x71, 0x75, + 0xe2, 0xb9, 0x85, 0x7f, 0x3d, 0x87, 0x95, 0x0c, 0x90, 0x29, 0x45, 0x9c, 0x97, 0x74, 0xee, 0x5f, + 0xe9, 0x4e, 0x29, 0x0d, 0x78, 0x04, 0x86, 0xd8, 0x6f, 0x22, 0x02, 0xb6, 0x02, 0x1c, 0x62, 0xaf, + 0x89, 0xd5, 0xb8, 0xca, 0x37, 0x15, 0x01, 0xff, 0xae, 0x00, 0xf3, 0x26, 0xba, 0x75, 0xec, 0xb5, + 0xd0, 0x16, 0x94, 0x92, 0x0d, 0xf1, 0x55, 0x6d, 0x89, 0xaf, 0x42, 0x76, 0x97, 0x88, 0xc5, 0x9f, + 0xea, 0xe6, 0x06, 0x5c, 0x50, 0xca, 0xce, 0x18, 0xc0, 0x9a, 0x46, 0x55, 0x9d, 0x76, 0xab, 0x34, + 0x4e, 0x77, 0xd9, 0x3f, 0x72, 0x5c, 0x8f, 0x28, 0x88, 0xca, 0xb3, 0x35, 0x10, 0x97, 0x72, 0xde, + 0x30, 0xc7, 0x16, 0x2d, 0x15, 0xc1, 0xdc, 0x12, 0xc4, 0xfa, 0x94, 0x4a, 0x70, 0xee, 0x0e, 0x61, + 0xd7, 0x08, 0x25, 0xb1, 0x6b, 0x30, 0xb8, 0xbd, 0x5e, 0x5f, 0xaa, 0xf2, 0x4b, 0x89, 0xec, 0x2a, + 0x7b, 0x3b, 0x6c, 0x34, 0x1d, 0x9b, 0x55, 0x58, 0x9f, 0xd0, 0x47, 0x4a, 0xf9, 0x13, 0x97, 0x12, + 0xef, 0x2d, 0x18, 0xe6, 0x45, 0x1c, 0x93, 0x86, 0xc1, 0xb4, 0x39, 0x94, 0xa8, 0xb3, 0xb6, 0x84, + 0x7e, 0xdd, 0xc6, 0x4e, 0xa8, 0x7c, 0x98, 0x3f, 0x24, 0xa6, 0x37, 0x2b, 0xe3, 0xdf, 0xe5, 0x49, + 0xf9, 0x82, 0x34, 0x2d, 0x66, 0x8e, 0x3e, 0x01, 0x63, 0xcb, 0xbf, 0xac, 0x75, 0xfa, 0xec, 0xc4, + 0xe6, 0xda, 0xf2, 0x12, 0xe1, 0x2a, 0x67, 0x96, 0x98, 0x8e, 0xbb, 0x34, 0x46, 0x34, 0xc2, 0xea, + 0x95, 0x44, 0xca, 0x1a, 0xba, 0xc9, 0xf9, 0x63, 0x2b, 0x0a, 0x88, 0xf5, 0x40, 0x3e, 0x62, 0x61, + 0xa0, 0x96, 0xf5, 0xf0, 0xf2, 0x06, 0x7d, 0x9e, 0xe3, 0x11, 0x3d, 0x60, 0x3f, 0x8f, 0x4e, 0xfc, + 0x5e, 0x81, 0xbd, 0xf7, 0x51, 0xdf, 0x54, 0x72, 0x54, 0x78, 0xcf, 0x7c, 0xe5, 0x14, 0x41, 0x69, + 0xe6, 0x89, 0xeb, 0xb5, 0xd4, 0x53, 0x04, 0xe7, 0x38, 0x3a, 0x14, 0x69, 0x35, 0x1a, 0xcf, 0x5d, + 0xaf, 0x65, 0x27, 0xa1, 0xd1, 0x47, 0x30, 0xa1, 0x14, 0xc9, 0xcf, 0x04, 0x7b, 0xe2, 0x53, 0x45, + 0x77, 0x5b, 0xb6, 0x0e, 0x69, 0xfd, 0x76, 0x11, 0x2e, 0xee, 0x74, 0x42, 0x1a, 0xb4, 0xbb, 0xe6, + 0xbd, 0xc0, 0x5e, 0xe4, 0x07, 0xaf, 0x68, 0xd0, 0x21, 0x7a, 0x1f, 0x06, 0x57, 0x71, 0xbb, 0xed, + 0xf3, 0x2d, 0x74, 0x45, 0xb8, 0xf0, 0x93, 0xd0, 0x14, 0x68, 0xb5, 0xcf, 0x66, 0xd0, 0xe8, 0x23, + 0x18, 0x5d, 0xc5, 0x4e, 0x10, 0xed, 0x63, 0x47, 0x7c, 0x25, 0xc5, 0x93, 0x93, 0x0a, 0x0a, 0x07, + 0x58, 0xed, 0xb3, 0x63, 0x68, 0xb4, 0x08, 0x03, 0x5b, 0xbe, 0x77, 0x20, 0x6f, 0xf5, 0x65, 0x34, + 0x48, 0x60, 0x56, 0xfb, 0x6c, 0x0a, 0x8b, 0x9e, 0xc2, 0x44, 0xf5, 0x00, 0x7b, 0xd1, 0x53, 0x1c, + 0x39, 0x2d, 0x27, 0x72, 0xb8, 0x34, 0x7d, 0x2b, 0x0b, 0x59, 0x03, 0x5e, 0xed, 0xb3, 0x75, 0xec, + 0xda, 0x20, 0xf4, 0x3f, 0x0d, 0x0f, 0xac, 0x93, 0x02, 0xcc, 0x2f, 0xfb, 0x2f, 0x3d, 0x23, 0x63, + 0xbe, 0xad, 0x33, 0x46, 0x84, 0x96, 0x1b, 0xe0, 0x13, 0xac, 0x79, 0x0f, 0x06, 0xb6, 0x5c, 0xef, + 0x20, 0x21, 0x40, 0x0c, 0x78, 0x04, 0x8a, 0x8e, 0xd0, 0xf5, 0x0e, 0xd0, 0xba, 0x90, 0xdc, 0xdc, + 0xb2, 0xeb, 0xd7, 0x3e, 0x17, 0x06, 0x6c, 0x15, 0x3a, 0x96, 0xd0, 0xec, 0xb7, 0x18, 0xe0, 0x3b, + 0x30, 0x97, 0xd1, 0x2e, 0x9a, 0x94, 0xdb, 0x62, 0x80, 0x6e, 0x87, 0xb7, 0x61, 0xd6, 0x38, 0x05, + 0x29, 0xc0, 0xff, 0x5e, 0x30, 0xac, 0x25, 0x36, 0xf2, 0x79, 0x18, 0x16, 0x2f, 0x13, 0x33, 0x95, + 0x5f, 0xfc, 0xa4, 0x21, 0xb4, 0xd4, 0x8d, 0x17, 0xbf, 0x9d, 0x28, 0x7e, 0xa3, 0x5d, 0xe5, 0x99, + 0x84, 0x7e, 0x6a, 0x1a, 0x7e, 0xfc, 0x06, 0xe9, 0xc3, 0x24, 0x2d, 0xd2, 0xe6, 0xaa, 0x1f, 0x46, + 0x9e, 0x8c, 0xf0, 0xb0, 0xe5, 0x6f, 0x74, 0x1b, 0x4a, 0x2b, 0x5f, 0x47, 0x38, 0xf0, 0x9c, 0x36, + 0x7f, 0xa3, 0x95, 0xe7, 0x85, 0xb2, 0x53, 0xe5, 0xd6, 0x9f, 0x15, 0xe9, 0x9b, 0x8d, 0x39, 0x0b, + 0x8c, 0xf0, 0x68, 0xb3, 0xce, 0xc7, 0x5c, 0xdc, 0xac, 0xa3, 0x05, 0x18, 0xdd, 0xac, 0x6b, 0x8f, + 0x34, 0xdb, 0x71, 0x01, 0x69, 0x9c, 0x74, 0xa4, 0x1a, 0x34, 0x0f, 0xdd, 0x08, 0x37, 0xa3, 0xe3, + 0x80, 0x3f, 0x55, 0x61, 0xa7, 0xca, 0x91, 0x05, 0xe3, 0x8f, 0xda, 0xee, 0x7e, 0x53, 0x10, 0x63, + 0x03, 0xd1, 0xca, 0xd0, 0x4d, 0x98, 0x5c, 0xf3, 0xc2, 0xc8, 0x69, 0xb7, 0xd9, 0x1b, 0xd6, 0x3c, + 0xbd, 0x9d, 0x9d, 0x28, 0x25, 0xed, 0x2e, 0xf9, 0x5e, 0xe4, 0xb8, 0x1e, 0x0e, 0xec, 0x63, 0x2f, + 0x72, 0x8f, 0x30, 0x8f, 0xe2, 0x4e, 0x95, 0xa3, 0xf7, 0x60, 0x56, 0x96, 0x6d, 0x06, 0xcd, 0x43, + 0x1c, 0x46, 0x01, 0x7d, 0xef, 0x9f, 0x5e, 0xca, 0xb7, 0xcd, 0x95, 0xb4, 0x85, 0xb6, 0x7f, 0xdc, + 0x5a, 0xf1, 0x5e, 0xb8, 0x81, 0xef, 0xd1, 0xa4, 0x35, 0x23, 0xbc, 0x85, 0x44, 0xb9, 0xb5, 0x65, + 0xdc, 0x7b, 0x6f, 0xb0, 0x90, 0xac, 0xd3, 0x02, 0x2c, 0x18, 0xb7, 0x87, 0x10, 0xc1, 0x2a, 0x72, + 0x21, 0xb1, 0x0a, 0x6f, 0xc3, 0x00, 0x95, 0xc9, 0xcc, 0xae, 0x10, 0x27, 0x99, 0x14, 0x9f, 0x91, + 0x22, 0xb5, 0x36, 0x85, 0x41, 0x8f, 0xa4, 0xff, 0xa5, 0x9f, 0x7e, 0xf5, 0xee, 0x26, 0x25, 0x9f, + 0xa1, 0x71, 0xd5, 0x0f, 0x23, 0x3c, 0x2e, 0x6f, 0x62, 0x2e, 0xff, 0x59, 0x01, 0x2a, 0x5d, 0xa4, + 0x82, 0x1c, 0x53, 0xa1, 0x87, 0x31, 0x3d, 0x96, 0x63, 0x62, 0x31, 0xd3, 0x8b, 0xbd, 0x49, 0x9e, + 0xf3, 0x1e, 0xd6, 0x12, 0xa0, 0xf4, 0xf7, 0x03, 0xbd, 0x0b, 0xa3, 0xf5, 0xfa, 0xaa, 0x76, 0xce, + 0x96, 0x3c, 0xfa, 0xb2, 0x63, 0x08, 0xeb, 0x03, 0xb8, 0x28, 0x89, 0xb0, 0x47, 0x63, 0x95, 0x8b, + 0x19, 0x3c, 0x97, 0x94, 0xbc, 0x0f, 0x12, 0x17, 0x58, 0x7f, 0x3a, 0x90, 0x42, 0xac, 0x1f, 0x1f, + 0x1d, 0x39, 0xc1, 0x2b, 0x54, 0xd5, 0x11, 0xfb, 0xbb, 0x7e, 0x2a, 0x6b, 0x03, 0x3f, 0x39, 0xa9, + 0xf4, 0x29, 0xd4, 0xd1, 0x37, 0x60, 0x82, 0x6e, 0x48, 0xaf, 0x89, 0x99, 0x1b, 0xa1, 0xc8, 0x6e, + 0x69, 0x6b, 0x85, 0x68, 0x17, 0x26, 0xf8, 0x5a, 0xa7, 0xbf, 0xc5, 0x12, 0xbb, 0x97, 0x5c, 0x62, + 0x5a, 0xf7, 0xee, 0x68, 0x28, 0x6c, 0x32, 0x74, 0x32, 0xe8, 0x7b, 0x30, 0x29, 0x24, 0x1a, 0x27, + 0xcc, 0x0e, 0x19, 0xee, 0xe7, 0x13, 0xd6, 0x71, 0x18, 0xe5, 0x04, 0x21, 0xd2, 0x65, 0x2e, 0x74, + 0x39, 0xe5, 0xc1, 0x5e, 0xba, 0xac, 0xa1, 0xf0, 0x2e, 0x6b, 0x65, 0xe5, 0xef, 0x02, 0x4a, 0x8f, + 0xab, 0xdb, 0x6a, 0x9a, 0x50, 0x56, 0x53, 0xb9, 0x0a, 0xd3, 0x86, 0x01, 0x9c, 0x89, 0xc4, 0x77, + 0x01, 0xa5, 0x7b, 0x7a, 0x16, 0x0a, 0xd6, 0x2d, 0xb8, 0x29, 0x59, 0x20, 0x57, 0x83, 0x46, 0x53, + 0x18, 0x0b, 0xbf, 0x56, 0x84, 0x4a, 0x17, 0x50, 0xf4, 0xfb, 0x85, 0x24, 0xb7, 0xd9, 0x6a, 0xfc, + 0x28, 0xc9, 0x6d, 0x33, 0xbe, 0x81, 0xed, 0xb5, 0x8f, 0xff, 0xd6, 0x5f, 0xbc, 0xf6, 0xe7, 0x36, + 0x3d, 0x65, 0x67, 0xe7, 0xd6, 0x80, 0xca, 0x2d, 0x1b, 0x66, 0x34, 0x45, 0xa5, 0x17, 0xd9, 0x7d, + 0x15, 0x80, 0x3f, 0x66, 0xbf, 0xee, 0x1f, 0xf0, 0x6b, 0x2d, 0x4a, 0x89, 0xf5, 0x10, 0x66, 0x13, + 0x34, 0xb9, 0x01, 0xf3, 0x2e, 0xc8, 0x00, 0x7c, 0x4a, 0xb4, 0xbf, 0x76, 0xe1, 0x67, 0x27, 0x95, + 0x09, 0xf2, 0x05, 0xbc, 0x13, 0xbf, 0xcd, 0x28, 0xfe, 0xb2, 0x9e, 0xaa, 0x26, 0x58, 0xb5, 0xad, + 0x5d, 0x48, 0xbc, 0x0f, 0x43, 0xac, 0x24, 0xf1, 0x02, 0x9a, 0x0a, 0xcd, 0x65, 0x02, 0x07, 0xb4, + 0x66, 0x69, 0xd0, 0x31, 0xfd, 0x51, 0x8d, 0xaf, 0xb7, 0x58, 0x3b, 0xec, 0xd5, 0xdc, 0xb8, 0x58, + 0xbe, 0xb2, 0x36, 0x50, 0x8d, 0xaf, 0xe1, 0x08, 0x3f, 0xb3, 0x80, 0xf3, 0xfc, 0x97, 0x6d, 0xdc, + 0x3a, 0xa0, 0xd9, 0xdd, 0x6a, 0xe3, 0xdc, 0xcf, 0x3c, 0xe0, 0x10, 0x02, 0x14, 0xcd, 0xfa, 0x1c, + 0x66, 0x97, 0xda, 0xd8, 0x09, 0x92, 0xed, 0xa1, 0x9b, 0x30, 0x4c, 0xcb, 0xf4, 0xe8, 0x0c, 0x87, + 0x14, 0xd1, 0xe8, 0x0c, 0x5e, 0x49, 0x6c, 0x36, 0xf6, 0x30, 0x95, 0x3a, 0xa4, 0xd8, 0x5c, 0x1a, + 0xa4, 0xbf, 0x13, 0x21, 0xab, 0x86, 0xd1, 0x33, 0x38, 0xeb, 0x33, 0x1a, 0x13, 0x65, 0x4a, 0xec, + 0xd7, 0x5b, 0x10, 0xf5, 0xdf, 0x80, 0x85, 0x6a, 0xa7, 0x83, 0xbd, 0x56, 0x8c, 0xb8, 0x1d, 0x38, + 0x3d, 0x5e, 0x4e, 0x41, 0x55, 0x18, 0xa4, 0xd0, 0xd2, 0xd7, 0xc4, 0xbb, 0x6b, 0xe8, 0x0e, 0x85, + 0xe3, 0x4f, 0xe2, 0xd0, 0x06, 0x18, 0xa6, 0xd5, 0x82, 0xb9, 0xfa, 0xf1, 0xfe, 0x91, 0xcb, 0x72, + 0xe8, 0xd1, 0x0b, 0x5e, 0xa2, 0xed, 0x35, 0xf1, 0xd0, 0x39, 0x63, 0xc6, 0xad, 0x38, 0x61, 0x9f, + 0x9a, 0x4d, 0xfa, 0xc5, 0xfd, 0x3b, 0x31, 0x2a, 0xb5, 0x39, 0x58, 0x2b, 0xb4, 0x9a, 0x3f, 0x86, + 0x6e, 0x4d, 0xc3, 0x05, 0xd5, 0x6e, 0x67, 0x2b, 0x64, 0x16, 0xa6, 0x75, 0x7b, 0x9c, 0x15, 0x7f, + 0x05, 0x33, 0xcc, 0xd1, 0xcc, 0x9e, 0xb4, 0x5b, 0x8c, 0x5f, 0x6f, 0x2b, 0xee, 0x2e, 0x26, 0xc2, + 0x53, 0xe8, 0xf1, 0xb9, 0x7c, 0xac, 0x74, 0x77, 0x91, 0xc5, 0xb5, 0xbe, 0x58, 0xd4, 0xbc, 0x3e, + 0xc5, 0xdd, 0xc5, 0xda, 0x30, 0x7f, 0x1a, 0x88, 0x50, 0x67, 0xd3, 0xff, 0x73, 0xa1, 0xbe, 0x48, + 0xaf, 0x52, 0xac, 0x62, 0x87, 0x86, 0x3d, 0x99, 0x03, 0xd2, 0x27, 0xa1, 0xe8, 0xb6, 0x84, 0x96, + 0xed, 0xb6, 0xac, 0x3f, 0x2a, 0xc0, 0x2d, 0xa6, 0x8b, 0x98, 0xf1, 0xa8, 0x71, 0x9e, 0x81, 0x8c, + 0x3e, 0x04, 0x96, 0xd4, 0x8a, 0x2b, 0x7c, 0x16, 0xef, 0x79, 0x1e, 0x25, 0x86, 0x80, 0xaa, 0x30, + 0xae, 0xc6, 0x47, 0x25, 0x1e, 0xbc, 0xc8, 0x70, 0x14, 0xd9, 0x63, 0x47, 0xcf, 0x1c, 0x19, 0x33, + 0xf5, 0x1c, 0x2e, 0xaf, 0x7c, 0x4d, 0x16, 0x04, 0xff, 0x3a, 0xf1, 0xc3, 0x9e, 0x38, 0xe0, 0x79, + 0x6a, 0x9b, 0xaf, 0x18, 0x5d, 0x0d, 0x4e, 0x16, 0x13, 0xf3, 0x40, 0x7c, 0xe0, 0xa4, 0xf6, 0x3a, + 0x6a, 0x6b, 0x65, 0xd6, 0x9f, 0x16, 0x60, 0xc1, 0xdc, 0x1a, 0x17, 0x2c, 0x6b, 0x70, 0x61, 0xc9, + 0xf1, 0x7c, 0xcf, 0x6d, 0x3a, 0xed, 0x7a, 0xf3, 0x10, 0xb7, 0x8e, 0xdb, 0x22, 0x6c, 0x4c, 0x4a, + 0x19, 0x62, 0xee, 0x70, 0x74, 0x01, 0x62, 0xa7, 0xb1, 0xd0, 0x07, 0x70, 0x91, 0x06, 0xed, 0x30, + 0xd9, 0xdb, 0xc6, 0x81, 0xa4, 0xc7, 0x7a, 0x96, 0x51, 0x8b, 0xee, 0xc1, 0x34, 0xfb, 0xa8, 0xb4, + 0x76, 0x3c, 0x37, 0x92, 0x48, 0xcc, 0x2a, 0x32, 0x55, 0xdd, 0x6e, 0xa8, 0xb1, 0x62, 0xe8, 0x32, + 0xcc, 0x2d, 0xaf, 0xec, 0xae, 0x2d, 0xad, 0x34, 0xb6, 0xbf, 0xb7, 0xb5, 0xd2, 0xd0, 0x1f, 0x01, + 0x9b, 0x81, 0x92, 0x5a, 0xb9, 0xbd, 0xb9, 0xbd, 0xc5, 0x52, 0x4d, 0xaa, 0xa5, 0x7b, 0x2b, 0xb5, + 0xea, 0xce, 0xf6, 0xea, 0x46, 0xa9, 0xdf, 0x1a, 0x18, 0x29, 0x96, 0x8a, 0xb7, 0x7f, 0xa4, 0x05, + 0x92, 0xa1, 0x05, 0x98, 0xe7, 0xe0, 0x3b, 0xf5, 0xea, 0xa3, 0xec, 0x26, 0x58, 0xed, 0xd3, 0x87, + 0xd5, 0x52, 0x01, 0x5d, 0x81, 0x4b, 0x5a, 0xe9, 0x56, 0xb5, 0x5e, 0xdf, 0xdb, 0xb4, 0x97, 0xd7, + 0x57, 0xea, 0xf5, 0x52, 0xf1, 0xf6, 0xae, 0xf6, 0x70, 0x14, 0x69, 0xe1, 0xe9, 0xc3, 0x6a, 0xc3, + 0x5e, 0xf9, 0x62, 0x67, 0xcd, 0x5e, 0x59, 0x4e, 0xb7, 0xa0, 0xd5, 0x7e, 0x6f, 0xa5, 0x5e, 0x2a, + 0xa0, 0x69, 0x98, 0xd2, 0x4a, 0x37, 0x36, 0x4b, 0xc5, 0xdb, 0x37, 0xf9, 0x9d, 0x54, 0x34, 0x09, + 0xb0, 0xbc, 0x52, 0x5f, 0x5a, 0xd9, 0x58, 0x5e, 0xdb, 0x78, 0x54, 0xea, 0x43, 0x13, 0x30, 0x5a, + 0x95, 0x3f, 0x0b, 0xb7, 0x3f, 0x86, 0xa9, 0x84, 0x2d, 0x40, 0x20, 0xa4, 0x1a, 0x5d, 0xea, 0x23, + 0x3c, 0x92, 0x3f, 0xa9, 0x01, 0xc7, 0xd4, 0xfa, 0x52, 0x61, 0xf1, 0x7f, 0xfe, 0x66, 0x01, 0xc6, + 0xc8, 0x42, 0x17, 0x01, 0x45, 0x5f, 0x29, 0xaa, 0x33, 0x9f, 0x60, 0x9e, 0x55, 0x21, 0x53, 0x4f, + 0xa6, 0x32, 0xaf, 0x9c, 0xe3, 0x58, 0xa1, 0x00, 0xb7, 0x0a, 0xf7, 0x0a, 0xc8, 0xa6, 0xee, 0xcb, + 0x84, 0x26, 0x29, 0x29, 0x9b, 0x95, 0xfd, 0xf2, 0x95, 0x5c, 0x05, 0x14, 0xfd, 0x0a, 0x58, 0x2a, + 0xcd, 0x0c, 0x7d, 0xeb, 0xdd, 0xde, 0xf4, 0x2a, 0xd1, 0xe6, 0xcd, 0xde, 0xc0, 0xd1, 0x63, 0x98, + 0x20, 0x9a, 0x88, 0x04, 0x43, 0x97, 0x93, 0x88, 0x8a, 0xf2, 0x53, 0x5e, 0x30, 0x57, 0xca, 0x47, + 0x5d, 0xc7, 0xe9, 0x40, 0x98, 0x19, 0x11, 0x22, 0x71, 0x73, 0x41, 0x94, 0xb0, 0xc3, 0xe3, 0xf2, + 0x85, 0x44, 0xf1, 0xee, 0xfd, 0x7b, 0x05, 0x54, 0xa7, 0x17, 0x7e, 0x35, 0x95, 0x06, 0x89, 0x08, + 0xb7, 0xb4, 0xae, 0xc3, 0x7a, 0x53, 0x91, 0x69, 0x0a, 0x32, 0x74, 0xa1, 0x0d, 0x40, 0x69, 0x4d, + 0x01, 0x5d, 0x8b, 0xd7, 0x81, 0x59, 0x89, 0x28, 0x5f, 0x4c, 0x9d, 0x4a, 0xad, 0x90, 0x6f, 0x05, + 0x5a, 0x81, 0x49, 0x1e, 0x96, 0xcc, 0x75, 0x17, 0x94, 0xa7, 0xfd, 0x64, 0x92, 0x79, 0x44, 0xf9, + 0x24, 0xf5, 0x1f, 0x54, 0x8e, 0xc7, 0x91, 0x54, 0x8a, 0xca, 0x97, 0x8d, 0x75, 0x7c, 0x7c, 0x0f, + 0x61, 0x52, 0x57, 0xa5, 0x90, 0x98, 0x20, 0xa3, 0x86, 0x95, 0xd9, 0xa1, 0x06, 0xcc, 0x3d, 0x75, + 0x5c, 0xea, 0x48, 0xe1, 0x67, 0x1f, 0xe2, 0xe4, 0x02, 0x55, 0x72, 0x8e, 0x32, 0xea, 0xd8, 0x6b, + 0x95, 0xbb, 0x3d, 0x75, 0x41, 0xb7, 0x4d, 0x5d, 0x68, 0x04, 0xfa, 0xc9, 0x0f, 0xb2, 0xf4, 0xd4, + 0x33, 0xa6, 0xc3, 0xbc, 0x72, 0xd6, 0xf9, 0x33, 0x7a, 0x4a, 0x55, 0x92, 0x04, 0x45, 0x65, 0x4d, + 0x9c, 0x99, 0xdc, 0x3c, 0x0d, 0x8e, 0x8f, 0xdc, 0xe4, 0x41, 0x72, 0x88, 0x32, 0x18, 0x97, 0x49, + 0xec, 0x5e, 0x01, 0x7d, 0x45, 0x77, 0xb5, 0x91, 0xdc, 0x9e, 0x1b, 0x1d, 0xf2, 0x40, 0x8a, 0xcb, + 0x46, 0x02, 0x7c, 0xa3, 0xe4, 0x50, 0xb7, 0x61, 0xc6, 0x74, 0xe4, 0x2d, 0x19, 0x9a, 0x73, 0x1e, + 0x9e, 0xb9, 0x0a, 0x6c, 0xa2, 0x58, 0xb5, 0xb2, 0x27, 0x29, 0xe7, 0xc4, 0x35, 0x93, 0xe6, 0xa7, + 0x30, 0x49, 0x56, 0xc9, 0x13, 0x8c, 0x3b, 0xd5, 0xb6, 0xfb, 0x02, 0x87, 0x48, 0x3c, 0x03, 0x23, + 0x8b, 0xb2, 0x70, 0x6f, 0x15, 0xd0, 0x37, 0x61, 0x6c, 0xcf, 0x89, 0x9a, 0x87, 0xfc, 0xd5, 0x02, + 0xf1, 0xa8, 0x01, 0x2d, 0x2b, 0x8b, 0x5f, 0xb4, 0xf2, 0x5e, 0x01, 0x7d, 0x07, 0x86, 0x1f, 0xe1, + 0x88, 0x46, 0x18, 0x5f, 0x97, 0xa7, 0x3f, 0x2c, 0xd2, 0x62, 0xcd, 0x93, 0x31, 0x51, 0xa2, 0xc3, + 0x49, 0xb7, 0x0d, 0xba, 0x0b, 0xc0, 0x04, 0x02, 0xa5, 0x90, 0xac, 0x2e, 0xa7, 0xba, 0x8d, 0x1e, + 0x91, 0x0f, 0x7f, 0x1b, 0x47, 0xb8, 0xd7, 0x26, 0xb3, 0x78, 0xb4, 0x0e, 0x93, 0xf2, 0x1d, 0xdc, + 0x0d, 0x7a, 0x45, 0xc5, 0x4a, 0x10, 0x0b, 0xcf, 0x40, 0xed, 0x63, 0xb2, 0x2b, 0x58, 0xa2, 0x14, + 0xf9, 0x44, 0x0e, 0xca, 0x7a, 0x34, 0x47, 0x32, 0x91, 0x81, 0x29, 0xb8, 0xab, 0x7e, 0x18, 0xe9, + 0xb8, 0xb2, 0xc4, 0x8c, 0x8b, 0xa1, 0xac, 0xb6, 0xab, 0x3f, 0x97, 0x13, 0xcb, 0xdc, 0xac, 0x57, + 0x7e, 0xca, 0xd7, 0x73, 0x20, 0x98, 0xb8, 0xa3, 0x92, 0x64, 0x99, 0xd8, 0xaa, 0xac, 0x99, 0xcd, + 0x0e, 0xf6, 0xea, 0xf5, 0x55, 0xfa, 0x5a, 0x8a, 0x38, 0xf3, 0x51, 0xca, 0x04, 0x61, 0x94, 0xae, + 0x22, 0x5f, 0x3d, 0xed, 0x99, 0x96, 0xf8, 0xab, 0x67, 0x78, 0x47, 0x27, 0xfe, 0xea, 0x19, 0x5f, + 0x76, 0x79, 0xc2, 0xac, 0x67, 0x2d, 0xe3, 0xfc, 0xee, 0x22, 0x12, 0xe1, 0xe6, 0x5a, 0x05, 0xdf, + 0xd8, 0x17, 0x4d, 0x75, 0xbb, 0x0f, 0xee, 0x15, 0xd0, 0x0a, 0x4c, 0xcb, 0x1b, 0x45, 0x71, 0x15, + 0xca, 0x40, 0xc8, 0xf9, 0xc2, 0xcc, 0x1a, 0xc8, 0xec, 0x2e, 0xe6, 0x10, 0x32, 0x96, 0xa3, 0xcf, + 0x61, 0x9a, 0xaf, 0x4d, 0xad, 0x3f, 0x25, 0x29, 0x66, 0xf8, 0xc9, 0x5e, 0x66, 0x4f, 0x1e, 0xc3, + 0x6c, 0x3d, 0xc1, 0x1d, 0x16, 0x09, 0x71, 0x49, 0x27, 0xa1, 0x64, 0x09, 0xce, 0xa4, 0xf5, 0x04, + 0x10, 0xb3, 0x74, 0x05, 0xb9, 0x17, 0x2e, 0x7e, 0x89, 0xae, 0x24, 0xba, 0x4e, 0x0a, 0x29, 0x18, + 0x95, 0x53, 0x99, 0x23, 0xdb, 0x66, 0xd9, 0x7c, 0x58, 0x06, 0x42, 0xa7, 0xe3, 0xec, 0xbb, 0x6d, + 0x37, 0x72, 0x31, 0x59, 0xaa, 0x2a, 0x82, 0x5a, 0x25, 0xd6, 0xc3, 0xa5, 0x4c, 0x08, 0xf4, 0x19, + 0x4c, 0x3c, 0xc2, 0x51, 0x9c, 0x08, 0x19, 0xcd, 0xa5, 0x52, 0x27, 0xf3, 0x35, 0x20, 0x2e, 0xc2, + 0xea, 0xd9, 0x97, 0xd7, 0xa0, 0xc4, 0xc4, 0xac, 0x42, 0xe2, 0x4a, 0x8a, 0x04, 0x07, 0x71, 0x02, + 0xe7, 0x28, 0xcc, 0xe4, 0xd6, 0x5d, 0x76, 0xfc, 0x87, 0xc4, 0xfa, 0x57, 0xf5, 0xb8, 0x69, 0xad, + 0x4c, 0x3e, 0x5c, 0x36, 0x6b, 0xcc, 0x00, 0x8c, 0x6e, 0xc4, 0xdf, 0xd4, 0xcc, 0xb4, 0xbe, 0x65, + 0x94, 0xbc, 0xa6, 0xba, 0xfb, 0x00, 0xc9, 0xac, 0x2a, 0x06, 0xa2, 0x37, 0xb5, 0x4f, 0xff, 0xd9, + 0xe8, 0x7e, 0x06, 0xa3, 0x32, 0x95, 0xac, 0x94, 0x4f, 0xc9, 0x44, 0xbc, 0xe5, 0xf9, 0x74, 0x05, + 0x1f, 0xe9, 0xa7, 0x2c, 0x71, 0xb4, 0x8e, 0x9f, 0xcc, 0xb6, 0x9a, 0xc9, 0xd8, 0x8f, 0x60, 0x4c, + 0xc9, 0xb3, 0x2a, 0x17, 0x72, 0x3a, 0xf7, 0x6a, 0x79, 0x42, 0xe9, 0xfb, 0xee, 0xe2, 0xbd, 0x02, + 0xba, 0x4b, 0xbf, 0x51, 0xf4, 0x9e, 0xd6, 0x6c, 0x8c, 0xa6, 0x64, 0x0a, 0x4c, 0xa0, 0xa0, 0x6f, + 0xd3, 0xe7, 0x6c, 0x96, 0x8e, 0x83, 0x80, 0xd8, 0xbd, 0x04, 0x2f, 0x4b, 0x15, 0x49, 0x20, 0x7e, + 0x46, 0xa5, 0x92, 0x82, 0xc8, 0x22, 0x2d, 0xbb, 0x61, 0xb3, 0xe7, 0x90, 0xef, 0x15, 0xd0, 0x03, + 0x18, 0x11, 0x69, 0xd9, 0xd1, 0x45, 0xbd, 0xab, 0xd9, 0xc3, 0x7b, 0x00, 0xc0, 0x98, 0x4d, 0x7b, + 0xaa, 0x57, 0x67, 0xb2, 0xf3, 0x01, 0xf9, 0xf0, 0xb6, 0xce, 0x88, 0xf4, 0x99, 0xf8, 0xf8, 0x52, + 0xa4, 0x79, 0x6d, 0x0a, 0x55, 0x76, 0x66, 0xe1, 0x13, 0xcd, 0x59, 0xcb, 0x16, 0x1f, 0x6b, 0xce, + 0xa6, 0x24, 0xf2, 0x99, 0x74, 0xd6, 0xa0, 0x54, 0x6d, 0xd2, 0x0f, 0x82, 0xcc, 0xbe, 0x28, 0xcd, + 0x96, 0x64, 0x85, 0xa0, 0x35, 0x9b, 0x4c, 0xe6, 0xb8, 0x8e, 0x1d, 0xfa, 0xc2, 0xcf, 0x9c, 0x54, + 0x2e, 0x12, 0x55, 0x66, 0x8c, 0x1c, 0x33, 0x65, 0x66, 0x89, 0x18, 0x56, 0xed, 0x37, 0x23, 0xf3, + 0x31, 0x95, 0x65, 0x4a, 0x66, 0xca, 0x8b, 0x49, 0x7c, 0x69, 0xd0, 0x89, 0xb0, 0x2e, 0x09, 0x5a, + 0x85, 0x29, 0xfe, 0x9e, 0x88, 0x64, 0x4b, 0x16, 0x76, 0x56, 0xf3, 0xdf, 0x86, 0xc9, 0x15, 0x22, + 0xeb, 0x8f, 0x5b, 0x2e, 0x7b, 0xd5, 0x0c, 0xe9, 0xcf, 0x54, 0x65, 0x22, 0xae, 0x8a, 0x44, 0xd3, + 0x4a, 0xca, 0x46, 0xb9, 0x4b, 0xd3, 0x59, 0x31, 0xcb, 0x33, 0x82, 0xac, 0x9a, 0xdd, 0x91, 0x5b, + 0xfb, 0x73, 0x19, 0x49, 0x12, 0xd1, 0x5b, 0x9a, 0x11, 0x99, 0x95, 0xe9, 0xd0, 0xa0, 0x36, 0x7e, + 0xa9, 0xe4, 0xa3, 0xc9, 0xa0, 0x99, 0x9f, 0x3d, 0x31, 0x73, 0xdc, 0xf2, 0x1d, 0x22, 0x63, 0x96, + 0x43, 0xf4, 0x8e, 0x4e, 0x3d, 0x27, 0x13, 0x62, 0x66, 0x0b, 0xd4, 0x48, 0xd7, 0x93, 0xf0, 0xa1, + 0xab, 0xf9, 0xb9, 0x02, 0x15, 0x23, 0x3d, 0x23, 0x7b, 0xdf, 0x63, 0xba, 0xcc, 0xe2, 0xa4, 0x35, + 0x48, 0x35, 0x79, 0x93, 0x39, 0x7b, 0xa4, 0x2e, 0x66, 0xce, 0xc4, 0xb7, 0x05, 0x53, 0x89, 0x1c, + 0x77, 0xd2, 0x37, 0x63, 0xce, 0xb2, 0x57, 0xbe, 0x9a, 0x55, 0xcd, 0x29, 0xd6, 0x45, 0x86, 0x77, + 0xa5, 0x83, 0x57, 0xb5, 0x0f, 0x4b, 0xba, 0x8f, 0x95, 0xcc, 0x7a, 0x39, 0xe4, 0x52, 0x32, 0x27, + 0x91, 0x24, 0x9a, 0x91, 0xac, 0x28, 0x47, 0x92, 0xcd, 0xa8, 0x33, 0x2a, 0xc7, 0x9d, 0x25, 0xe9, + 0xb3, 0xe8, 0x6c, 0xc3, 0xac, 0x31, 0x85, 0x90, 0xfc, 0xfa, 0xe7, 0x25, 0x18, 0xca, 0xa4, 0x8a, + 0xe1, 0xa2, 0x39, 0x8b, 0x18, 0xfa, 0x86, 0x6e, 0xfa, 0x9b, 0x73, 0x2a, 0x95, 0xdf, 0xea, 0x02, + 0xc5, 0x19, 0xfa, 0x15, 0xfd, 0xda, 0xa5, 0xda, 0xb8, 0xae, 0x38, 0x03, 0x32, 0x1a, 0xb0, 0xf2, + 0x40, 0xe4, 0x1a, 0x98, 0x31, 0x65, 0x31, 0xcc, 0x64, 0xf1, 0x8d, 0x6c, 0x9a, 0xf1, 0xc2, 0xda, + 0x15, 0x8f, 0xf6, 0x64, 0x72, 0x26, 0x37, 0xdb, 0x54, 0x8e, 0x35, 0x59, 0x96, 0xeb, 0xa1, 0xf7, + 0x2e, 0x67, 0x7b, 0x86, 0x66, 0x4c, 0x39, 0xce, 0x92, 0x8e, 0x1b, 0x53, 0x0a, 0x2b, 0xc9, 0x86, + 0xdc, 0x24, 0x69, 0xbb, 0xcc, 0x89, 0xa3, 0x53, 0x57, 0x9d, 0x38, 0x46, 0xd2, 0xd7, 0xb2, 0x01, + 0xe2, 0x15, 0x61, 0x48, 0xd6, 0x28, 0x57, 0x44, 0x76, 0xda, 0x48, 0xb9, 0x22, 0xf2, 0x72, 0x3d, + 0xda, 0x62, 0xd3, 0x65, 0xb0, 0x25, 0x27, 0xb3, 0x57, 0x8e, 0xa5, 0x34, 0x1f, 0x4f, 0x5c, 0xa2, + 0xdb, 0x67, 0x9d, 0xb6, 0xaf, 0xe0, 0x52, 0x66, 0x16, 0x2f, 0xf4, 0x76, 0x6a, 0x43, 0x67, 0x70, + 0x22, 0xbb, 0xa7, 0x13, 0x5a, 0x02, 0x2e, 0xe9, 0xc5, 0x4a, 0xe4, 0xfa, 0x4a, 0x49, 0x6c, 0x43, + 0x22, 0xb0, 0x47, 0x54, 0xc1, 0x55, 0x92, 0x79, 0x65, 0x8e, 0xf5, 0x8a, 0x89, 0x4e, 0x98, 0x96, + 0xa9, 0x4a, 0xbf, 0x84, 0x26, 0x96, 0xac, 0x38, 0x8b, 0x4c, 0xed, 0xa5, 0x6b, 0x59, 0x74, 0x96, + 0xa9, 0xa5, 0x20, 0x72, 0x7b, 0xa1, 0x4b, 0x1a, 0x9b, 0xb4, 0xaf, 0x64, 0x59, 0x1b, 0x9c, 0xfe, + 0x81, 0x5c, 0xa2, 0xee, 0x62, 0x99, 0x4b, 0x2c, 0xb3, 0x17, 0x97, 0xd3, 0x34, 0x34, 0x57, 0xb1, + 0xe4, 0x02, 0xeb, 0xcd, 0x42, 0x92, 0x39, 0x5a, 0x87, 0xb2, 0x87, 0x84, 0x54, 0xd6, 0x74, 0xe9, + 0x52, 0xb6, 0x86, 0x3a, 0xcd, 0x74, 0x7e, 0xf6, 0xaa, 0xa6, 0xb8, 0x5b, 0x7f, 0x51, 0xfa, 0xbd, + 0x94, 0xd2, 0x1c, 0x37, 0xc7, 0x16, 0x8d, 0x00, 0x35, 0xa4, 0x45, 0x93, 0x32, 0x34, 0x37, 0x6b, + 0x9a, 0x41, 0x3b, 0x93, 0x52, 0x39, 0x93, 0x62, 0x6e, 0x9e, 0xb4, 0xcc, 0x9e, 0xfe, 0x50, 0x91, + 0xca, 0xa9, 0xe4, 0x67, 0xe8, 0x56, 0x52, 0x35, 0xcb, 0xca, 0x8f, 0x96, 0x23, 0xf5, 0x67, 0x4c, + 0x79, 0xd3, 0x14, 0xdf, 0x6d, 0x66, 0x52, 0x35, 0x03, 0x17, 0xa4, 0x78, 0xcb, 0xa0, 0x96, 0x93, + 0x45, 0x2d, 0xb3, 0x87, 0xdf, 0x57, 0xc4, 0x5b, 0x22, 0xdb, 0x99, 0xf4, 0x05, 0x74, 0x49, 0x87, + 0x96, 0x49, 0x7b, 0x83, 0xc6, 0x0c, 0xa7, 0x53, 0x95, 0x49, 0xdd, 0x25, 0x2f, 0x91, 0x99, 0xd1, + 0xb5, 0x3b, 0x9b, 0x1e, 0x22, 0xa1, 0x77, 0x31, 0xe1, 0x98, 0xed, 0xd6, 0x31, 0x29, 0x87, 0x0d, + 0x29, 0xce, 0x12, 0x72, 0x38, 0x3b, 0x09, 0x5a, 0x8e, 0xa1, 0x33, 0x55, 0x77, 0x0f, 0x3c, 0x25, + 0x43, 0x99, 0x34, 0x73, 0xd2, 0x49, 0xd3, 0xa4, 0x88, 0x31, 0x25, 0x34, 0xdb, 0x24, 0x1a, 0x0e, + 0xd3, 0xcf, 0xd5, 0x5c, 0x53, 0xa8, 0x9c, 0x9d, 0x62, 0x4b, 0x8a, 0x1b, 0x63, 0x72, 0x2a, 0x85, + 0xa0, 0x9a, 0xe8, 0x49, 0x12, 0x34, 0xe4, 0x9c, 0x92, 0x04, 0x8d, 0x99, 0xa1, 0x98, 0xe7, 0xc4, + 0xf6, 0xdb, 0x58, 0xf5, 0x9c, 0x28, 0x69, 0x9a, 0x12, 0x2e, 0x0c, 0xf4, 0x09, 0x75, 0x60, 0xe4, + 0x7b, 0x3d, 0xe6, 0x74, 0x4a, 0xb1, 0xb4, 0x7c, 0x20, 0x0e, 0x03, 0x68, 0x83, 0x3a, 0xe5, 0xee, + 0x3e, 0x09, 0x8a, 0xa4, 0xfb, 0x24, 0xd4, 0x8e, 0x66, 0xbb, 0x37, 0xc7, 0xd5, 0x67, 0xfe, 0x25, + 0xaf, 0x0c, 0xb9, 0x48, 0x24, 0xaf, 0x4c, 0x19, 0x3e, 0xa8, 0xe9, 0xba, 0x2d, 0x0c, 0xf0, 0x98, + 0xde, 0x95, 0xdc, 0x14, 0x1d, 0xe5, 0xab, 0xf9, 0x79, 0x2d, 0xf8, 0x39, 0x5e, 0x29, 0x99, 0x89, + 0x00, 0x99, 0x32, 0xac, 0x28, 0x09, 0x1e, 0xa4, 0x35, 0x94, 0x99, 0xc2, 0x60, 0x4b, 0xf8, 0x98, + 0x75, 0xba, 0x19, 0x79, 0x36, 0x54, 0xd2, 0xf9, 0x0a, 0x4a, 0x9c, 0x94, 0x40, 0x35, 0x29, 0x53, + 0x49, 0x0f, 0x54, 0x05, 0xc5, 0x90, 0xc7, 0xc0, 0x15, 0xb7, 0x66, 0xcd, 0xb9, 0xbe, 0xde, 0xd1, + 0x6d, 0xbd, 0x9c, 0xe7, 0xaa, 0xba, 0x9e, 0x94, 0xa2, 0x5f, 0x12, 0x79, 0xbe, 0xd3, 0x39, 0x6d, + 0xde, 0x4a, 0x78, 0x4b, 0xcd, 0x0f, 0x1c, 0x95, 0xf3, 0x52, 0xe6, 0xa0, 0xa7, 0xf4, 0x88, 0x7d, + 0x73, 0x6d, 0x79, 0x89, 0xc7, 0x02, 0xf8, 0x41, 0xea, 0xd8, 0x6a, 0xcf, 0x8d, 0x0e, 0x59, 0x92, + 0x27, 0x45, 0xfa, 0x30, 0x10, 0x0d, 0x71, 0xf7, 0x01, 0xaa, 0x53, 0xcd, 0x5d, 0x2b, 0x35, 0x9c, + 0x5c, 0x19, 0x08, 0x96, 0xcd, 0x04, 0x69, 0xaa, 0x4a, 0xaa, 0x18, 0x90, 0x8d, 0xa7, 0x77, 0x33, + 0xa3, 0x0f, 0x79, 0xfa, 0x05, 0x5b, 0x36, 0x66, 0x32, 0xbd, 0x8a, 0x6f, 0x79, 0x8c, 0x92, 0xb8, + 0x20, 0xa6, 0xf5, 0x47, 0x29, 0x2f, 0x67, 0x94, 0xa3, 0x0d, 0x1a, 0x36, 0x92, 0x2c, 0x55, 0xac, + 0x18, 0xf3, 0x0d, 0xb4, 0x4c, 0x7a, 0x6c, 0x2a, 0x89, 0xda, 0xfe, 0x5a, 0x53, 0xa9, 0x21, 0xee, + 0x2e, 0xf2, 0xa9, 0xd4, 0x4a, 0xcf, 0x36, 0x95, 0x09, 0x82, 0xfa, 0x54, 0xea, 0xdd, 0xcc, 0xe8, + 0x43, 0xf7, 0xa9, 0x34, 0x93, 0x39, 0xf3, 0x54, 0x26, 0x6e, 0xe7, 0x69, 0xfd, 0x31, 0x4d, 0x65, + 0x12, 0x9e, 0x4d, 0x65, 0xb2, 0x34, 0x61, 0x90, 0xe6, 0x4c, 0x65, 0x12, 0xf3, 0x0b, 0x4a, 0x8f, + 0x5d, 0xff, 0x3b, 0xd3, 0x64, 0x8a, 0x67, 0x98, 0x12, 0xa8, 0xbb, 0x0f, 0xd0, 0x1e, 0xf5, 0x86, + 0x24, 0xca, 0x7b, 0x9b, 0xd0, 0x85, 0x2c, 0xa2, 0x74, 0x4a, 0xd7, 0x84, 0x9e, 0x95, 0xec, 0x6e, + 0x66, 0x5f, 0xf2, 0xe6, 0x83, 0x4d, 0x6b, 0x92, 0xd4, 0x59, 0x27, 0xf6, 0xa9, 0x10, 0x9a, 0xa9, + 0x1b, 0x94, 0x89, 0x5e, 0xa9, 0x93, 0x9b, 0x59, 0x83, 0xb6, 0xa9, 0xaf, 0x27, 0x5d, 0xae, 0xf8, + 0x89, 0xb2, 0xae, 0x6a, 0x76, 0xa5, 0x9a, 0xba, 0x92, 0xa9, 0x52, 0xcd, 0xba, 0xaf, 0x29, 0xa9, + 0xa6, 0xb1, 0x3f, 0xa7, 0xb6, 0x33, 0x8f, 0x3e, 0xf7, 0x9e, 0xf9, 0xd9, 0x8a, 0xce, 0xb4, 0x16, + 0xce, 0x40, 0x60, 0x69, 0x14, 0xc9, 0xa7, 0xdc, 0xc3, 0x2f, 0x0a, 0x33, 0x99, 0x6f, 0xc2, 0x47, + 0x9f, 0x43, 0x89, 0x6f, 0xf0, 0x98, 0x80, 0x09, 0x30, 0x73, 0xea, 0x6a, 0xc2, 0x64, 0xef, 0xa1, + 0x07, 0xbd, 0x98, 0xea, 0xbd, 0x70, 0x22, 0xdb, 0xae, 0x25, 0x22, 0x70, 0x3b, 0x20, 0x26, 0x67, + 0x2b, 0x6d, 0x8f, 0xea, 0x9d, 0x11, 0x87, 0xba, 0x3a, 0xf8, 0xee, 0x22, 0x5a, 0xa3, 0x9b, 0x59, + 0x2f, 0xce, 0x33, 0xd8, 0xcd, 0x64, 0xe8, 0x5e, 0x5b, 0x95, 0x61, 0xce, 0x7a, 0x9f, 0xb2, 0xda, + 0xce, 0xee, 0x94, 0x64, 0x51, 0x8f, 0xa3, 0xcb, 0x62, 0x11, 0xd3, 0xa8, 0x99, 0xf3, 0xa0, 0x1b, + 0x67, 0x92, 0x81, 0xd7, 0xe8, 0xbb, 0x30, 0x2a, 0x90, 0xbb, 0x33, 0x24, 0x89, 0x4d, 0x19, 0xb2, + 0x0c, 0x13, 0x5a, 0x54, 0xb9, 0x54, 0xea, 0x4c, 0xb1, 0xe6, 0x39, 0xf3, 0x3c, 0xa1, 0x45, 0x8f, + 0x4b, 0x2a, 0xa6, 0x98, 0xf2, 0x4c, 0x2a, 0xdf, 0x81, 0x31, 0xce, 0xd2, 0x5c, 0x6e, 0x64, 0x5b, + 0xeb, 0xb3, 0x4a, 0xcc, 0xe2, 0x71, 0xcb, 0x8d, 0x96, 0x7c, 0xef, 0x99, 0x7b, 0xd0, 0x95, 0x31, + 0x69, 0x94, 0xdd, 0x45, 0xb4, 0x4b, 0x13, 0x5c, 0x88, 0xb4, 0x23, 0x38, 0x7a, 0xe9, 0x07, 0xcf, + 0x5d, 0xef, 0xa0, 0x0b, 0xc9, 0x6b, 0x3a, 0xc9, 0x24, 0x1e, 0xa3, 0x5b, 0xcf, 0xa6, 0xdb, 0x15, + 0x3f, 0xc7, 0x5a, 0x5f, 0xa0, 0xc7, 0xfd, 0x67, 0xed, 0x71, 0xf6, 0xc9, 0xc5, 0xa5, 0x38, 0xda, + 0xcf, 0xc6, 0x4d, 0x3f, 0x68, 0x75, 0x27, 0x56, 0xd1, 0x63, 0xeb, 0x12, 0x68, 0xbb, 0x8b, 0x84, + 0x6a, 0x3d, 0x93, 0x6a, 0x37, 0xec, 0x9c, 0xaf, 0xd5, 0x65, 0x3a, 0xf6, 0x33, 0xf6, 0x36, 0x5f, + 0x6a, 0x91, 0x2f, 0xcd, 0x56, 0x80, 0x9f, 0xe1, 0x80, 0x86, 0x6c, 0x76, 0x0b, 0x56, 0xd4, 0xc1, + 0x77, 0x17, 0x09, 0x95, 0x7a, 0x8a, 0x4a, 0x16, 0x74, 0x9e, 0xa2, 0x46, 0x87, 0xd6, 0x63, 0x6f, + 0xb2, 0xc8, 0x7c, 0x48, 0x7d, 0xa6, 0x3b, 0x6b, 0x5d, 0x38, 0x22, 0x82, 0x88, 0x05, 0xe0, 0xee, + 0x7d, 0x82, 0x59, 0x57, 0x30, 0xd3, 0x10, 0x99, 0x6d, 0x7e, 0x57, 0x38, 0x47, 0xbb, 0x36, 0x9b, + 0x1d, 0xc4, 0x30, 0x2a, 0x93, 0x6f, 0x21, 0xc5, 0xad, 0xa0, 0xa5, 0x96, 0x2a, 0x4f, 0xa8, 0x21, + 0x8b, 0x21, 0xaa, 0x32, 0x2d, 0x5e, 0x4d, 0x42, 0xa5, 0x1c, 0xa7, 0x1a, 0xb3, 0x53, 0x25, 0x49, + 0x30, 0xb7, 0xc8, 0xba, 0xdf, 0x7c, 0xae, 0xba, 0x45, 0x94, 0xac, 0x46, 0x65, 0x3d, 0xe7, 0x10, + 0x17, 0xe2, 0x34, 0xf1, 0x90, 0x1a, 0xd7, 0xa1, 0xe6, 0x35, 0x52, 0xdd, 0x22, 0x7a, 0x06, 0x26, + 0xe9, 0x16, 0xa1, 0x0d, 0xea, 0x94, 0xbb, 0xbb, 0x45, 0x28, 0x92, 0xee, 0x16, 0x51, 0x3b, 0x9a, + 0x2d, 0x2e, 0x50, 0x3a, 0x05, 0x93, 0x54, 0xb8, 0x33, 0xb3, 0x33, 0xe5, 0x84, 0x6c, 0x4c, 0x1b, + 0xb2, 0xc6, 0x49, 0x77, 0x43, 0x76, 0x46, 0xb9, 0xb2, 0x1e, 0x7f, 0x70, 0xaf, 0x80, 0x36, 0xe0, + 0xe2, 0x23, 0x1c, 0x71, 0x01, 0x66, 0xe3, 0x30, 0x0a, 0x5c, 0x7a, 0x69, 0x2b, 0xfb, 0x0b, 0x27, + 0xf4, 0x6b, 0x03, 0xce, 0xee, 0x7b, 0x84, 0x5e, 0xdd, 0x4c, 0x2f, 0x17, 0x2f, 0xc7, 0xa3, 0xc4, + 0xdd, 0x8f, 0x67, 0xe9, 0x62, 0xf6, 0x12, 0x1f, 0x66, 0x87, 0xee, 0xd9, 0xa8, 0xa5, 0xf8, 0x4d, + 0x55, 0x6e, 0x31, 0xdc, 0x81, 0x21, 0x86, 0x94, 0xf9, 0x8d, 0x1c, 0x57, 0x71, 0xd0, 0x7d, 0x11, + 0xd9, 0x45, 0x50, 0xb4, 0xaa, 0xcc, 0x7e, 0xdd, 0x87, 0x51, 0x76, 0x96, 0xd0, 0x3b, 0xca, 0x27, + 0x22, 0xfe, 0x2b, 0xaf, 0x63, 0x59, 0xc8, 0x9f, 0xc3, 0x84, 0x7a, 0xe0, 0x7e, 0x76, 0x46, 0x7e, + 0x87, 0x9e, 0xe7, 0x08, 0xb7, 0x69, 0x36, 0xfe, 0x6c, 0xe2, 0x9d, 0x5d, 0xce, 0x52, 0x26, 0x20, + 0x65, 0xfa, 0xc5, 0xac, 0xee, 0x5f, 0x48, 0x61, 0xa3, 0x4f, 0xc4, 0xf5, 0x05, 0x89, 0x9c, 0x06, + 0xca, 0xe1, 0xd9, 0x24, 0x63, 0xf3, 0xeb, 0x20, 0x4b, 0x01, 0xdb, 0xb5, 0xdb, 0xbd, 0x9c, 0x3b, + 0x75, 0x67, 0x5d, 0x16, 0x95, 0x4d, 0xaa, 0x78, 0xa5, 0x5e, 0x80, 0xce, 0x26, 0x74, 0x35, 0xfb, + 0xd1, 0x68, 0x3a, 0x19, 0x8f, 0xa9, 0x61, 0x97, 0x4e, 0xa5, 0x99, 0x35, 0xbc, 0x9c, 0x47, 0xa8, + 0x63, 0x4b, 0x36, 0x4d, 0x2e, 0x07, 0x2d, 0xcf, 0x30, 0xe6, 0x17, 0xaa, 0xce, 0x85, 0xdc, 0x9a, + 0x88, 0x5b, 0xea, 0x7d, 0xb0, 0x39, 0x4a, 0x90, 0xe1, 0xa4, 0xab, 0xeb, 0x5c, 0x64, 0x91, 0xfb, + 0x25, 0xaa, 0xff, 0x99, 0xf3, 0xe7, 0x65, 0x12, 0xbb, 0xa5, 0x1c, 0x96, 0xe6, 0x67, 0xde, 0x7b, + 0x4e, 0xef, 0x85, 0x98, 0xdf, 0xc8, 0xbe, 0xd9, 0x85, 0x8a, 0xe0, 0xc4, 0xdb, 0x5d, 0xe1, 0xe4, + 0xb9, 0xc9, 0x65, 0xf6, 0x85, 0x35, 0xb7, 0xd7, 0xe5, 0xcd, 0x6f, 0xc3, 0x51, 0x56, 0x46, 0x72, + 0x3a, 0x41, 0x50, 0x0f, 0x0a, 0xcb, 0x1d, 0x43, 0x16, 0xfb, 0xbf, 0x80, 0x4a, 0x7c, 0x22, 0x7c, + 0xb6, 0x49, 0xc8, 0xd6, 0xe8, 0x51, 0x3a, 0x65, 0x1f, 0xca, 0x7b, 0xd1, 0xb4, 0x7c, 0x3d, 0x8b, + 0xc3, 0xa1, 0x12, 0x6a, 0xc0, 0x63, 0x59, 0x12, 0xaf, 0xc5, 0x67, 0xbd, 0x3b, 0x9f, 0xe3, 0xbb, + 0xe2, 0x17, 0x65, 0xce, 0x85, 0x50, 0x7a, 0xb6, 0xcf, 0x4e, 0x48, 0x1e, 0xd8, 0x26, 0x08, 0x59, + 0x39, 0xd3, 0x7b, 0x96, 0x78, 0x94, 0xe4, 0x54, 0x9c, 0x75, 0x42, 0x9d, 0xf8, 0x72, 0x48, 0x3a, + 0xaf, 0xa0, 0xd4, 0xe5, 0x32, 0x73, 0x1c, 0xca, 0xd9, 0xcd, 0x49, 0x4a, 0xb8, 0x44, 0xb6, 0x29, + 0x6b, 0x42, 0x4b, 0x6a, 0xb6, 0x64, 0xaf, 0xc7, 0x9e, 0x02, 0x43, 0xb6, 0xb3, 0x32, 0x88, 0x4a, + 0x7b, 0x1d, 0xd5, 0xa1, 0xcc, 0x96, 0x88, 0xe9, 0x02, 0xbe, 0x0c, 0xc4, 0x37, 0x55, 0xe6, 0x58, + 0x17, 0x75, 0x28, 0xb3, 0xe5, 0x72, 0x9e, 0x44, 0x1b, 0x34, 0x81, 0xad, 0x91, 0xe2, 0x5b, 0xca, + 0xb5, 0xc8, 0xec, 0x67, 0x0d, 0xca, 0xf9, 0x0d, 0xa3, 0x1f, 0xc0, 0xac, 0xf1, 0x5d, 0x03, 0x79, + 0xa6, 0x9e, 0xf7, 0xea, 0x41, 0x37, 0xe2, 0xcf, 0x61, 0x3e, 0x2b, 0x03, 0x59, 0x7c, 0x31, 0x20, + 0x3f, 0x2d, 0x9c, 0x94, 0xa9, 0x5d, 0x53, 0x99, 0x6d, 0xc0, 0x8c, 0x29, 0xab, 0x97, 0xdc, 0x1c, + 0x39, 0x29, 0xbf, 0x8c, 0xb7, 0x0f, 0xb6, 0x60, 0xd6, 0x98, 0x59, 0x4b, 0x72, 0x26, 0x2f, 0xef, + 0x96, 0x91, 0xe2, 0x97, 0x30, 0x97, 0x91, 0x46, 0x2a, 0x3e, 0xf8, 0xcb, 0x4d, 0x33, 0x95, 0x13, + 0x80, 0x50, 0xce, 0xce, 0x50, 0x24, 0xe3, 0x4e, 0xba, 0x26, 0x31, 0x2a, 0x1b, 0xd3, 0xb6, 0xa1, + 0x6d, 0xba, 0x08, 0x4d, 0x29, 0x8b, 0xd4, 0x45, 0x98, 0x93, 0xd2, 0x28, 0xe3, 0xd6, 0xc8, 0x5c, + 0x46, 0x96, 0xa2, 0x1c, 0xaa, 0x3d, 0xf4, 0x76, 0x43, 0xc8, 0x7f, 0x3d, 0x6d, 0x4d, 0x22, 0x96, + 0xd1, 0x98, 0xd3, 0xc6, 0xd8, 0x4f, 0xe5, 0xba, 0x73, 0xbb, 0x9d, 0xa3, 0x06, 0x21, 0xf5, 0xbe, + 0x33, 0x81, 0xa4, 0xbe, 0xf3, 0x09, 0x15, 0x37, 0x4f, 0xa2, 0xa6, 0x90, 0xa9, 0xe2, 0xf9, 0x31, + 0x8c, 0xd7, 0xd5, 0xc6, 0x0d, 0x8d, 0x64, 0x2e, 0x0a, 0x19, 0x9c, 0xdf, 0xbd, 0xef, 0x5d, 0x4f, + 0xe5, 0xaa, 0xed, 0x76, 0x4f, 0xa3, 0xc8, 0x3c, 0xa8, 0xd7, 0x9e, 0xda, 0x96, 0x92, 0xda, 0xf4, + 0x82, 0xbc, 0x3c, 0xa8, 0x37, 0xbf, 0xce, 0xdd, 0x60, 0x8f, 0x83, 0x26, 0x13, 0x04, 0x20, 0xab, + 0x7b, 0xf6, 0x0a, 0x19, 0xaa, 0x9a, 0x9b, 0x61, 0x60, 0x85, 0xce, 0x59, 0xfc, 0x12, 0x6a, 0x8e, + 0x91, 0x2f, 0x57, 0xa9, 0xe1, 0xc1, 0xd5, 0x27, 0xea, 0x4d, 0x77, 0xf6, 0x7e, 0x6a, 0x8e, 0x97, + 0x32, 0x79, 0xc3, 0x3d, 0xf1, 0xe0, 0xea, 0x63, 0x28, 0x25, 0x1f, 0x7f, 0x91, 0x2e, 0xa4, 0x8c, + 0x57, 0x61, 0x72, 0x96, 0x05, 0xc4, 0x4f, 0xbc, 0x48, 0x47, 0x4d, 0xea, 0xd5, 0x97, 0xf2, 0x25, + 0x43, 0x8d, 0x54, 0xb1, 0xc6, 0xd5, 0x07, 0x61, 0x64, 0x08, 0x8b, 0xe1, 0x95, 0x98, 0xf2, 0x65, + 0x63, 0x1d, 0x27, 0x14, 0xb1, 0x64, 0x29, 0x19, 0x99, 0x7c, 0xa5, 0xda, 0xda, 0x3d, 0xe3, 0x70, + 0xf9, 0x76, 0x2f, 0xa0, 0xbc, 0x55, 0x2c, 0xdf, 0x6f, 0x4d, 0x43, 0xa1, 0xb7, 0x0d, 0x31, 0xc7, + 0xa6, 0x24, 0xb9, 0xe5, 0x6e, 0x49, 0x84, 0xd1, 0x1e, 0x2c, 0x24, 0x62, 0xa2, 0xf5, 0x96, 0xba, + 0x11, 0xc8, 0x9c, 0xc1, 0x3d, 0x58, 0xe0, 0x97, 0xb4, 0xcf, 0x99, 0xf0, 0x3e, 0x2c, 0xe4, 0xa5, + 0x07, 0x46, 0xb7, 0xcd, 0x71, 0xcf, 0x46, 0xf6, 0x64, 0xeb, 0xb0, 0xd7, 0xd2, 0xf1, 0xcf, 0x89, + 0x79, 0x3f, 0xab, 0x7c, 0x79, 0x0a, 0x93, 0x7a, 0x6a, 0x60, 0xa4, 0xca, 0x90, 0x54, 0xa2, 0xe2, + 0xf2, 0x95, 0x8c, 0x5a, 0xbe, 0x3e, 0x3e, 0xa3, 0x12, 0x3f, 0x4e, 0x37, 0x54, 0xd6, 0x2f, 0xc6, + 0xa9, 0xa9, 0x77, 0xcb, 0x86, 0x2c, 0x46, 0xe8, 0x3b, 0x30, 0x15, 0xdf, 0x8f, 0x63, 0x24, 0x0c, + 0x60, 0x39, 0x8e, 0xa3, 0xa9, 0xf8, 0xa6, 0xdc, 0xd9, 0xd1, 0x57, 0x85, 0xd8, 0x8f, 0xd1, 0xaf, + 0xa4, 0x42, 0xc1, 0xb5, 0x31, 0xf4, 0x22, 0xfd, 0x15, 0xde, 0x9e, 0x75, 0x76, 0x9a, 0x74, 0xbb, + 0x99, 0x5f, 0x3a, 0x52, 0xb7, 0x5b, 0xee, 0x6b, 0x4c, 0x52, 0xd5, 0xcc, 0xa0, 0xf3, 0x14, 0x6e, + 0xd0, 0xf7, 0x02, 0xb6, 0xb0, 0xd7, 0x72, 0xbd, 0x03, 0x33, 0x54, 0x76, 0xdf, 0x93, 0xaf, 0x0c, + 0xb4, 0xe1, 0x7a, 0xd7, 0xa7, 0x9e, 0xd0, 0x5d, 0xed, 0xc5, 0x84, 0xee, 0x8f, 0x42, 0xe5, 0x5d, + 0xbf, 0x30, 0xbd, 0x98, 0x24, 0xbf, 0x69, 0x39, 0x8f, 0x37, 0xc9, 0x6f, 0x5a, 0xee, 0x93, 0x4b, + 0x5f, 0xd2, 0xb7, 0xa2, 0xf9, 0xb7, 0x85, 0xbe, 0x01, 0x82, 0x3d, 0xf6, 0x06, 0x64, 0xee, 0x31, + 0xc8, 0x75, 0xfd, 0xf0, 0x2f, 0x85, 0x48, 0xed, 0x87, 0xab, 0xdc, 0xea, 0xc9, 0x22, 0xde, 0x9d, + 0x48, 0x16, 0x5f, 0x6a, 0xcb, 0x3f, 0xf9, 0xcb, 0xab, 0x85, 0x9f, 0xfc, 0xf4, 0x6a, 0xe1, 0x3f, + 0xfe, 0xf4, 0x6a, 0xe1, 0xbf, 0xfc, 0xf4, 0x6a, 0xe1, 0xfb, 0x8b, 0xbd, 0xbd, 0x1b, 0xd8, 0x6c, + 0xbb, 0xd8, 0x8b, 0xee, 0x32, 0x72, 0x43, 0xf4, 0xbf, 0x07, 0xff, 0x2f, 0x00, 0x00, 0xff, 0xff, + 0xe5, 0x1d, 0x2d, 0x86, 0x2a, 0xc9, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -15044,6 +15249,8 @@ type AuthServiceClient interface { DeleteAllInstallers(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) // ListResources retrieves a paginated list of resources. ListResources(ctx context.Context, in *ListResourcesRequest, opts ...grpc.CallOption) (*ListResourcesResponse, error) + // ListUnifiedResources retrieves a paginated list of all resource types displayable in the UI. + ListUnifiedResources(ctx context.Context, in *ListUnifiedResourcesRequest, opts ...grpc.CallOption) (*ListUnifiedResourcesResponse, error) // GetDomainName returns local auth domain of the current auth server GetDomainName(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetDomainNameResponse, error) // GetClusterCACert returns the PEM-encoded TLS certs for the local cluster @@ -17443,6 +17650,15 @@ func (c *authServiceClient) ListResources(ctx context.Context, in *ListResources return out, nil } +func (c *authServiceClient) ListUnifiedResources(ctx context.Context, in *ListUnifiedResourcesRequest, opts ...grpc.CallOption) (*ListUnifiedResourcesResponse, error) { + out := new(ListUnifiedResourcesResponse) + err := c.cc.Invoke(ctx, "/proto.AuthService/ListUnifiedResources", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *authServiceClient) GetDomainName(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetDomainNameResponse, error) { out := new(GetDomainNameResponse) err := c.cc.Invoke(ctx, "/proto.AuthService/GetDomainName", in, out, opts...) @@ -18193,6 +18409,8 @@ type AuthServiceServer interface { DeleteAllInstallers(context.Context, *emptypb.Empty) (*emptypb.Empty, error) // ListResources retrieves a paginated list of resources. ListResources(context.Context, *ListResourcesRequest) (*ListResourcesResponse, error) + // ListUnifiedResources retrieves a paginated list of all resource types displayable in the UI. + ListUnifiedResources(context.Context, *ListUnifiedResourcesRequest) (*ListUnifiedResourcesResponse, error) // GetDomainName returns local auth domain of the current auth server GetDomainName(context.Context, *emptypb.Empty) (*GetDomainNameResponse, error) // GetClusterCACert returns the PEM-encoded TLS certs for the local cluster @@ -18899,6 +19117,9 @@ func (*UnimplementedAuthServiceServer) DeleteAllInstallers(ctx context.Context, func (*UnimplementedAuthServiceServer) ListResources(ctx context.Context, req *ListResourcesRequest) (*ListResourcesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListResources not implemented") } +func (*UnimplementedAuthServiceServer) ListUnifiedResources(ctx context.Context, req *ListUnifiedResourcesRequest) (*ListUnifiedResourcesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListUnifiedResources not implemented") +} func (*UnimplementedAuthServiceServer) GetDomainName(ctx context.Context, req *emptypb.Empty) (*GetDomainNameResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetDomainName not implemented") } @@ -22965,6 +23186,24 @@ func _AuthService_ListResources_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _AuthService_ListUnifiedResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListUnifiedResourcesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).ListUnifiedResources(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.AuthService/ListUnifiedResources", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).ListUnifiedResources(ctx, req.(*ListUnifiedResourcesRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AuthService_GetDomainName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { @@ -24186,6 +24425,10 @@ var _AuthService_serviceDesc = grpc.ServiceDesc{ MethodName: "ListResources", Handler: _AuthService_ListResources_Handler, }, + { + MethodName: "ListUnifiedResources", + Handler: _AuthService_ListUnifiedResources_Handler, + }, { MethodName: "GetDomainName", Handler: _AuthService_GetDomainName_Handler, @@ -32127,6 +32370,177 @@ func (m *PaginatedResource_AppServerOrSAMLIdPServiceProvider) MarshalToSizedBuff } return len(dAtA) - i, nil } +func (m *ListUnifiedResourcesRequest) 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 *ListUnifiedResourcesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListUnifiedResourcesRequest) 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.UsePreviewAsRoles { + i-- + if m.UsePreviewAsRoles { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + } + if m.UseSearchAsRoles { + i-- + if m.UseSearchAsRoles { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + { + size, err := m.WindowsDesktopFilter.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthservice(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + { + size, err := m.SortBy.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthservice(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + if len(m.SearchKeywords) > 0 { + for iNdEx := len(m.SearchKeywords) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SearchKeywords[iNdEx]) + copy(dAtA[i:], m.SearchKeywords[iNdEx]) + i = encodeVarintAuthservice(dAtA, i, uint64(len(m.SearchKeywords[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if len(m.PredicateExpression) > 0 { + i -= len(m.PredicateExpression) + copy(dAtA[i:], m.PredicateExpression) + i = encodeVarintAuthservice(dAtA, i, uint64(len(m.PredicateExpression))) + i-- + dAtA[i] = 0x2a + } + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintAuthservice(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintAuthservice(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintAuthservice(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.StartKey) > 0 { + i -= len(m.StartKey) + copy(dAtA[i:], m.StartKey) + i = encodeVarintAuthservice(dAtA, i, uint64(len(m.StartKey))) + i-- + dAtA[i] = 0x1a + } + if m.Limit != 0 { + i = encodeVarintAuthservice(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x10 + } + if len(m.Kinds) > 0 { + for iNdEx := len(m.Kinds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Kinds[iNdEx]) + copy(dAtA[i:], m.Kinds[iNdEx]) + i = encodeVarintAuthservice(dAtA, i, uint64(len(m.Kinds[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ListUnifiedResourcesResponse) 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 *ListUnifiedResourcesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListUnifiedResourcesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.NextKey) > 0 { + i -= len(m.NextKey) + copy(dAtA[i:], m.NextKey) + i = encodeVarintAuthservice(dAtA, i, uint64(len(m.NextKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.Resources) > 0 { + for iNdEx := len(m.Resources) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Resources[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuthservice(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *ListResourcesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -32555,12 +32969,12 @@ func (m *SessionTrackerUpdateExpiry) MarshalToSizedBuffer(dAtA []byte) (int, err copy(dAtA[i:], m.XXX_unrecognized) } if m.Expires != nil { - n92, err92 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) - if err92 != nil { - return 0, err92 + n94, err94 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expires, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expires):]) + if err94 != nil { + return 0, err94 } - i -= n92 - i = encodeVarintAuthservice(dAtA, i, uint64(n92)) + i -= n94 + i = encodeVarintAuthservice(dAtA, i, uint64(n94)) i-- dAtA[i] = 0xa } @@ -38309,6 +38723,81 @@ func (m *PaginatedResource_AppServerOrSAMLIdPServiceProvider) Size() (n int) { } return n } +func (m *ListUnifiedResourcesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Kinds) > 0 { + for _, s := range m.Kinds { + l = len(s) + n += 1 + l + sovAuthservice(uint64(l)) + } + } + if m.Limit != 0 { + n += 1 + sovAuthservice(uint64(m.Limit)) + } + l = len(m.StartKey) + if l > 0 { + n += 1 + l + sovAuthservice(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovAuthservice(uint64(len(k))) + 1 + len(v) + sovAuthservice(uint64(len(v))) + n += mapEntrySize + 1 + sovAuthservice(uint64(mapEntrySize)) + } + } + l = len(m.PredicateExpression) + if l > 0 { + n += 1 + l + sovAuthservice(uint64(l)) + } + if len(m.SearchKeywords) > 0 { + for _, s := range m.SearchKeywords { + l = len(s) + n += 1 + l + sovAuthservice(uint64(l)) + } + } + l = m.SortBy.Size() + n += 1 + l + sovAuthservice(uint64(l)) + l = m.WindowsDesktopFilter.Size() + n += 1 + l + sovAuthservice(uint64(l)) + if m.UseSearchAsRoles { + n += 2 + } + if m.UsePreviewAsRoles { + n += 2 + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ListUnifiedResourcesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Resources) > 0 { + for _, e := range m.Resources { + l = e.Size() + n += 1 + l + sovAuthservice(uint64(l)) + } + } + l = len(m.NextKey) + if l > 0 { + n += 1 + l + sovAuthservice(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *ListResourcesRequest) Size() (n int) { if m == nil { return 0 @@ -58179,6 +58668,554 @@ func (m *PaginatedResource) Unmarshal(dAtA []byte) error { } return nil } +func (m *ListUnifiedResourcesRequest) 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 ErrIntOverflowAuthservice + } + 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: ListUnifiedResourcesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListUnifiedResourcesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kinds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + 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 ErrInvalidLengthAuthservice + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Kinds = append(m.Kinds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartKey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + 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 ErrInvalidLengthAuthservice + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StartKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthAuthservice + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthAuthservice + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthAuthservice + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthAuthservice + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipAuthservice(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthservice + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PredicateExpression", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + 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 ErrInvalidLengthAuthservice + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PredicateExpression = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SearchKeywords", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + 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 ErrInvalidLengthAuthservice + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SearchKeywords = append(m.SearchKeywords, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SortBy", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SortBy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WindowsDesktopFilter", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.WindowsDesktopFilter.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseSearchAsRoles", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.UseSearchAsRoles = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UsePreviewAsRoles", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.UsePreviewAsRoles = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipAuthservice(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthservice + } + 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 *ListUnifiedResourcesResponse) 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 ErrIntOverflowAuthservice + } + 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: ListUnifiedResourcesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListUnifiedResourcesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuthservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Resources = append(m.Resources, &PaginatedResource{}) + if err := m.Resources[len(m.Resources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextKey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthservice + } + 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 ErrInvalidLengthAuthservice + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthservice(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthservice + } + 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 *ListResourcesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/api/client/proto/types.go b/api/client/proto/types.go index 142c44bac5bde..9f41c4210564c 100644 --- a/api/client/proto/types.go +++ b/api/client/proto/types.go @@ -65,6 +65,20 @@ func (req *ListResourcesRequest) CheckAndSetDefaults() error { return nil } +// CheckAndSetDefaults checks and sets default values. +func (req *ListUnifiedResourcesRequest) CheckAndSetDefaults() error { + // If the Limit parameter was not provided instead of returning an error fallback to the default limit. + if req.Limit == 0 { + req.Limit = apidefaults.DefaultChunkSize + } + + if req.Limit < 0 { + return trace.BadParameter("negative parameter: limit") + } + + return nil +} + // RequiresFakePagination checks if we need to fallback to GetXXX calls // that retrieves entire resources upfront rather than working with subsets. func (req *ListResourcesRequest) RequiresFakePagination() bool { diff --git a/api/proto/teleport/legacy/client/proto/authservice.proto b/api/proto/teleport/legacy/client/proto/authservice.proto index 35457927e30c8..46420299201b9 100644 --- a/api/proto/teleport/legacy/client/proto/authservice.proto +++ b/api/proto/teleport/legacy/client/proto/authservice.proto @@ -1731,6 +1731,51 @@ message PaginatedResource { reserved "KubeService"; } +// ListUnifiedResourcesRequest is a request to receive a paginated list of unified resources +message ListUnifiedResourcesRequest { + // Kinds is a list of kinds to match against a resource's kind. This can be used in a + // unified resource request that can include multiple types. + repeated string Kinds = 1 [(gogoproto.jsontag) = "kinds,omitempty"]; + // Limit is the maximum amount of resources to retrieve. + int32 Limit = 2 [(gogoproto.jsontag) = "limit,omitempty"]; + // StartKey is used to start listing resources from a specific spot. It + // should be set to the previous NextKey value if using pagination, or + // left empty. + string StartKey = 3 [(gogoproto.jsontag) = "start_key,omitempty"]; + // Labels is a label-based matcher if non-empty. + map Labels = 4 [(gogoproto.jsontag) = "labels,omitempty"]; + // PredicateExpression defines boolean conditions that will be matched against the resource. + string PredicateExpression = 5 [(gogoproto.jsontag) = "predicate_expression,omitempty"]; + // SearchKeywords is a list of search keywords to match against resource field values. + repeated string SearchKeywords = 6 [(gogoproto.jsontag) = "search_keywords,omitempty"]; + // SortBy describes which resource field and which direction to sort by. + types.SortBy SortBy = 7 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "sort_by,omitempty" + ]; + // WindowsDesktopFilter specifies windows desktop specific filters. + types.WindowsDesktopFilter WindowsDesktopFilter = 8 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "windows_desktop_filter,omitempty" + ]; + // UseSearchAsRoles indicates that the response should include all resources + // the caller is able to request access to using search_as_roles + bool UseSearchAsRoles = 9 [(gogoproto.jsontag) = "use_search_as_roles,omitempty"]; + // UsePreviewAsRoles indicates that the response should include all resources + // the caller would be able to access with their preview_as_roles + bool UsePreviewAsRoles = 10 [(gogoproto.jsontag) = "use_preview_as_roles,omitempty"]; +} + +// ListUnifiedResourceResponse response of ListUnifiedResources. +message ListUnifiedResourcesResponse { + // Resources is a list of resource. + repeated PaginatedResource Resources = 1 [(gogoproto.jsontag) = "resources,omitempty"]; + // NextKey is the next Key to use as StartKey in a ListResourcesRequest to + // continue retrieving pages of resource. If NextKey is empty, there are no + // more pages. + string NextKey = 2 [(gogoproto.jsontag) = "next_key,omitempty"]; +} + // ListResourcesRequest defines a request to retrieve resources paginated. Only // one type of resource can be retrieved per request. // @@ -2811,6 +2856,9 @@ service AuthService { // ListResources retrieves a paginated list of resources. rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse); + // ListUnifiedResources retrieves a paginated list of all resource types displayable in the UI. + rpc ListUnifiedResources(ListUnifiedResourcesRequest) returns (ListUnifiedResourcesResponse); + // GetDomainName returns local auth domain of the current auth server rpc GetDomainName(google.protobuf.Empty) returns (GetDomainNameResponse); // GetClusterCACert returns the PEM-encoded TLS certs for the local cluster diff --git a/api/types/appserver.go b/api/types/appserver.go index b12ada614999c..42fda579a9e3d 100644 --- a/api/types/appserver.go +++ b/api/types/appserver.go @@ -47,6 +47,9 @@ type AppServer interface { String() string // Copy returns a copy of this app server object. Copy() AppServer + + // CloneResource returns a copy of the AppServer as a ResourceWithLabels + CloneResource() ResourceWithLabels // GetApp returns the app this app server proxies. GetApp() Application // SetApp sets the app this app server proxies. @@ -292,6 +295,10 @@ func (s *AppServerV3) Copy() AppServer { return utils.CloneProtoMsg(s) } +func (s *AppServerV3) CloneResource() ResourceWithLabels { + return s.Copy() +} + // MatchSearch goes through select field values and tries to // match against the list of search values. func (s *AppServerV3) MatchSearch(values []string) bool { diff --git a/api/types/constants.go b/api/types/constants.go index 5c782db857496..4d5227398d07c 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -148,6 +148,10 @@ const ( // It uses the private key created above to SSH into the host. SubKindOpenSSHEICENode = "openssh-ec2-ice" + // KindUnifiedResource is a meta Kind that is used for the unified resource search present on + // the webUI and Connect. It allows us to query and return multiple kinds at the same time + KindUnifiedResource = "unified_resource" + // KindAppServer is an application server resource. KindAppServer = "app_server" diff --git a/api/types/databaseserver.go b/api/types/databaseserver.go index 93393e4ac4d29..fb392b3bfba10 100644 --- a/api/types/databaseserver.go +++ b/api/types/databaseserver.go @@ -48,6 +48,9 @@ type DatabaseServer interface { String() string // Copy returns a copy of this database server object. Copy() DatabaseServer + + // CloneResource returns a copy of the DatabaseServer as a ResourceWithLabels + CloneResource() ResourceWithLabels // GetDatabase returns the database this database server proxies. GetDatabase() Database // SetDatabase sets the database this database server proxies. @@ -268,6 +271,11 @@ func (s *DatabaseServerV3) Copy() DatabaseServer { return utils.CloneProtoMsg(s) } +// CloneResource returns a copy of this database server object. +func (s *DatabaseServerV3) CloneResource() ResourceWithLabels { + return s.Copy() +} + // MatchSearch goes through select field values and tries to // match against the list of search values. func (s *DatabaseServerV3) MatchSearch(values []string) bool { diff --git a/api/types/desktop.go b/api/types/desktop.go index ace944f72b2c2..a0e25839ce956 100644 --- a/api/types/desktop.go +++ b/api/types/desktop.go @@ -131,6 +131,10 @@ type WindowsDesktop interface { // NonAD checks whether this is a standalone host that // is not joined to an Active Directory domain. NonAD() bool + // Copy returns a copy of this windows desktop + Copy() *WindowsDesktopV3 + // CloneResource returns a copy of the WindowDesktop as a ResourceWithLabels + CloneResource() ResourceWithLabels } var _ WindowsDesktop = &WindowsDesktopV3{} @@ -197,6 +201,15 @@ func (d *WindowsDesktopV3) MatchSearch(values []string) bool { return MatchSearch(fieldVals, values, nil) } +// Copy returns a copy of this windows desktop object. +func (d *WindowsDesktopV3) Copy() *WindowsDesktopV3 { + return utils.CloneProtoMsg(d) +} + +func (d *WindowsDesktopV3) CloneResource() ResourceWithLabels { + return d.Copy() +} + // DeduplicateDesktops deduplicates desktops by name. func DeduplicateDesktops(desktops []WindowsDesktop) (result []WindowsDesktop) { seen := make(map[string]struct{}) diff --git a/api/types/kubernetes_server.go b/api/types/kubernetes_server.go index 393684ddc3447..c9fef193b1a18 100644 --- a/api/types/kubernetes_server.go +++ b/api/types/kubernetes_server.go @@ -47,6 +47,8 @@ type KubeServer interface { String() string // Copy returns a copy of this kube server object. Copy() KubeServer + // CloneResource returns a copy of the KubeServer as a ResourceWithLabels + CloneResource() ResourceWithLabels // GetCluster returns the Kubernetes Cluster this kube server proxies. GetCluster() KubeCluster // SetCluster sets the kube cluster this kube server server proxies. @@ -282,6 +284,11 @@ func (s *KubernetesServerV3) Copy() KubeServer { return utils.CloneProtoMsg(s) } +// CloneResource returns a copy of this kube server object. +func (s *KubernetesServerV3) CloneResource() ResourceWithLabels { + return s.Copy() +} + // MatchSearch goes through select field values and tries to // match against the list of search values. func (s *KubernetesServerV3) MatchSearch(values []string) bool { diff --git a/api/types/resource.go b/api/types/resource.go index d8b460d51e971..4c67413894ea8 100644 --- a/api/types/resource.go +++ b/api/types/resource.go @@ -469,6 +469,17 @@ func MatchLabels(resource ResourceWithLabels, labels map[string]string) bool { return true } +// MatchKinds takes an array of strings that represent a Kind and +// returns true if the resource's kind matches any item in the given array. +func MatchKinds(resource ResourceWithLabels, kinds []string) bool { + if len(kinds) == 0 { + return true + } + resourceKind := resource.GetKind() + + return slices.Contains(kinds, resourceKind) +} + // IsValidLabelKey checks if the supplied string matches the // label key regexp. func IsValidLabelKey(s string) bool { diff --git a/api/types/saml_idp_service_provider.go b/api/types/saml_idp_service_provider.go index 9b8ee9dcbad49..ddb550e950858 100644 --- a/api/types/saml_idp_service_provider.go +++ b/api/types/saml_idp_service_provider.go @@ -40,6 +40,11 @@ type SAMLIdPServiceProvider interface { GetEntityID() string // SetEntityID sets the entity ID. SetEntityID(string) + // Copy returns a copy of this saml idp service provider object. + Copy() SAMLIdPServiceProvider + // CloneResource returns a copy of the SAMLIdPServiceProvider as a ResourceWithLabels + // This is helpful when interfacing with multiple types at the same time in unified resources + CloneResource() ResourceWithLabels } // NewSAMLIdPServiceProvider returns a new SAMLIdPServiceProvider based off a metadata object and SAMLIdPServiceProviderSpecV1. @@ -83,6 +88,14 @@ func (s *SAMLIdPServiceProviderV1) String() string { s.GetName()) } +func (s *SAMLIdPServiceProviderV1) Copy() SAMLIdPServiceProvider { + return utils.CloneProtoMsg(s) +} + +func (s *SAMLIdPServiceProviderV1) CloneResource() ResourceWithLabels { + return s.Copy() +} + // MatchSearch goes through select field values and tries to // match against the list of search values. func (s *SAMLIdPServiceProviderV1) MatchSearch(values []string) bool { diff --git a/api/types/server.go b/api/types/server.go index ae8311100230b..ad0430061639b 100644 --- a/api/types/server.go +++ b/api/types/server.go @@ -77,6 +77,10 @@ type Server interface { // DeepCopy creates a clone of this server value DeepCopy() Server + // CloneResource is used to return a clone of the Server and match the CloneAny interface + // This is helpful when interfacing with multiple types at the same time in unified resources + CloneResource() ResourceWithLabels + // GetCloudMetadata gets the cloud metadata for the server. GetCloudMetadata() *CloudMetadata // SetCloudMetadata sets the server's cloud metadata. @@ -497,6 +501,11 @@ func (s *ServerV2) DeepCopy() Server { return utils.CloneProtoMsg(s) } +// CloneResource creates a clone of this server value +func (s *ServerV2) CloneResource() ResourceWithLabels { + return s.DeepCopy() +} + // GetCloudMetadata gets the cloud metadata for the server. func (s *ServerV2) GetCloudMetadata() *CloudMetadata { return s.Spec.CloudMetadata diff --git a/constants.go b/constants.go index 357cb09b6e980..0661675df3c9d 100644 --- a/constants.go +++ b/constants.go @@ -205,6 +205,10 @@ const ( // ComponentWeb is a web server ComponentWeb = "web" + // ComponentUnifiedResource is a cache of resources meant to be listed and displayed + // together in the web UI + ComponentUnifiedResource = "unified_resource" + // ComponentWebsocket is websocket server that the web client connects to. ComponentWebsocket = "websocket" diff --git a/lib/auth/auth.go b/lib/auth/auth.go index 9cf468c6a4cc6..804919d252322 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -645,6 +645,10 @@ type Server struct { // lockWatcher is a lock watcher, used to verify cert generation requests. lockWatcher *services.LockWatcher + // UnifiedResourceCache is a cache of multiple resource kinds to be presented + // in a unified manner in the web UI. + UnifiedResourceCache *services.UnifiedResourceCache + inventory *inventory.Controller // githubOrgSSOCache is used to cache whether Github organizations use @@ -799,7 +803,13 @@ func (a *Server) CloseContext() context.Context { return a.closeCtx } -// SetLockWatcher sets the lock watcher. +// SetUnifiedResourcesCache sets the unified resource cache. +func (a *Server) SetUnifiedResourcesCache(unifiedResourcesCache *services.UnifiedResourceCache) { + a.lock.Lock() + defer a.lock.Unlock() + a.UnifiedResourceCache = unifiedResourcesCache +} + func (a *Server) SetLockWatcher(lockWatcher *services.LockWatcher) { a.lock.Lock() defer a.lock.Unlock() @@ -1361,6 +1371,7 @@ func (a *Server) Close() error { errs = append(errs, err) } } + return trace.NewAggregate(errs...) } diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 9e8e6bba30c1b..34a346e47d29b 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "net/url" + "sort" "strings" "time" @@ -1490,6 +1491,280 @@ func (a *ServerWithRoles) GetNode(ctx context.Context, namespace, name string) ( return node, nil } +func stringCompare(a string, b string, isDesc bool) bool { + if isDesc { + return a > b + } + return a < b +} + +func unifiedNameCompare(a types.ResourceWithLabels, b types.ResourceWithLabels, isDesc bool) bool { + var nameA, nameB string + resourceA, ok := a.(types.Server) + if ok { + nameA = resourceA.GetHostname() + } else { + nameA = a.GetName() + } + + resourceB, ok := b.(types.Server) + if ok { + nameB = resourceB.GetHostname() + } else { + nameB = b.GetName() + } + + return stringCompare(nameA, nameB, isDesc) +} + +// MakePaginatedResources converts a list of ResourceWithLabels to a PaginatedResource used in +// grpc responses. +func (s *ServerWithRoles) MakePaginatedResources(requestType string, resources []types.ResourceWithLabels) ([]*proto.PaginatedResource, error) { + paginatedResources := make([]*proto.PaginatedResource, 0, len(resources)) + for _, resource := range resources { + var protoResource *proto.PaginatedResource + resourceKind := requestType + if requestType == types.KindUnifiedResource { + resourceKind = resource.GetKind() + } + switch resourceKind { + case types.KindDatabaseServer: + database, ok := resource.(*types.DatabaseServerV3) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_DatabaseServer{DatabaseServer: database}} + case types.KindDatabaseService: + databaseService, ok := resource.(*types.DatabaseServiceV1) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_DatabaseService{DatabaseService: databaseService}} + case types.KindAppServer: + app, ok := resource.(*types.AppServerV3) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_AppServer{AppServer: app}} + case types.KindNode: + srv, ok := resource.(*types.ServerV2) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_Node{Node: srv}} + case types.KindKubeServer: + srv, ok := resource.(*types.KubernetesServerV3) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_KubernetesServer{KubernetesServer: srv}} + case types.KindWindowsDesktop: + desktop, ok := resource.(*types.WindowsDesktopV3) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_WindowsDesktop{WindowsDesktop: desktop}} + case types.KindWindowsDesktopService: + desktopService, ok := resource.(*types.WindowsDesktopServiceV3) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_WindowsDesktopService{WindowsDesktopService: desktopService}} + case types.KindKubernetesCluster: + cluster, ok := resource.(*types.KubernetesClusterV3) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_KubeCluster{KubeCluster: cluster}} + case types.KindUserGroup: + userGroup, ok := resource.(*types.UserGroupV1) + if !ok { + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_UserGroup{UserGroup: userGroup}} + case types.KindSAMLIdPServiceProvider, types.KindAppOrSAMLIdPServiceProvider: + switch appOrSP := resource.(type) { + case *types.AppServerV3: + protoResource = &proto.PaginatedResource{ + Resource: &proto.PaginatedResource_AppServerOrSAMLIdPServiceProvider{ + AppServerOrSAMLIdPServiceProvider: &types.AppServerOrSAMLIdPServiceProviderV1{ + Resource: &types.AppServerOrSAMLIdPServiceProviderV1_AppServer{ + AppServer: appOrSP, + }, + }, + }} + case *types.SAMLIdPServiceProviderV1: + protoResource = &proto.PaginatedResource{ + Resource: &proto.PaginatedResource_AppServerOrSAMLIdPServiceProvider{ + AppServerOrSAMLIdPServiceProvider: &types.AppServerOrSAMLIdPServiceProviderV1{ + Resource: &types.AppServerOrSAMLIdPServiceProviderV1_SAMLIdPServiceProvider{ + SAMLIdPServiceProvider: appOrSP, + }, + }, + }} + default: + return nil, trace.BadParameter("%s has invalid type %T", resourceKind, resource) + } + + default: + return nil, trace.NotImplemented("resource type %s doesn't support pagination", resource.GetKind()) + } + + paginatedResources = append(paginatedResources, protoResource) + } + return paginatedResources, nil +} + +// ListUnifiedResources returns a paginated list of unified resources filtered by user access. +func (a *ServerWithRoles) ListUnifiedResources(ctx context.Context, req *proto.ListUnifiedResourcesRequest) (*proto.ListUnifiedResourcesResponse, error) { + // Fetch full list of resources in the backend. + var ( + elapsedFetch time.Duration + elapsedFilter time.Duration + unifiedResources []types.ResourceWithLabels + filteredResources []types.ResourceWithLabels + ) + + defer func() { + log.WithFields(logrus.Fields{ + "user": a.context.User.GetName(), + "elapsed_fetch": elapsedFetch, + "elapsed_filter": elapsedFilter, + }).Debugf( + "ListUnifiedResources(%v->%v) in %v.", + len(unifiedResources), len(filteredResources), elapsedFetch+elapsedFilter) + }() + + startFetch := time.Now() + unifiedResources, err := a.authServer.UnifiedResourceCache.GetUnifiedResources(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + elapsedFetch = time.Since(startFetch) + + startFilter := time.Now() + for _, resource := range unifiedResources { + switch r := resource.(type) { + case types.Server: + { + if err := a.checkAccessToNode(r); err != nil { + if trace.IsAccessDenied(err) { + continue + } + + return nil, trace.Wrap(err) + } + + filteredResources = append(filteredResources, resource) + } + case types.DatabaseServer: + { + if err := a.checkAccessToDatabase(r.GetDatabase()); err != nil { + if trace.IsAccessDenied(err) { + continue + } + + return nil, trace.Wrap(err) + } + + filteredResources = append(filteredResources, resource) + } + + case types.AppServer: + { + if err := a.checkAccessToApp(r.GetApp()); err != nil { + if trace.IsAccessDenied(err) { + continue + } + + return nil, trace.Wrap(err) + } + + filteredResources = append(filteredResources, resource) + } + case types.SAMLIdPServiceProvider: + { + if err := a.action(apidefaults.Namespace, types.KindSAMLIdPServiceProvider, types.VerbList); err == nil { + filteredResources = append(filteredResources, resource) + } + } + case types.KubeServer: + kube := r.GetCluster() + if err := a.checkAccessToKubeCluster(kube); err != nil { + if trace.IsAccessDenied(err) { + continue + } + + return nil, trace.Wrap(err) + } + + filteredResources = append(filteredResources, kube) + case types.WindowsDesktop: + { + if err := a.checkAccessToWindowsDesktop(r); err != nil { + if trace.IsAccessDenied(err) { + continue + } + + return nil, trace.Wrap(err) + } + + filteredResources = append(filteredResources, resource) + } + } + } + elapsedFilter = time.Since(startFilter) + + if req.SortBy.Field != "" { + isDesc := req.SortBy.IsDesc + switch req.SortBy.Field { + case types.ResourceMetadataName: + sort.SliceStable(filteredResources, func(i, j int) bool { + return unifiedNameCompare(filteredResources[i], filteredResources[j], isDesc) + }) + case types.ResourceSpecType: + sort.SliceStable(filteredResources, func(i, j int) bool { + return stringCompare(filteredResources[i].GetKind(), filteredResources[j].GetKind(), isDesc) + }) + default: + return nil, trace.NotImplemented("sorting by field %q for unified resource %q is not supported", req.SortBy.Field, types.KindUnifiedResource) + } + } + + // Apply request filters and get pagination info. + resp, err := local.FakePaginate(filteredResources, local.FakePaginateParams{ + Limit: req.Limit, + Labels: req.Labels, + SearchKeywords: req.SearchKeywords, + PredicateExpression: req.PredicateExpression, + StartKey: req.StartKey, + Kinds: req.Kinds, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + paginatedResources, err := a.MakePaginatedResources(types.KindUnifiedResource, resp.Resources) + if err != nil { + return nil, trace.Wrap(err, "making paginated unified resources") + } + + return &proto.ListUnifiedResourcesResponse{ + NextKey: resp.NextKey, + Resources: paginatedResources, + }, nil +} + func (a *ServerWithRoles) GetNodes(ctx context.Context, namespace string) ([]types.Server, error) { if err := a.action(namespace, types.KindNode, types.VerbList); err != nil { return nil, trace.Wrap(err) @@ -1893,7 +2168,14 @@ func (a *ServerWithRoles) listResourcesWithSort(ctx context.Context, req proto.L } // Apply request filters and get pagination info. - resp, err := local.FakePaginate(resources, req) + resp, err := local.FakePaginate(resources, local.FakePaginateParams{ + ResourceType: req.ResourceType, + Limit: req.Limit, + Labels: req.Labels, + SearchKeywords: req.SearchKeywords, + PredicateExpression: req.PredicateExpression, + StartKey: req.StartKey, + }) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index 1e179aa28be9d..b5d8668461346 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -22,6 +22,7 @@ import ( "crypto/x509/pkix" "fmt" "io" + "strings" "testing" "time" @@ -4143,6 +4144,375 @@ func TestListResources_WithRoles(t *testing.T) { } } +// TestListUnifiedResources_KindsFilter will generate multiple resources +// and filter for only one kind. +func TestListUnifiedResources_KindsFilter(t *testing.T) { + t.Parallel() + ctx := context.Background() + srv := newTestTLSServer(t) + for i := 0; i < 5; i++ { + name := uuid.New().String() + node, err := types.NewServerWithLabels( + name, + types.KindNode, + types.ServerSpecV2{}, + map[string]string{"name": name}, + ) + require.NoError(t, err) + + _, err = srv.Auth().UpsertNode(ctx, node) + require.NoError(t, err) + db, err := types.NewDatabaseServerV3(types.Metadata{ + Name: name, + }, types.DatabaseServerSpecV3{ + HostID: "_", + Hostname: "_", + Database: &types.DatabaseV3{ + Metadata: types.Metadata{ + Name: fmt.Sprintf("name-%d", i), + }, + Spec: types.DatabaseSpecV3{ + Protocol: "_", + URI: "_", + }, + }, + }) + require.NoError(t, err) + _, err = srv.Auth().UpsertDatabaseServer(ctx, db) + require.NoError(t, err) + } + + // create user and client + user, _, err := CreateUserAndRole(srv.Auth(), "user", nil, nil) + require.NoError(t, err) + clt, err := srv.NewClient(TestUser(user.GetName())) + require.NoError(t, err) + resp, err := clt.ListUnifiedResources(ctx, &proto.ListUnifiedResourcesRequest{ + Kinds: []string{types.KindDatabase}, + Limit: 5, + }) + require.NoError(t, err) + require.Eventually(t, func() bool { + return len(resp.Resources) == 5 + }, time.Second, time.Second/10) + // Check that all resources are of type KindDatabaseServer + for _, resource := range resp.Resources { + r := resource.GetDatabaseServer() + require.Equal(t, types.KindDatabaseServer, r.GetKind()) + } +} + +// TestListUnifiedResources_WithSearch will generate multiple resources +// and filter by a search query +func TestListUnifiedResources_WithSearch(t *testing.T) { + t.Parallel() + ctx := context.Background() + srv := newTestTLSServer(t) + names := []string{"tifa", "cloud", "aerith", "baret", "cid", "tifa2"} + for i := 0; i < 6; i++ { + name := names[i] + node, err := types.NewServerWithLabels( + name, + types.KindNode, + types.ServerSpecV2{ + Hostname: name, + }, + map[string]string{"name": name}, + ) + require.NoError(t, err) + + _, err = srv.Auth().UpsertNode(ctx, node) + require.NoError(t, err) + } + testNodes, err := srv.Auth().GetNodes(ctx, apidefaults.Namespace) + require.NoError(t, err) + require.Len(t, testNodes, 6) + + // create user and client + user, _, err := CreateUserAndRole(srv.Auth(), "user", nil, nil) + require.NoError(t, err) + clt, err := srv.NewClient(TestUser(user.GetName())) + require.NoError(t, err) + resp, err := clt.ListUnifiedResources(ctx, &proto.ListUnifiedResourcesRequest{ + SearchKeywords: []string{"tifa"}, + Limit: 10, + }) + require.NoError(t, err) + require.Len(t, resp.Resources, 2) + require.Empty(t, resp.NextKey) + + // Check that our returned resource has the correct name + for _, resource := range resp.Resources { + r := resource.GetNode() + require.True(t, strings.Contains(r.GetHostname(), "tifa")) + } +} + +// TestListUnifiedResources_MixedAccess will generate multiple resources +// and only return the kinds the user has access to +func TestListUnifiedResources_MixedAccess(t *testing.T) { + t.Parallel() + ctx := context.Background() + srv := newTestTLSServer(t) + names := []string{"tifa", "cloud", "aerith", "baret", "cid", "tifa2"} + for i := 0; i < 6; i++ { + name := names[i] + + // add nodes + node, err := types.NewServerWithLabels( + name, + types.KindNode, + types.ServerSpecV2{ + Hostname: name, + }, + map[string]string{"name": "mylabel"}, + ) + require.NoError(t, err) + + _, err = srv.Auth().UpsertNode(ctx, node) + require.NoError(t, err) + + // add dbs + db, err := types.NewDatabaseServerV3(types.Metadata{ + Name: name, + }, types.DatabaseServerSpecV3{ + HostID: "_", + Hostname: "_", + Database: &types.DatabaseV3{ + Metadata: types.Metadata{ + Name: fmt.Sprintf("name-%d", i), + }, + Spec: types.DatabaseSpecV3{ + Protocol: "_", + URI: "_", + }, + }, + }) + require.NoError(t, err) + _, err = srv.Auth().UpsertDatabaseServer(ctx, db) + require.NoError(t, err) + + // add desktops + desktop, err := types.NewWindowsDesktopV3(name, map[string]string{"name": "mylabel"}, + types.WindowsDesktopSpecV3{Addr: "_", HostID: "_"}) + require.NoError(t, err) + require.NoError(t, srv.Auth().UpsertWindowsDesktop(ctx, desktop)) + } + testNodes, err := srv.Auth().GetNodes(ctx, apidefaults.Namespace) + require.NoError(t, err) + require.Len(t, testNodes, 6) + + testDbs, err := srv.Auth().GetDatabaseServers(ctx, apidefaults.Namespace) + require.NoError(t, err) + require.Len(t, testDbs, 6) + + testDesktops, err := srv.Auth().GetWindowsDesktops(ctx, types.WindowsDesktopFilter{}) + require.NoError(t, err) + require.Len(t, testDesktops, 6) + + // create user, role, and client + username := "user" + user, role, err := CreateUserAndRole(srv.Auth(), username, nil, nil) + // remove permission from nodes and desktops + role.SetNodeLabels(types.Deny, types.Labels{"name": {"mylabel"}}) + require.NoError(t, srv.Auth().UpsertRole(ctx, role)) + require.NoError(t, err) + identity := TestUser(user.GetName()) + clt, err := srv.NewClient(identity) + require.NoError(t, err) + + require.NoError(t, err) + resp, err := clt.ListUnifiedResources(ctx, &proto.ListUnifiedResourcesRequest{ + Limit: 10, + }) + require.NoError(t, err) + require.Len(t, resp.Resources, 6) + require.Empty(t, resp.NextKey) + + // only receive databases + for _, resource := range resp.Resources { + r := resource.GetDatabaseServer() + require.Equal(t, types.KindDatabaseServer, r.GetKind()) + } +} + +// TestListUnifiedResources_WithPredicate will return resources that match the +// predicate expression +func TestListUnifiedResources_WithPredicate(t *testing.T) { + t.Parallel() + ctx := context.Background() + srv := newTestTLSServer(t) + names := []string{"tifa", "cloud", "aerith", "baret", "cid", "tifa2"} + for i := 0; i < 6; i++ { + name := names[i] + + // add nodes + node, err := types.NewServerWithLabels( + name, + types.KindNode, + types.ServerSpecV2{ + Hostname: name, + }, + map[string]string{"name": name}, + ) + require.NoError(t, err) + + _, err = srv.Auth().UpsertNode(ctx, node) + require.NoError(t, err) + } + testNodes, err := srv.Auth().GetNodes(ctx, apidefaults.Namespace) + require.NoError(t, err) + require.Len(t, testNodes, 6) + + // create user, role, and client + username := "theuser" + user, _, err := CreateUserAndRole(srv.Auth(), username, nil, nil) + require.NoError(t, err) + identity := TestUser(user.GetName()) + clt, err := srv.NewClient(identity) + require.NoError(t, err) + + require.NoError(t, err) + resp, err := clt.ListUnifiedResources(ctx, &proto.ListUnifiedResourcesRequest{ + PredicateExpression: `labels.name == "tifa"`, + Limit: 10, + }) + require.NoError(t, err) + require.Len(t, resp.Resources, 1) + require.Empty(t, resp.NextKey) +} + +// go test ./lib/auth -bench=BenchmarkListUnifiedResources -run=^$ -v -benchtime 1x +// goos: darwin +// goarch: arm64 +// pkg: github.com/gravitational/teleport/lib/auth +// BenchmarkListUnifiedResources +// BenchmarkListUnifiedResources/simple_labels +// BenchmarkListUnifiedResources/simple_labels-10 1 22900895292 ns/op 15071189320 B/op 272733781 allocs/op +// PASS +// ok github.com/gravitational/teleport/lib/auth 25.135s +func BenchmarkListUnifiedResources(b *testing.B) { + const nodeCount = 50_000 + const roleCount = 32 + + logger := logrus.StandardLogger() + logger.ReplaceHooks(make(logrus.LevelHooks)) + logrus.SetFormatter(utils.NewTestJSONFormatter()) + logger.SetLevel(logrus.DebugLevel) + logger.SetOutput(io.Discard) + + ctx := context.Background() + srv := newTestTLSServer(b) + + var ids []string + for i := 0; i < roleCount; i++ { + ids = append(ids, uuid.New().String()) + } + + ids[0] = "hidden" + + var hiddenNodes int + // Create test nodes. + for i := 0; i < nodeCount; i++ { + name := uuid.New().String() + id := ids[i%len(ids)] + if id == "hidden" { + hiddenNodes++ + } + node, err := types.NewServerWithLabels( + name, + types.KindNode, + types.ServerSpecV2{}, + map[string]string{ + "key": id, + "group": "users", + }, + ) + require.NoError(b, err) + + _, err = srv.Auth().UpsertNode(ctx, node) + require.NoError(b, err) + } + + for _, tc := range []struct { + desc string + editRole func(types.Role, string) + }{ + { + desc: "simple labels", + editRole: func(r types.Role, id string) { + if id == "hidden" { + r.SetNodeLabels(types.Deny, types.Labels{"key": {id}}) + } else { + r.SetNodeLabels(types.Allow, types.Labels{"key": {id}}) + } + }, + }, + } { + b.Run(tc.desc, func(b *testing.B) { + benchmarkListUnifiedResources( + b, ctx, + nodeCount, roleCount, hiddenNodes, + srv, + ids, + tc.editRole, + ) + }) + } +} + +func benchmarkListUnifiedResources( + b *testing.B, ctx context.Context, + nodeCount, roleCount, hiddenNodes int, + srv *TestTLSServer, + ids []string, + editRole func(r types.Role, id string), +) { + var roles []types.Role + for _, id := range ids { + role, err := types.NewRole(fmt.Sprintf("role-%s", id), types.RoleSpecV6{}) + require.NoError(b, err) + editRole(role, id) + roles = append(roles, role) + } + + // create user, role, and client + username := "user" + + user, err := CreateUser(srv.Auth(), username, roles...) + require.NoError(b, err) + user.SetTraits(map[string][]string{ + "group": {"users"}, + "email": {"test@example.com"}, + }) + err = srv.Auth().UpsertUser(user) + require.NoError(b, err) + identity := TestUser(user.GetName()) + clt, err := srv.NewClient(identity) + require.NoError(b, err) + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + var resources []*proto.PaginatedResource + req := &proto.ListUnifiedResourcesRequest{ + Limit: 1_000, + } + for { + rsp, err := clt.ListUnifiedResources(ctx, req) + require.NoError(b, err) + + resources = append(resources, rsp.Resources...) + req.StartKey = rsp.NextKey + if req.StartKey == "" { + break + } + } + require.Len(b, resources, nodeCount-hiddenNodes) + } +} + // TestGenerateHostCert attempts to generate host certificates using various // RBAC rules func TestGenerateHostCert(t *testing.T) { diff --git a/lib/auth/clt.go b/lib/auth/clt.go index 8597d470c76d9..b47203120c0e9 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -864,4 +864,6 @@ type ClientI interface { // UpsertUserPreferences creates or updates user preferences for a given username. UpsertUserPreferences(ctx context.Context, req *userpreferencesv1.UpsertUserPreferencesRequest) error + // ListUnifiedResources returns a paginated list of unified resources. + ListUnifiedResources(ctx context.Context, req *proto.ListUnifiedResourcesRequest) (*proto.ListUnifiedResourcesResponse, error) } diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 0513df8da9aa6..0e34d5eb6ae21 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -4330,6 +4330,17 @@ func (g *GRPCServer) GenerateCertAuthorityCRL(ctx context.Context, req *authpb.C return &authpb.CRL{CRL: crl}, nil } +// ListUnifiedResources retrieves a paginated list of unified resources. +func (g *GRPCServer) ListUnifiedResources(ctx context.Context, req *authpb.ListUnifiedResourcesRequest) (*authpb.ListUnifiedResourcesResponse, error) { + auth, err := g.authenticate(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + return auth.ListUnifiedResources(ctx, req) + +} + // ListResources retrieves a paginated list of resources. func (g *GRPCServer) ListResources(ctx context.Context, req *authpb.ListResourcesRequest) (*authpb.ListResourcesResponse, error) { auth, err := g.authenticate(ctx) @@ -4342,111 +4353,16 @@ func (g *GRPCServer) ListResources(ctx context.Context, req *authpb.ListResource return nil, trace.Wrap(err) } + paginatedResources, err := auth.MakePaginatedResources(req.ResourceType, resp.Resources) + if err != nil { + return nil, trace.Wrap(err, "making paginated resources") + } protoResp := &authpb.ListResourcesResponse{ NextKey: resp.NextKey, - Resources: make([]*authpb.PaginatedResource, len(resp.Resources)), + Resources: paginatedResources, TotalCount: int32(resp.TotalCount), } - for i, resource := range resp.Resources { - var protoResource *authpb.PaginatedResource - switch req.ResourceType { - case types.KindDatabaseServer: - database, ok := resource.(*types.DatabaseServerV3) - if !ok { - return nil, trace.BadParameter("database server has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_DatabaseServer{DatabaseServer: database}} - case types.KindDatabaseService: - databaseService, ok := resource.(*types.DatabaseServiceV1) - if !ok { - return nil, trace.BadParameter("database service has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_DatabaseService{DatabaseService: databaseService}} - case types.KindAppServer: - app, ok := resource.(*types.AppServerV3) - if !ok { - return nil, trace.BadParameter("application server has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_AppServer{AppServer: app}} - case types.KindNode: - srv, ok := resource.(*types.ServerV2) - if !ok { - return nil, trace.BadParameter("node has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_Node{Node: srv}} - case types.KindKubeServer: - srv, ok := resource.(*types.KubernetesServerV3) - if !ok { - return nil, trace.BadParameter("kubernetes server has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_KubernetesServer{KubernetesServer: srv}} - case types.KindWindowsDesktop: - desktop, ok := resource.(*types.WindowsDesktopV3) - if !ok { - return nil, trace.BadParameter("windows desktop has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_WindowsDesktop{WindowsDesktop: desktop}} - case types.KindWindowsDesktopService: - desktopService, ok := resource.(*types.WindowsDesktopServiceV3) - if !ok { - return nil, trace.BadParameter("windows desktop service has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_WindowsDesktopService{WindowsDesktopService: desktopService}} - case types.KindKubernetesCluster: - cluster, ok := resource.(*types.KubernetesClusterV3) - if !ok { - return nil, trace.BadParameter("kubernetes cluster has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_KubeCluster{KubeCluster: cluster}} - case types.KindUserGroup: - userGroup, ok := resource.(*types.UserGroupV1) - if !ok { - return nil, trace.BadParameter("user group has invalid type %T", resource) - } - - protoResource = &authpb.PaginatedResource{Resource: &authpb.PaginatedResource_UserGroup{UserGroup: userGroup}} - case types.KindAppOrSAMLIdPServiceProvider: - switch appOrSP := resource.(type) { - case *types.AppServerV3: - protoResource = &authpb.PaginatedResource{ - Resource: &authpb.PaginatedResource_AppServerOrSAMLIdPServiceProvider{ - AppServerOrSAMLIdPServiceProvider: &types.AppServerOrSAMLIdPServiceProviderV1{ - Resource: &types.AppServerOrSAMLIdPServiceProviderV1_AppServer{ - AppServer: appOrSP, - }, - }, - }, - } - case *types.SAMLIdPServiceProviderV1: - protoResource = &authpb.PaginatedResource{ - Resource: &authpb.PaginatedResource_AppServerOrSAMLIdPServiceProvider{ - AppServerOrSAMLIdPServiceProvider: &types.AppServerOrSAMLIdPServiceProviderV1{ - Resource: &types.AppServerOrSAMLIdPServiceProviderV1_SAMLIdPServiceProvider{ - SAMLIdPServiceProvider: appOrSP, - }, - }, - }, - } - default: - return nil, trace.BadParameter("expected types.SAMLIdPServiceProviderV1 or types.AppServerV3, got %T", resource) - } - - default: - return nil, trace.NotImplemented("resource type %s doesn't support pagination", req.ResourceType) - } - - protoResp.Resources[i] = protoResource - } - return protoResp, nil } diff --git a/lib/auth/helpers.go b/lib/auth/helpers.go index 9b8810352a135..76c7e23ba3777 100644 --- a/lib/auth/helpers.go +++ b/lib/auth/helpers.go @@ -374,6 +374,21 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) { } srv.AuthServer.SetLockWatcher(srv.LockWatcher) + unifiedResourcesCache, err := services.NewUnifiedResourceCache(srv.AuthServer.CloseContext(), services.UnifiedResourceCacheConfig{ + ResourceWatcherConfig: services.ResourceWatcherConfig{ + QueueSize: defaults.UnifiedResourcesQueueSize, + Component: teleport.ComponentUnifiedResource, + Client: srv.AuthServer, + MaxStaleness: time.Minute, + }, + ResourceGetter: srv.AuthServer, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + srv.AuthServer.SetUnifiedResourcesCache(unifiedResourcesCache) + headlessAuthenticationWatcher, err := local.NewHeadlessAuthenticationWatcher(srv.AuthServer.CloseContext(), local.HeadlessAuthenticationWatcherConfig{ Backend: b, }) diff --git a/lib/cache/cache.go b/lib/cache/cache.go index ad6d86fa80294..3f4591a8a6f51 100644 --- a/lib/cache/cache.go +++ b/lib/cache/cache.go @@ -2582,7 +2582,14 @@ func (c *Cache) ListResources(ctx context.Context, req proto.ListResourcesReques return nil, trace.Wrap(err) } - return local.FakePaginate(servers.AsResources(), req) + return local.FakePaginate(servers.AsResources(), local.FakePaginateParams{ + ResourceType: req.ResourceType, + Limit: req.Limit, + Labels: req.Labels, + SearchKeywords: req.SearchKeywords, + PredicateExpression: req.PredicateExpression, + StartKey: req.StartKey, + }) } } diff --git a/lib/defaults/defaults.go b/lib/defaults/defaults.go index e029193ab1cd8..7ae598bdf666a 100644 --- a/lib/defaults/defaults.go +++ b/lib/defaults/defaults.go @@ -321,6 +321,9 @@ const ( // ProxyQueueSize is proxy service queue size ProxyQueueSize = 8192 + // UnifiedResourcesQueueSize is the unified resource watcher queue size + UnifiedResourcesQueueSize = 8192 + // NodeQueueSize is node service queue size NodeQueueSize = 128 diff --git a/lib/kube/grpc/grpc.go b/lib/kube/grpc/grpc.go index 5f3a630c1cab1..60fd2a4015af9 100644 --- a/lib/kube/grpc/grpc.go +++ b/lib/kube/grpc/grpc.go @@ -24,7 +24,6 @@ import ( "golang.org/x/exp/slices" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - apiproto "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/defaults" proto "github.com/gravitational/teleport/api/gen/proto/go/teleport/kube/v1" "github.com/gravitational/teleport/api/types" @@ -530,14 +529,13 @@ func (s *Server) listResourcesUsingFakePagination( fakeRsp, err := local.FakePaginate( sortedClusters.AsResources(), // map the request to the fake pagination request. - apiproto.ListResourcesRequest{ + local.FakePaginateParams{ StartKey: req.StartKey, Limit: req.Limit, ResourceType: req.ResourceType, Labels: req.Labels, PredicateExpression: req.PredicateExpression, SearchKeywords: req.SearchKeywords, - NeedTotalCount: req.NeedTotalCount, }, ) if err != nil { diff --git a/lib/service/service.go b/lib/service/service.go index 3f2a93bd920c8..b4808e973be30 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -1737,6 +1737,22 @@ func (process *TeleportProcess) initAuthService() error { } authServer.SetLockWatcher(lockWatcher) + unifiedResourcesCache, err := services.NewUnifiedResourceCache(process.ExitContext(), services.UnifiedResourceCacheConfig{ + ResourceWatcherConfig: services.ResourceWatcherConfig{ + QueueSize: defaults.UnifiedResourcesQueueSize, + Component: teleport.ComponentUnifiedResource, + Log: process.log.WithField(trace.Component, teleport.ComponentUnifiedResource), + Client: authServer, + MaxStaleness: time.Minute, + }, + ResourceGetter: authServer, + }) + if err != nil { + return trace.Wrap(err) + } + + authServer.SetUnifiedResourcesCache(unifiedResourcesCache) + if embedderClient != nil { log.Debugf("Starting embedding watcher") embeddingProcessor := ai.NewEmbeddingProcessor(&ai.EmbeddingProcessorConfig{ diff --git a/lib/services/desktop.go b/lib/services/desktop.go index 30e3111ccf96f..b4d927412a815 100644 --- a/lib/services/desktop.go +++ b/lib/services/desktop.go @@ -27,7 +27,7 @@ import ( // WindowsDesktops defines an interface for managing Windows desktop hosts. type WindowsDesktops interface { - GetWindowsDesktops(context.Context, types.WindowsDesktopFilter) ([]types.WindowsDesktop, error) + WindowsDesktopGetter CreateWindowsDesktop(context.Context, types.WindowsDesktop) error UpdateWindowsDesktop(context.Context, types.WindowsDesktop) error UpsertWindowsDesktop(ctx context.Context, desktop types.WindowsDesktop) error @@ -37,6 +37,11 @@ type WindowsDesktops interface { ListWindowsDesktopServices(ctx context.Context, req types.ListWindowsDesktopServicesRequest) (*types.ListWindowsDesktopServicesResponse, error) } +// WindowsDesktopGetter is an interface for fetching WindowsDesktop resources. +type WindowsDesktopGetter interface { + GetWindowsDesktops(context.Context, types.WindowsDesktopFilter) ([]types.WindowsDesktop, error) +} + // MarshalWindowsDesktop marshals the WindowsDesktop resource to JSON. func MarshalWindowsDesktop(s types.WindowsDesktop, opts ...MarshalOption) ([]byte, error) { if err := s.CheckAndSetDefaults(); err != nil { diff --git a/lib/services/local/presence.go b/lib/services/local/presence.go index 1f04dacfa7aac..55119433117b3 100644 --- a/lib/services/local/presence.go +++ b/lib/services/local/presence.go @@ -1709,12 +1709,75 @@ func (s *PresenceService) listResourcesWithSort(ctx context.Context, req proto.L return nil, trace.NotImplemented("resource type %q is not supported for ListResourcesWithSort", req.ResourceType) } - return FakePaginate(resources, req) + return FakePaginate(resources, FakePaginateParams{ + ResourceType: req.ResourceType, + Limit: req.Limit, + Labels: req.Labels, + SearchKeywords: req.SearchKeywords, + PredicateExpression: req.PredicateExpression, + StartKey: req.StartKey, + }) +} + +// FakePaginateParams is used in FakePaginate to help filter down listing of resources into pages +// and includes required fields to support ListResources and ListUnifiedResources requests +type FakePaginateParams struct { + // ResourceType is the resource that is going to be retrieved. + // This only needs to be set explicitly for the `ListResources` rpc. + ResourceType string + // Namespace is the namespace of resources. + Namespace string + // Limit is the maximum amount of resources to retrieve. + Limit int32 + // StartKey is used to start listing resources from a specific spot. It + // should be set to the previous NextKey value if using pagination, or + // left empty. + StartKey string + // Labels is a label-based matcher if non-empty. + Labels map[string]string + // PredicateExpression defines boolean conditions that will be matched against the resource. + PredicateExpression string + // SearchKeywords is a list of search keywords to match against resource field values. + SearchKeywords []string + // SortBy describes which resource field and which direction to sort by. + SortBy types.SortBy + // WindowsDesktopFilter specifies windows desktop specific filters. + WindowsDesktopFilter types.WindowsDesktopFilter + // Kinds is a list of kinds to match against a resource's kind. This can be used in a + // unified resource request that can include multiple types. + Kinds []string + // NeedTotalCount indicates whether or not the caller also wants the total number of resources after filtering. + NeedTotalCount bool +} + +// GetWindowsDesktopFilter retrieves the WindowsDesktopFilter from params +func (req *FakePaginateParams) GetWindowsDesktopFilter() types.WindowsDesktopFilter { + if req != nil { + return req.WindowsDesktopFilter + } + return types.WindowsDesktopFilter{} +} + +// CheckAndSetDefaults checks and sets default values. +func (req *FakePaginateParams) CheckAndSetDefaults() error { + if req.Namespace == "" { + req.Namespace = apidefaults.Namespace + } + // If the Limit parameter was not provided instead of returning an error fallback to the default limit. + if req.Limit == 0 { + req.Limit = apidefaults.DefaultChunkSize + } + + if req.Limit < 0 { + return trace.BadParameter("negative parameter limit") + } + + return nil } // FakePaginate is used when we are working with an entire list of resources upfront but still requires pagination. // While applying filters, it will also deduplicate matches found. -func FakePaginate(resources []types.ResourceWithLabels, req proto.ListResourcesRequest) (*types.ListResourcesResponse, error) { +func FakePaginate(resources []types.ResourceWithLabels, req FakePaginateParams) (*types.ListResourcesResponse, error) { if err := req.CheckAndSetDefaults(); err != nil { return nil, trace.Wrap(err) } @@ -1726,6 +1789,7 @@ func FakePaginate(resources []types.ResourceWithLabels, req proto.ListResourcesR Labels: req.Labels, SearchKeywords: req.SearchKeywords, PredicateExpression: req.PredicateExpression, + Kinds: req.Kinds, } // Iterate and filter every resource, deduplicating while matching. diff --git a/lib/services/local/presence_test.go b/lib/services/local/presence_test.go index 2d5777903f866..a0a495e65fe9d 100644 --- a/lib/services/local/presence_test.go +++ b/lib/services/local/presence_test.go @@ -862,7 +862,14 @@ func TestListResources_Helpers(t *testing.T) { nodes, err := presence.GetNodes(ctx, namespace) require.NoError(t, err) - return FakePaginate(types.Servers(nodes).AsResources(), req) + return FakePaginate(types.Servers(nodes).AsResources(), FakePaginateParams{ + ResourceType: req.ResourceType, + Limit: req.Limit, + Labels: req.Labels, + SearchKeywords: req.SearchKeywords, + PredicateExpression: req.PredicateExpression, + StartKey: req.StartKey, + }) }, }, } @@ -1077,7 +1084,7 @@ func TestFakePaginate_TotalCount(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - req := proto.ListResourcesRequest{ + req := FakePaginateParams{ ResourceType: types.KindNode, Limit: int32(tc.limit), NeedTotalCount: true, @@ -1111,7 +1118,7 @@ func TestFakePaginate_TotalCount(t *testing.T) { t.Run("total count with no match", func(t *testing.T) { t.Parallel() - req := proto.ListResourcesRequest{ + req := FakePaginateParams{ ResourceType: types.KindNode, Limit: 5, NeedTotalCount: true, @@ -1126,7 +1133,7 @@ func TestFakePaginate_TotalCount(t *testing.T) { t.Run("total count with all matches", func(t *testing.T) { t.Parallel() - req := proto.ListResourcesRequest{ + req := FakePaginateParams{ ResourceType: types.KindNode, Limit: 5, NeedTotalCount: true, diff --git a/lib/services/matchers.go b/lib/services/matchers.go index a4f1aea2c67f6..b4c07352f7bb8 100644 --- a/lib/services/matchers.go +++ b/lib/services/matchers.go @@ -17,6 +17,8 @@ limitations under the License. package services import ( + "fmt" + "github.com/gravitational/trace" "github.com/sirupsen/logrus" "golang.org/x/exp/slices" @@ -140,18 +142,19 @@ type ResourceSeenKey struct{ name, addr string } // is not provided but is provided for kind `KubernetesCluster`. func MatchResourceByFilters(resource types.ResourceWithLabels, filter MatchResourceFilter, seenMap map[ResourceSeenKey]struct{}) (bool, error) { var specResource types.ResourceWithLabels + resourceKind := resource.GetKind() // We assume when filtering for services like KubeService, AppServer, and DatabaseServer // the user is wanting to filter the contained resource ie. KubeClusters, Application, and Database. resourceKey := ResourceSeenKey{} - switch filter.ResourceKind { + switch resourceKind { case types.KindNode, types.KindDatabaseService, types.KindKubernetesCluster, types.KindWindowsDesktop, types.KindWindowsDesktopService, types.KindUserGroup: specResource = resource - resourceKey.name = specResource.GetName() + resourceKey.name = fmt.Sprintf("%s/%s", specResource.GetName(), resourceKind) case types.KindKubeServer: if seenMap != nil { @@ -159,34 +162,24 @@ func MatchResourceByFilters(resource types.ResourceWithLabels, filter MatchResou } return matchAndFilterKubeClusters(resource, filter) - case types.KindAppServer: - server, ok := resource.(types.AppServer) - if !ok { - return false, trace.BadParameter("expected types.AppServer, got %T", resource) - } - specResource = server.GetApp() - app := server.GetApp() - resourceKey.name = app.GetName() - resourceKey.addr = app.GetPublicAddr() - case types.KindDatabaseServer: server, ok := resource.(types.DatabaseServer) if !ok { return false, trace.BadParameter("expected types.DatabaseServer, got %T", resource) } specResource = server.GetDatabase() - resourceKey.name = specResource.GetName() + resourceKey.name = fmt.Sprintf("%s/%s/", specResource.GetName(), resourceKind) - case types.KindAppOrSAMLIdPServiceProvider: + case types.KindAppServer, types.KindSAMLIdPServiceProvider, types.KindAppOrSAMLIdPServiceProvider: switch appOrSP := resource.(type) { case types.AppServer: app := appOrSP.GetApp() specResource = app - resourceKey.name = app.GetName() + resourceKey.name = fmt.Sprintf("%s/%s/", specResource.GetName(), resourceKind) resourceKey.addr = app.GetPublicAddr() case types.SAMLIdPServiceProvider: specResource = appOrSP - resourceKey.name = appOrSP.GetName() + resourceKey.name = fmt.Sprintf("%s/%s/", specResource.GetName(), resourceKind) default: return false, trace.BadParameter("expected types.SAMLIdPServiceProvider or types.AppServer, got %T", resource) } @@ -195,15 +188,15 @@ func MatchResourceByFilters(resource types.ResourceWithLabels, filter MatchResou // of cases we need to handle. If the resource type didn't match any arm before // and it is not a Kubernetes resource kind, we return an error. if !slices.Contains(types.KubernetesResourcesKinds, filter.ResourceKind) { - return false, trace.NotImplemented("filtering for resource kind %q not supported", filter.ResourceKind) + return false, trace.NotImplemented("filtering for resource kind %q not supported", resourceKind) } specResource = resource - resourceKey.name = specResource.GetName() + resourceKey.name = fmt.Sprintf("%s/%s/", specResource.GetName(), resourceKind) } var match bool - if len(filter.Labels) == 0 && len(filter.SearchKeywords) == 0 && filter.PredicateExpression == "" { + if len(filter.Labels) == 0 && len(filter.SearchKeywords) == 0 && filter.PredicateExpression == "" && len(filter.Kinds) == 0 { match = true } @@ -241,6 +234,10 @@ func matchResourceByFilters(resource types.ResourceWithLabels, filter MatchResou } } + if !types.MatchKinds(resource, filter.Kinds) { + return false, nil + } + if !types.MatchLabels(resource, filter.Labels) { return false, nil } @@ -286,6 +283,10 @@ type MatchResourceFilter struct { SearchKeywords []string // PredicateExpression holds boolean conditions that must be matched. PredicateExpression string + // Kinds is a list of resourceKinds to be used when doing a unified resource query. + // It will filter out any kind not present in the list. If the list is not present or empty + // then all kinds are valid and will be returned (still subject to other included filters) + Kinds []string } const ( diff --git a/lib/services/matchers_test.go b/lib/services/matchers_test.go index d4b2ff63760df..a635204b4835a 100644 --- a/lib/services/matchers_test.go +++ b/lib/services/matchers_test.go @@ -432,8 +432,15 @@ func TestMatchResourceByFilters(t *testing.T) { filters: MatchResourceFilter{ResourceKind: types.KindNode}, }, { - name: "unsupported resource kind", - resource: func() types.ResourceWithLabels { return nil }, + name: "unsupported resource kind", + resource: func() types.ResourceWithLabels { + badResource, err := types.NewConnectionDiagnosticV1("123", map[string]string{}, + types.ConnectionDiagnosticSpecV1{ + Message: types.DiagnosticMessageSuccess, + }) + require.NoError(t, err) + return badResource + }, filters: MatchResourceFilter{ ResourceKind: "unsupported", SearchKeywords: []string{"nothing"}, diff --git a/lib/services/presence.go b/lib/services/presence.go index aba69f2c6cd07..d203fea241a1b 100644 --- a/lib/services/presence.go +++ b/lib/services/presence.go @@ -36,6 +36,16 @@ type NodesGetter interface { GetNodes(ctx context.Context, namespace string) ([]types.Server, error) } +// DatabaseServersGetter is a service that gets database servers. +type DatabaseServersGetter interface { + GetDatabaseServers(context.Context, string, ...MarshalOption) ([]types.DatabaseServer, error) +} + +// AppServersGetter is a service that gets application servers. +type AppServersGetter interface { + GetApplicationServers(ctx context.Context, namespace string) ([]types.AppServer, error) +} + // NodesStreamGetter is a service that gets nodes. type NodesStreamGetter interface { // GetNodeStream returns a list of registered servers. diff --git a/lib/services/saml_idp_service_provider.go b/lib/services/saml_idp_service_provider.go index 31f76648688b0..2ce68712a64dc 100644 --- a/lib/services/saml_idp_service_provider.go +++ b/lib/services/saml_idp_service_provider.go @@ -25,10 +25,14 @@ import ( "github.com/gravitational/teleport/lib/utils" ) +// SAMLIdPServiceProviderGetter defines interface for fetching SAMLIdPServiceProvider resources. +type SAMLIdpServiceProviderGetter interface { + ListSAMLIdPServiceProviders(ctx context.Context, pageSize int, nextKey string) ([]types.SAMLIdPServiceProvider, string, error) +} + // SAMLIdPServiceProvider defines an interface for managing SAML IdP service providers. type SAMLIdPServiceProviders interface { - // ListSAMLIdPServiceProviders returns a paginated list of all SAML IdP service provider resources. - ListSAMLIdPServiceProviders(context.Context, int, string) ([]types.SAMLIdPServiceProvider, string, error) + SAMLIdpServiceProviderGetter // GetSAMLIdPServiceProvider returns the specified SAML IdP service provider resources. GetSAMLIdPServiceProvider(ctx context.Context, name string) (types.SAMLIdPServiceProvider, error) // CreateSAMLIdPServiceProvider creates a new SAML IdP service provider resource. diff --git a/lib/services/unified_resource.go b/lib/services/unified_resource.go new file mode 100644 index 0000000000000..b0b7d607ecd85 --- /dev/null +++ b/lib/services/unified_resource.go @@ -0,0 +1,514 @@ +// Copyright 2023 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 services + +import ( + "bytes" + "context" + "sync" + "time" + + "github.com/google/btree" + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + log "github.com/sirupsen/logrus" + + "github.com/gravitational/teleport" + apidefaults "github.com/gravitational/teleport/api/defaults" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/backend" + "github.com/gravitational/teleport/lib/utils" +) + +// UnifiedResourceCacheConfig is used to configure a UnifiedResourceCache +type UnifiedResourceCacheConfig struct { + // BTreeDegree is a degree of B-Tree, 2 for example, will create a + // 2-3-4 tree (each node contains 1-3 items and 2-4 children). + BTreeDegree int + // Clock is a clock for time-related operations + Clock clockwork.Clock + // Component is a logging component + Component string + ResourceWatcherConfig + ResourceGetter +} + +// UnifiedResourceCache contains a representation of all resources that are displayable in the UI +type UnifiedResourceCache struct { + mu sync.Mutex + log *log.Entry + cfg UnifiedResourceCacheConfig + // tree is a BTree with items + tree *btree.BTreeG[*item] + initializationC chan struct{} + stale bool + once sync.Once + cache *utils.FnCache + ResourceGetter +} + +// NewUnifiedResourceCache creates a new memory cache that holds the unified resources +func NewUnifiedResourceCache(ctx context.Context, cfg UnifiedResourceCacheConfig) (*UnifiedResourceCache, error) { + if err := cfg.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err, "setting defaults for unified resource cache") + } + + lazyCache, err := utils.NewFnCache(utils.FnCacheConfig{ + Context: ctx, + TTL: 15 * time.Second, + Clock: cfg.Clock, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + m := &UnifiedResourceCache{ + log: log.WithFields(log.Fields{ + trace.Component: cfg.Component, + }), + cfg: cfg, + tree: btree.NewG(cfg.BTreeDegree, func(a, b *item) bool { + return a.Less(b) + }), + initializationC: make(chan struct{}), + ResourceGetter: cfg.ResourceGetter, + cache: lazyCache, + stale: true, + } + + if err := newWatcher(ctx, m, cfg.ResourceWatcherConfig); err != nil { + return nil, trace.Wrap(err, "creating unified resource watcher") + } + return m, nil +} + +// CheckAndSetDefaults checks and sets default values +func (cfg *UnifiedResourceCacheConfig) CheckAndSetDefaults() error { + if cfg.BTreeDegree <= 0 { + cfg.BTreeDegree = 8 + } + if cfg.Clock == nil { + cfg.Clock = clockwork.NewRealClock() + } + if cfg.Component == "" { + cfg.Component = teleport.ComponentUnifiedResource + } + return nil +} + +// put stores the value into backend (creates if it does not +// exist, updates it otherwise) +func (c *UnifiedResourceCache) put(ctx context.Context, i item) error { + if len(i.Key) == 0 { + return trace.BadParameter("missing parameter key") + } + c.mu.Lock() + defer c.mu.Unlock() + c.tree.ReplaceOrInsert(&i) + return nil +} + +func putResources[T resource](tree *btree.BTreeG[*item], resources []T) { + for _, resource := range resources { + tree.ReplaceOrInsert(&item{Key: resourceKey(resource), Value: resource}) + } +} + +// delete removes the item by key, returns NotFound error +// if item does not exist +func (c *UnifiedResourceCache) delete(ctx context.Context, key []byte) error { + if len(key) == 0 { + return trace.BadParameter("missing parameter key") + } + return c.read(ctx, func(tree *btree.BTreeG[*item]) error { + if _, ok := tree.Delete(&item{Key: key}); !ok { + return trace.NotFound("key %q is not found", string(key)) + } + return nil + }) +} + +func (c *UnifiedResourceCache) getRange(ctx context.Context, startKey, endKey []byte, limit int) ([]resource, error) { + if len(startKey) == 0 { + return nil, trace.BadParameter("missing parameter startKey") + } + if len(endKey) == 0 { + return nil, trace.BadParameter("missing parameter endKey") + } + if limit <= 0 { + limit = backend.DefaultRangeLimit + } + + var res []resource + err := c.read(ctx, func(tree *btree.BTreeG[*item]) error { + tree.AscendRange(&item{Key: startKey}, &item{Key: endKey}, func(item *item) bool { + res = append(res, item.Value) + if limit > 0 && len(res) >= limit { + return false + } + return true + }) + return nil + }) + if err != nil { + return nil, trace.Wrap(err) + } + + if len(res) == backend.DefaultRangeLimit { + c.log.Warnf("Range query hit backend limit. (this is a bug!) startKey=%q,limit=%d", startKey, backend.DefaultRangeLimit) + } + + return res, nil +} + +// GetUnifiedResources returns a list of all resources stored in the current unifiedResourceCollector tree +func (c *UnifiedResourceCache) GetUnifiedResources(ctx context.Context) ([]types.ResourceWithLabels, error) { + result, err := c.getRange(ctx, backend.Key(prefix), backend.RangeEnd(backend.Key(prefix)), backend.NoLimit) + if err != nil { + return nil, trace.Wrap(err, "getting unified resource range") + } + + resources := make([]types.ResourceWithLabels, 0, len(result)) + for _, item := range result { + resources = append(resources, item.CloneResource()) + } + + return resources, nil +} + +// ResourceGetter is an interface that provides a way to fetch all the resources +// that can be stored in the UnifiedResourceCache +type ResourceGetter interface { + NodesGetter + DatabaseServersGetter + AppServersGetter + WindowsDesktopGetter + KubernetesServerGetter + SAMLIdpServiceProviderGetter +} + +// newWatcher starts and returns a new resource watcher for unified resources. +func newWatcher(ctx context.Context, resourceCache *UnifiedResourceCache, cfg ResourceWatcherConfig) error { + if err := cfg.CheckAndSetDefaults(); err != nil { + return trace.Wrap(err, "setting defaults for unified resource watcher config") + } + + if _, err := newResourceWatcher(ctx, resourceCache, cfg); err != nil { + return trace.Wrap(err, "creating a new unified resource watcher") + } + return nil +} + +func resourceKey(resource types.Resource) []byte { + return backend.Key(prefix, resource.GetName(), resource.GetKind()) +} + +func (c *UnifiedResourceCache) getResourcesAndUpdateCurrent(ctx context.Context) error { + newNodes, err := c.getNodes(ctx) + if err != nil { + return trace.Wrap(err) + } + + newDbs, err := c.getDatabaseServers(ctx) + if err != nil { + return trace.Wrap(err) + } + + newKubes, err := c.getKubeServers(ctx) + if err != nil { + return trace.Wrap(err) + } + + newApps, err := c.getAppServers(ctx) + if err != nil { + return trace.Wrap(err) + } + + newSAMLApps, err := c.getSAMLApps(ctx) + if err != nil { + return trace.Wrap(err) + } + + newDesktops, err := c.getDesktops(ctx) + if err != nil { + return trace.Wrap(err) + } + + c.mu.Lock() + defer c.mu.Unlock() + c.tree.Clear(false) + putResources[types.Server](c.tree, newNodes) + putResources[types.DatabaseServer](c.tree, newDbs) + putResources[types.AppServer](c.tree, newApps) + putResources[types.KubeServer](c.tree, newKubes) + putResources[types.SAMLIdPServiceProvider](c.tree, newSAMLApps) + putResources[types.WindowsDesktop](c.tree, newDesktops) + c.stale = false + c.defineCollectorAsInitialized() + return nil + +} + +// getNodes will get all nodes +func (c *UnifiedResourceCache) getNodes(ctx context.Context) ([]types.Server, error) { + newNodes, err := c.ResourceGetter.GetNodes(ctx, apidefaults.Namespace) + if err != nil { + return nil, trace.Wrap(err, "getting nodes for unified resource watcher") + } + + return newNodes, err +} + +// getDatabaseServers will get all database servers +func (c *UnifiedResourceCache) getDatabaseServers(ctx context.Context) ([]types.DatabaseServer, error) { + newDbs, err := c.GetDatabaseServers(ctx, apidefaults.Namespace) + if err != nil { + return nil, trace.Wrap(err, "getting database servers for unified resource watcher") + } + // because it's possible to have multiple replicas of a database server serving the same database + // we only want to store one based on its internal database resource + unique := map[string]struct{}{} + resources := make([]types.DatabaseServer, 0, len(newDbs)) + for _, dbServer := range newDbs { + db := dbServer.GetDatabase() + if _, ok := unique[db.GetName()]; ok { + continue + } + unique[db.GetName()] = struct{}{} + resources = append(resources, dbServer) + } + + return resources, nil +} + +// getKubeServers will get all kube servers +func (c *UnifiedResourceCache) getKubeServers(ctx context.Context) ([]types.KubeServer, error) { + newKubes, err := c.GetKubernetesServers(ctx) + if err != nil { + return nil, trace.Wrap(err, "getting kube servers for unified resource watcher") + } + unique := map[string]struct{}{} + resources := make([]types.KubeServer, 0, len(newKubes)) + for _, kubeServer := range newKubes { + cluster := kubeServer.GetCluster() + if _, ok := unique[cluster.GetName()]; ok { + continue + } + unique[cluster.GetName()] = struct{}{} + resources = append(resources, kubeServer) + } + + return resources, nil +} + +// getAppServers will get all application servers +func (c *UnifiedResourceCache) getAppServers(ctx context.Context) ([]types.AppServer, error) { + newApps, err := c.GetApplicationServers(ctx, apidefaults.Namespace) + if err != nil { + return nil, trace.Wrap(err, "getting app servers for unified resource watcher") + } + unique := map[string]struct{}{} + resources := make([]types.AppServer, 0, len(newApps)) + for _, appServer := range newApps { + app := appServer.GetApp() + if _, ok := unique[app.GetName()]; ok { + continue + } + unique[app.GetName()] = struct{}{} + resources = append(resources, appServer) + } + + return resources, nil +} + +// getDesktops will get all windows desktops +func (c *UnifiedResourceCache) getDesktops(ctx context.Context) ([]types.WindowsDesktop, error) { + newDesktops, err := c.GetWindowsDesktops(ctx, types.WindowsDesktopFilter{}) + if err != nil { + return nil, trace.Wrap(err, "getting desktops for unified resource watcher") + } + + return newDesktops, nil +} + +// getSAMLApps will get all SAML Idp Service Providers +func (c *UnifiedResourceCache) getSAMLApps(ctx context.Context) ([]types.SAMLIdPServiceProvider, error) { + var newSAMLApps []types.SAMLIdPServiceProvider + startKey := "" + + for { + resp, nextKey, err := c.ListSAMLIdPServiceProviders(ctx, apidefaults.DefaultChunkSize, startKey) + + if err != nil { + return nil, trace.Wrap(err, "getting SAML apps for unified resource watcher") + } + newSAMLApps = append(newSAMLApps, resp...) + + if nextKey == "" { + break + } + + startKey = nextKey + } + + return newSAMLApps, nil +} + +// read applies the supplied closure to either the primary tree or the ttl-based fallback tree depending on +// wether or not the cache is currently healthy. locking is handled internally and the passed-in tree should +// not be accessed after the closure completes. +func (c *UnifiedResourceCache) read(ctx context.Context, fn func(tree *btree.BTreeG[*item]) error) error { + c.mu.Lock() + + if !c.stale { + fn(c.tree) + c.mu.Unlock() + return nil + } + + c.mu.Unlock() + ttlTree, err := utils.FnCacheGet(ctx, c.cache, "unified_resources", func(ctx context.Context) (*btree.BTreeG[*item], error) { + fallbackCache := &UnifiedResourceCache{ + cfg: c.cfg, + tree: btree.NewG(c.cfg.BTreeDegree, func(a, b *item) bool { + return a.Less(b) + }), + ResourceGetter: c.ResourceGetter, + initializationC: make(chan struct{}), + } + if err := fallbackCache.getResourcesAndUpdateCurrent(ctx); err != nil { + return nil, trace.Wrap(err) + } + return fallbackCache.tree, nil + }) + c.mu.Lock() + + if !c.stale { + // primary became healthy while we were waiting + fn(c.tree) + c.mu.Unlock() + return nil + } + c.mu.Unlock() + + if err != nil { + // ttl-tree setup failed + return trace.Wrap(err) + } + + fn(ttlTree) + return nil +} + +func (c *UnifiedResourceCache) notifyStale() { + c.mu.Lock() + defer c.mu.Unlock() + c.stale = true +} + +func (c *UnifiedResourceCache) initializationChan() <-chan struct{} { + return c.initializationC +} + +// IsInitialized is used to check that the cache has done its initial +// sync +func (c *UnifiedResourceCache) IsInitialized() bool { + select { + case <-c.initializationC: + return true + default: + return false + } +} + +func (c *UnifiedResourceCache) processEventAndUpdateCurrent(ctx context.Context, event types.Event) { + if event.Resource == nil { + c.log.Warnf("Unexpected event: %v.", event) + return + } + + switch event.Type { + case types.OpDelete: + c.delete(ctx, resourceKey(event.Resource)) + case types.OpPut: + c.put(ctx, item{ + Key: resourceKey(event.Resource), + Value: event.Resource.(resource), + }) + default: + c.log.Warnf("unsupported event type %s.", event.Type) + return + } +} + +// resourceKinds returns a list of resources to be watched. +func (c *UnifiedResourceCache) resourceKinds() []types.WatchKind { + return []types.WatchKind{ + {Kind: types.KindNode}, + {Kind: types.KindDatabaseServer}, + {Kind: types.KindAppServer}, + {Kind: types.KindSAMLIdPServiceProvider}, + {Kind: types.KindWindowsDesktop}, + {Kind: types.KindKubeServer}, + } +} + +func (c *UnifiedResourceCache) defineCollectorAsInitialized() { + c.once.Do(func() { + // mark watcher as initialized. + close(c.initializationC) + }) +} + +// Less is used for Btree operations, +// returns true if item is less than the other one +func (i *item) Less(iother btree.Item) bool { + switch other := iother.(type) { + case *item: + return bytes.Compare(i.Key, other.Key) < 0 + case *prefixItem: + return !iother.Less(i) + default: + return false + } +} + +// prefixItem is used for prefix matches on a B-Tree +type prefixItem struct { + // prefix is a prefix to match + prefix []byte +} + +// Less is used for Btree operations +func (p *prefixItem) Less(iother btree.Item) bool { + other := iother.(*item) + return !bytes.HasPrefix(other.Key, p.prefix) +} + +type resource interface { + types.ResourceWithLabels + CloneResource() types.ResourceWithLabels +} + +type item struct { + // Key is a key of the key value item + Key []byte + // Value represents a resource such as types.Server or types.DatabaseServer + Value resource +} + +const ( + prefix = "unified_resource" +) diff --git a/lib/services/unified_resource_test.go b/lib/services/unified_resource_test.go new file mode 100644 index 0000000000000..049e9f593289b --- /dev/null +++ b/lib/services/unified_resource_test.go @@ -0,0 +1,193 @@ +// Copyright 2023 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 services_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/defaults" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/backend/memory" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/services/local" +) + +func TestUnifiedResourceWatcher(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + bk, err := memory.New(memory.Config{}) + require.NoError(t, err) + + type client struct { + services.Presence + services.WindowsDesktops + services.SAMLIdPServiceProviders + types.Events + } + + samlService, err := local.NewSAMLIdPServiceProviderService(bk) + require.NoError(t, err) + + clt := &client{ + Presence: local.NewPresenceService(bk), + WindowsDesktops: local.NewWindowsDesktopService(bk), + SAMLIdPServiceProviders: samlService, + Events: local.NewEventsService(bk), + } + // Add node to the backend. + node := newNodeServer(t, "node1", "127.0.0.1:22", false /*tunnel*/) + _, err = clt.UpsertNode(ctx, node) + require.NoError(t, err) + + db, err := types.NewDatabaseV3(types.Metadata{ + Name: "db1", + }, types.DatabaseSpecV3{ + Protocol: "test-protocol", + URI: "test-uri", + }) + require.NoError(t, err) + dbServer, err := types.NewDatabaseServerV3(types.Metadata{ + Name: "db1-server", + }, types.DatabaseServerSpecV3{ + Hostname: "db-hostname", + HostID: uuid.NewString(), + Database: db, + }) + require.NoError(t, err) + _, err = clt.UpsertDatabaseServer(ctx, dbServer) + require.NoError(t, err) + + w, err := services.NewUnifiedResourceCache(ctx, services.UnifiedResourceCacheConfig{ + ResourceWatcherConfig: services.ResourceWatcherConfig{ + Component: teleport.ComponentUnifiedResource, + Client: clt, + }, + ResourceGetter: clt, + }) + require.NoError(t, err) + // node and db expected initially + res, err := w.GetUnifiedResources(ctx) + require.NoError(t, err) + require.Len(t, res, 2) + + assert.Eventually(t, func() bool { + return w.IsInitialized() + }, 5*time.Second, 10*time.Millisecond, "unified resource watcher never initialized") + + // Add app to the backend. + app, err := types.NewAppServerV3( + types.Metadata{Name: "app1"}, + types.AppServerSpecV3{ + HostID: "app1-host-id", + App: newApp(t, "app1"), + }, + ) + require.NoError(t, err) + _, err = clt.UpsertApplicationServer(ctx, app) + require.NoError(t, err) + + // Add saml idp service provider to the backend. + samlapp, err := types.NewSAMLIdPServiceProvider( + types.Metadata{ + Name: "sp1", + }, + types.SAMLIdPServiceProviderSpecV1{ + EntityDescriptor: newTestEntityDescriptor("sp1"), + EntityID: "sp1", + }, + ) + require.NoError(t, err) + err = clt.CreateSAMLIdPServiceProvider(ctx, samlapp) + require.NoError(t, err) + + win, err := types.NewWindowsDesktopV3( + "win1", + nil, + types.WindowsDesktopSpecV3{Addr: "localhost", HostID: "win1-host-id"}, + ) + require.NoError(t, err) + err = clt.UpsertWindowsDesktop(ctx, win) + require.NoError(t, err) + + // we expect each of the resources above to exist + expectedRes := []types.ResourceWithLabels{node, app, samlapp, dbServer, win} + assert.Eventually(t, func() bool { + res, err = w.GetUnifiedResources(ctx) + return len(res) == len(expectedRes) + }, 5*time.Second, 10*time.Millisecond, "Timed out waiting for unified resources to be added") + assert.Empty(t, cmp.Diff( + expectedRes, + res, + cmpopts.EquateEmpty(), + cmpopts.IgnoreFields(types.Metadata{}, "ID"), + // Ignore order. + cmpopts.SortSlices(func(a, b types.ResourceWithLabels) bool { return a.GetName() < b.GetName() }), + )) + + // // Update and remove some resources. + nodeUpdated := newNodeServer(t, "node1", "192.168.0.1:22", false /*tunnel*/) + _, err = clt.UpsertNode(ctx, nodeUpdated) + require.NoError(t, err) + err = clt.DeleteApplicationServer(ctx, defaults.Namespace, "app1-host-id", "app1") + require.NoError(t, err) + + // this should include the updated node, and shouldn't have any apps included + expectedRes = []types.ResourceWithLabels{nodeUpdated, samlapp, dbServer, win} + assert.Eventually(t, func() bool { + res, err = w.GetUnifiedResources(ctx) + require.NoError(t, err) + serverUpdated := slices.ContainsFunc(res, func(r types.ResourceWithLabels) bool { + node, ok := r.(types.Server) + return ok && node.GetAddr() == "192.168.0.1:22" + }) + return len(res) == len(expectedRes) && serverUpdated + }, 5*time.Second, 10*time.Millisecond, "Timed out waiting for unified resources to be updated") + assert.Empty(t, cmp.Diff( + expectedRes, + res, + cmpopts.EquateEmpty(), + cmpopts.IgnoreFields(types.Metadata{}, "ID"), + // Ignore order. + cmpopts.SortSlices(func(a, b types.ResourceWithLabels) bool { return a.GetName() < b.GetName() }), + )) +} + +func newTestEntityDescriptor(entityID string) string { + return fmt.Sprintf(testEntityDescriptor, entityID) +} + +// A test entity descriptor from https://sptest.iamshowcase.com/testsp_metadata.xml. +const testEntityDescriptor = ` + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + + + +` diff --git a/lib/services/watcher.go b/lib/services/watcher.go index fd010c44f74ec..ecb25d0ab7f32 100644 --- a/lib/services/watcher.go +++ b/lib/services/watcher.go @@ -18,6 +18,7 @@ package services import ( "context" + "strings" "sync" "sync/atomic" "time" @@ -37,8 +38,8 @@ import ( // resourceCollector is a generic interface for maintaining an up-to-date view // of a resource set being monitored. Used in conjunction with resourceWatcher. type resourceCollector interface { - // resourceKind specifies the resource kind to watch. - resourceKind() string + // resourceKinds specifies the resource kind to watch. + resourceKinds() []types.WatchKind // getResourcesAndUpdateCurrent is called when the resources should be // (re-)fetched directly. getResourcesAndUpdateCurrent(context.Context) error @@ -69,6 +70,8 @@ type ResourceWatcherConfig struct { MaxStaleness time.Duration // ResetC is a channel to notify of internal watcher reset (used in tests). ResetC chan time.Duration + // QueueSize is an optional queue size + QueueSize int } // CheckAndSetDefaults checks parameters and sets default values. @@ -108,7 +111,7 @@ func newResourceWatcher(ctx context.Context, collector resourceCollector, cfg Re if err != nil { return nil, trace.Wrap(err) } - cfg.Log = cfg.Log.WithField("resource-kind", collector.resourceKind()) + cfg.Log = cfg.Log.WithField("resource-kind", collector.resourceKinds()) ctx, cancel := context.WithCancel(ctx) p := &resourceWatcher{ ResourceWatcherConfig: cfg, @@ -182,9 +185,13 @@ func (p *resourceWatcher) WaitInitialization() error { case <-p.collector.initializationChan(): return nil case <-t.C: - p.Log.Debugf("ResourceWatcher %s is not yet initialized.", p.collector.resourceKind()) + p.Log.Debugf("ResourceWatcher %s is not yet initialized.", p.collector.resourceKinds()) case <-p.ctx.Done(): - return trace.BadParameter("ResourceWatcher %s failed to initialize.", p.collector.resourceKind()) + var kindStrings []string + for _, kind := range p.collector.resourceKinds() { + kindStrings = append(kindStrings, kind.Kind) + } + return trace.BadParameter("ResourceWatcher %s failed to initialize.", strings.Join(kindStrings, ", ")) } } } @@ -257,11 +264,16 @@ func (p *resourceWatcher) runWatchLoop() { // watch monitors new resource updates, maintains a local view and broadcasts // notifications to connected agents. func (p *resourceWatcher) watch() error { - watcher, err := p.Client.NewWatcher(p.ctx, types.Watch{ + watch := types.Watch{ Name: p.Component, MetricComponent: p.Component, - Kinds: []types.WatchKind{{Kind: p.collector.resourceKind()}}, - }) + Kinds: p.collector.resourceKinds(), + } + + if p.QueueSize > 0 { + watch.QueueSize = p.QueueSize + } + watcher, err := p.Client.NewWatcher(p.ctx, watch) if err != nil { return trace.Wrap(err) } @@ -389,9 +401,9 @@ func (p *proxyCollector) GetCurrent() []types.Server { return serverMapValues(p.current) } -// resourceKind specifies the resource kind to watch. -func (p *proxyCollector) resourceKind() string { - return types.KindProxy +// resourceKinds specifies the resource kind to watch. +func (p *proxyCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindProxy}} } // getResourcesAndUpdateCurrent is called when the resources should be @@ -531,7 +543,7 @@ func NewLockWatcher(ctx context.Context, cfg LockWatcherConfig) (*LockWatcher, e } // Resource watcher require the fanout to be initialized before passing in. // Otherwise, Emit() may fail due to a race condition mentioned in https://github.com/gravitational/teleport/issues/19289 - collector.fanout.SetInit([]types.WatchKind{{Kind: collector.resourceKind()}}) + collector.fanout.SetInit(collector.resourceKinds()) watcher, err := newResourceWatcher(ctx, collector, cfg.ResourceWatcherConfig) if err != nil { return nil, trace.Wrap(err) @@ -628,9 +640,9 @@ func (p *lockCollector) GetCurrent() []types.Lock { return lockMapValues(p.current) } -// resourceKind specifies the resource kind to watch. -func (p *lockCollector) resourceKind() string { - return types.KindLock +// resourceKinds specifies the resource kind to watch. +func (p *lockCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindLock}} } // initializationChan is used to check that the cache has done its initial @@ -804,9 +816,9 @@ type databaseCollector struct { once sync.Once } -// resourceKind specifies the resource kind to watch. -func (p *databaseCollector) resourceKind() string { - return types.KindDatabase +// resourceKinds specifies the resource kind to watch. +func (p *databaseCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindDatabase}} } // isInitialized is used to check that the cache has done its initial @@ -944,9 +956,9 @@ type appCollector struct { once sync.Once } -// resourceKind specifies the resource kind to watch. -func (p *appCollector) resourceKind() string { - return types.KindApp +// resourceKinds specifies the resource kind to watch. +func (p *appCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindApp}} } // isInitialized is used to check that the cache has done its initial @@ -1098,9 +1110,9 @@ func (k *kubeCollector) initializationChan() <-chan struct{} { return k.initializationC } -// resourceKind specifies the resource kind to watch. -func (k *kubeCollector) resourceKind() string { - return types.KindKubernetesCluster +// resourceKinds specifies the resource kind to watch. +func (k *kubeCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindKubernetesCluster}} } // getResourcesAndUpdateCurrent refreshes the list of current resources. @@ -1292,9 +1304,9 @@ func (k *kubeServerCollector) initializationChan() <-chan struct{} { return k.initializationC } -// resourceKind specifies the resource kind to watch. -func (k *kubeServerCollector) resourceKind() string { - return types.KindKubeServer +// resourceKinds specifies the resource kind to watch. +func (k *kubeServerCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindKubeServer}} } // getResourcesAndUpdateCurrent refreshes the list of current resources. @@ -1450,7 +1462,7 @@ func NewCertAuthorityWatcher(ctx context.Context, cfg CertAuthorityWatcherConfig } // Resource watcher require the fanout to be initialized before passing in. // Otherwise, Emit() may fail due to a race condition mentioned in https://github.com/gravitational/teleport/issues/19289 - collector.fanout.SetInit([]types.WatchKind{{Kind: collector.resourceKind()}}) + collector.fanout.SetInit(collector.resourceKinds()) watcher, err := newResourceWatcher(ctx, collector, cfg.ResourceWatcherConfig) if err != nil { return nil, trace.Wrap(err) @@ -1484,7 +1496,7 @@ func (c *caCollector) Subscribe(ctx context.Context, filter types.CertAuthorityF watch := types.Watch{ Kinds: []types.WatchKind{ { - Kind: c.resourceKind(), + Kind: types.KindCertAuthority, Filter: filter.IntoMap(), }, }, @@ -1504,9 +1516,9 @@ func (c *caCollector) Subscribe(ctx context.Context, filter types.CertAuthorityF return sub, nil } -// resourceKind specifies the resource kind to watch. -func (c *caCollector) resourceKind() string { - return types.KindCertAuthority +// resourceKinds specifies the resource kind to watch. +func (c *caCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindCertAuthority}} } // isInitialized is used to check that the cache has done its initial @@ -1767,9 +1779,9 @@ func (n *nodeCollector) NodeCount() int { return len(n.current) } -// resourceKind specifies the resource kind to watch. -func (n *nodeCollector) resourceKind() string { - return types.KindNode +// resourceKinds specifies the resource kind to watch. +func (n *nodeCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindNode}} } // getResourcesAndUpdateCurrent is called when the resources should be @@ -1919,9 +1931,9 @@ type accessRequestCollector struct { once sync.Once } -// resourceKind specifies the resource kind to watch. -func (p *accessRequestCollector) resourceKind() string { - return types.KindAccessRequest +// resourceKinds specifies the resource kind to watch. +func (p *accessRequestCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindAccessRequest}} } // isInitialized is used to check that the cache has done its initial @@ -2082,9 +2094,9 @@ type oktaAssignmentCollector struct { once sync.Once } -// resourceKind specifies the resource kind to watch. -func (*oktaAssignmentCollector) resourceKind() string { - return types.KindOktaAssignment +// resourceKinds specifies the resource kind to watch. +func (*oktaAssignmentCollector) resourceKinds() []types.WatchKind { + return []types.WatchKind{{Kind: types.KindOktaAssignment}} } // initializationChan is used to check if the initial state sync has been completed. diff --git a/lib/services/watcher_test.go b/lib/services/watcher_test.go index 0224342021a01..04c9310e90265 100644 --- a/lib/services/watcher_test.go +++ b/lib/services/watcher_test.go @@ -714,7 +714,7 @@ func TestAppWatcher(t *testing.T) { } } -func newApp(t *testing.T, name string) types.Application { +func newApp(t *testing.T, name string) *types.AppV3 { app, err := types.NewAppV3(types.Metadata{ Name: name, }, types.AppSpecV3{ diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 34031c909d31f..b648fead1ca68 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -638,6 +638,9 @@ func (h *Handler) bindDefaultEndpoints() { // get namespaces h.GET("/webapi/sites/:site/namespaces", h.WithClusterAuth(h.getSiteNamespaces)) + // get unified resources + h.GET("/webapi/sites/:site/resources", h.WithClusterAuth(h.clusterUnifiedResourcesGet)) + // get nodes h.GET("/webapi/sites/:site/nodes", h.WithClusterAuth(h.clusterNodesGet)) h.POST("/webapi/sites/:site/nodes", h.WithClusterAuth(h.handleNodeCreate)) @@ -2444,6 +2447,151 @@ func (h *Handler) getSiteNamespaces(w http.ResponseWriter, r *http.Request, _ ht }, nil } +func makeUnifiedResourceRequest(r *http.Request) (*proto.ListUnifiedResourcesRequest, error) { + + values := r.URL.Query() + + limit, err := queryLimitAsInt32(values, "limit", defaults.MaxIterationLimit) + if err != nil { + return nil, trace.Wrap(err) + } + + sortBy := types.GetSortByFromString(values.Get("sort")) + + var kinds []string + for _, kind := range values["kinds"] { + if kind != "" { + kinds = append(kinds, kind) + } + } + + startKey := values.Get("startKey") + return &proto.ListUnifiedResourcesRequest{ + Kinds: kinds, + Limit: limit, + StartKey: startKey, + SortBy: sortBy, + PredicateExpression: values.Get("query"), + SearchKeywords: client.ParseSearchKeywords(values.Get("search"), ' '), + UseSearchAsRoles: values.Get("searchAsRoles") == "yes", + }, nil +} + +// clusterUnifiedResourcesGet returns a list of resources for a given cluster site. This includes all resources available to be displayed in the web ui +// such as Nodes, Apps, Desktops, etc etc +func (h *Handler) clusterUnifiedResourcesGet(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (interface{}, error) { + clt, err := sctx.GetUserClient(r.Context(), site) + if err != nil { + return nil, trace.Wrap(err) + } + + identity, err := sctx.GetIdentity() + if err != nil { + return nil, trace.Wrap(err) + } + + req, err := makeUnifiedResourceRequest(r) + if err != nil { + return nil, trace.Wrap(err) + } + + page, err := apiclient.ListUnifiedResourcePage(r.Context(), clt, req) + if err != nil { + return nil, trace.Wrap(err) + } + + accessChecker, err := sctx.GetUserAccessChecker() + if err != nil { + return nil, trace.Wrap(err) + } + + dbNames, dbUsers, err := getDatabaseUsersAndNames(accessChecker) + if err != nil { + return nil, trace.Wrap(err) + } + + userGroups, err := apiclient.GetAllResources[types.UserGroup](r.Context(), clt, &proto.ListResourcesRequest{ + ResourceType: types.KindUserGroup, + Namespace: apidefaults.Namespace, + UseSearchAsRoles: true, + }) + if err != nil { + h.log.Debugf("Unable to fetch user groups while listing applications, unable to display associated user groups: %v", err) + } + + userGroupLookup := make(map[string]types.UserGroup, len(userGroups)) + for _, userGroup := range userGroups { + userGroupLookup[userGroup.GetName()] = userGroup + } + + unifiedResources := make([]any, 0, len(page.Resources)) + for _, resource := range page.Resources { + switch r := resource.(type) { + case types.Server: + server, err := ui.MakeServer(site.GetName(), r, accessChecker) + if err != nil { + return nil, trace.Wrap(err) + } + unifiedResources = append(unifiedResources, server) + case types.DatabaseServer: + db := ui.MakeDatabase(r.GetDatabase(), dbUsers, dbNames) + unifiedResources = append(unifiedResources, db) + case types.AppServer: + app := ui.MakeApp(r.GetApp(), ui.MakeAppsConfig{ + LocalClusterName: h.auth.clusterName, + LocalProxyDNSName: h.proxyDNSName(), + AppClusterName: site.GetName(), + Identity: identity, + UserGroupLookup: userGroupLookup, + Logger: h.log, + }) + unifiedResources = append(unifiedResources, app) + case types.AppServerOrSAMLIdPServiceProvider: + if r.IsAppServer() { + app := ui.MakeApp(r.GetAppServer().GetApp(), ui.MakeAppsConfig{ + LocalClusterName: h.auth.clusterName, + LocalProxyDNSName: h.proxyDNSName(), + AppClusterName: site.GetName(), + Identity: identity, + UserGroupLookup: userGroupLookup, + Logger: h.log, + }) + unifiedResources = append(unifiedResources, app) + } else { + app := ui.MakeSAMLApp(r.GetSAMLIdPServiceProvider(), ui.MakeAppsConfig{ + LocalClusterName: h.auth.clusterName, + LocalProxyDNSName: h.proxyDNSName(), + AppClusterName: site.GetName(), + Identity: identity, + }) + unifiedResources = append(unifiedResources, app) + } + case types.WindowsDesktop: + desktop, err := ui.MakeDesktop(r, accessChecker) + if err != nil { + return nil, trace.Wrap(err) + } + unifiedResources = append(unifiedResources, desktop) + case types.KubeCluster: + kube := ui.MakeKubeCluster(r, accessChecker) + if err != nil { + return nil, trace.Wrap(err) + } + unifiedResources = append(unifiedResources, kube) + default: + return nil, trace.Errorf("UI Resource has unknown type: %T", resource) + } + } + + resp := listResourcesGetResponse{ + Items: unifiedResources, + StartKey: page.NextKey, + TotalCount: page.Total, + } + + return resp, nil +} + // clusterNodesGet returns a list of nodes for a given cluster site. func (h *Handler) clusterNodesGet(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (interface{}, error) { // Get a client to the Auth Server with the logged in user's identity. The diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 350e37d5b1452..9f5547f1fd793 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -1105,6 +1105,7 @@ func TestClusterNodesGet(t *testing.T) { require.Equal(t, 2, res.TotalCount) require.ElementsMatch(t, res.Items, []ui.Server{ { + Kind: types.KindNode, ClusterName: clusterName, Name: server1.GetName(), Hostname: server1.GetHostname(), @@ -1114,6 +1115,7 @@ func TestClusterNodesGet(t *testing.T) { SSHLogins: []string{pack.login}, }, { + Kind: types.KindNode, ClusterName: clusterName, Name: "server2", Labels: []ui.Label{{Name: "test-field", Value: "test-value"}}, @@ -1132,12 +1134,112 @@ func TestClusterNodesGet(t *testing.T) { require.Equal(t, res, res2) } +func TestUnifiedResourcesGet(t *testing.T) { + t.Parallel() + env := newWebPack(t, 1) + proxy := env.proxies[0] + pack := proxy.authPack(t, "test-user@example.com", nil) + + // Add nodes + for i := 0; i < 20; i++ { + name := fmt.Sprintf("server-%d", i) + node, err := types.NewServer(name, types.KindNode, types.ServerSpecV2{ + Hostname: name, + }) + require.NoError(t, err) + _, err = env.server.Auth().UpsertNode(context.Background(), node) + require.NoError(t, err) + } + // add db + db, err := types.NewDatabaseV3(types.Metadata{ + Name: "dbdb", + Labels: map[string]string{ + "env": "prod", + }, + }, types.DatabaseSpecV3{ + Protocol: "test-protocol", + URI: "test-uri", + }) + require.NoError(t, err) + dbServer, err := types.NewDatabaseServerV3(types.Metadata{ + Name: "dddb1", + }, types.DatabaseServerSpecV3{ + Hostname: "dddb1", + HostID: uuid.NewString(), + Database: db, + }) + require.NoError(t, err) + _, err = env.server.Auth().UpsertDatabaseServer(context.Background(), dbServer) + require.NoError(t, err) + + // add windows desktop + win, err := types.NewWindowsDesktopV3( + "zzzz9", + nil, + types.WindowsDesktopSpecV3{Addr: "localhost", HostID: "win1-host-id"}, + ) + require.NoError(t, err) + err = env.server.Auth().UpsertWindowsDesktop(context.Background(), win) + require.NoError(t, err) + + clusterName := env.server.ClusterName() + endpoint := pack.clt.Endpoint("webapi", "sites", clusterName, "resources") + + // test sort type ascend + query := url.Values{"sort": []string{"type:asc"}} + re, err := pack.clt.Get(context.Background(), endpoint, query) + require.NoError(t, err) + res := clusterNodesGetResponse{} + require.NoError(t, json.Unmarshal(re.Bytes(), &res)) + require.Equal(t, types.KindDatabase, res.Items[0].Kind) + + // test sort type desc + query = url.Values{"sort": []string{"type:desc"}} + re, err = pack.clt.Get(context.Background(), endpoint, query) + require.NoError(t, err) + res = clusterNodesGetResponse{} + require.NoError(t, json.Unmarshal(re.Bytes(), &res)) + require.Equal(t, types.KindWindowsDesktop, res.Items[0].Kind) + + // test with no access + noAccessRole, err := types.NewRole(services.RoleNameForUser("test-no-access@example.com"), types.RoleSpecV6{}) + require.NoError(t, err) + noAccessPack := proxy.authPack(t, "test-no-access@example.com", []types.Role{noAccessRole}) + + // shouldnt get any results with no access + query = url.Values{} + re, err = noAccessPack.clt.Get(context.Background(), endpoint, query) + require.NoError(t, err) + res = clusterNodesGetResponse{} + require.NoError(t, json.Unmarshal(re.Bytes(), &res)) + require.Len(t, res.Items, 0) + + // should return first page and have a second page + query = url.Values{"sort": []string{"name"}, "limit": []string{"15"}} + re, err = pack.clt.Get(context.Background(), endpoint, query) + require.NoError(t, err) + res = clusterNodesGetResponse{} + require.NoError(t, json.Unmarshal(re.Bytes(), &res)) + require.Len(t, res.Items, 15) + require.NotEqual(t, "", res.StartKey) + + // should return second page and have no third page + query = url.Values{"sort": []string{"name"}, "limit": []string{"15"}} + query.Add("startKey", res.StartKey) + re, err = pack.clt.Get(context.Background(), endpoint, query) + require.NoError(t, err) + res = clusterNodesGetResponse{} + require.NoError(t, json.Unmarshal(re.Bytes(), &res)) + require.Len(t, res.Items, 8) + require.Equal(t, "", res.StartKey) + +} + type clusterAlertsGetResponse struct { Alerts []types.ClusterAlert `json:"alerts"` } func TestClusterAlertsGet(t *testing.T) { - t.Parallel() env := newWebPack(t, 1) // generate alert @@ -3759,9 +3861,7 @@ func authExportTestByEndpoint(t *testing.T, endpointExport, authType string, exp } } -func TestClusterDatabasesGet(t *testing.T) { - t.Parallel() - +func TestClusterDatabasesGet_NoRole(t *testing.T) { env := newWebPack(t, 1) proxy := env.proxies[0] @@ -3770,18 +3870,62 @@ func TestClusterDatabasesGet(t *testing.T) { query := url.Values{"sort": []string{"name"}} endpoint := pack.clt.Endpoint("webapi", "sites", env.server.ClusterName(), "databases") - re, err := pack.clt.Get(context.Background(), endpoint, query) - require.NoError(t, err) type testResponse struct { Items []ui.Database `json:"items"` TotalCount int `json:"totalCount"` } - // No db registered. + // add db + db, err := types.NewDatabaseV3(types.Metadata{ + Name: "dbdb", + Labels: map[string]string{ + "env": "prod", + }, + }, types.DatabaseSpecV3{ + Protocol: "test-protocol", + URI: "test-uri:1234", + }) + require.NoError(t, err) + dbServer, err := types.NewDatabaseServerV3(types.Metadata{ + Name: "dddb1", + }, types.DatabaseServerSpecV3{ + Hostname: "dddb1", + HostID: uuid.NewString(), + Database: db, + }) + require.NoError(t, err) + + _, err = env.server.Auth().UpsertDatabaseServer(context.Background(), dbServer) + require.NoError(t, err) + + // Test without defined database names or users in role. + re, err := pack.clt.Get(context.Background(), endpoint, query) + require.NoError(t, err) + resp := testResponse{} require.NoError(t, json.Unmarshal(re.Bytes(), &resp)) - require.Len(t, resp.Items, 0) + require.Len(t, resp.Items, 1) + require.ElementsMatch(t, resp.Items, []ui.Database{{ + Kind: types.KindDatabase, + Name: "dbdb", + Type: types.DatabaseTypeSelfHosted, + Labels: []ui.Label{{Name: "env", Value: "prod"}}, + Protocol: "test-protocol", + Hostname: "test-uri", + URI: "test-uri:1234", + }}) +} + +func TestClusterDatabasesGet_WithRole(t *testing.T) { + env := newWebPack(t, 1) + + proxy := env.proxies[0] + + type testResponse struct { + Items []ui.Database `json:"items"` + TotalCount int `json:"totalCount"` + } // Register databases. db, err := types.NewDatabaseServerV3(types.Metadata{ @@ -3793,71 +3937,18 @@ func TestClusterDatabasesGet(t *testing.T) { Metadata: types.Metadata{ Name: "db1", Description: "test-description", - Labels: map[string]string{"test-field": "test-value"}, }, Spec: types.DatabaseSpecV3{ Protocol: "test-protocol", URI: "test-uri:1234", - AWS: types.AWS{ - IAMPolicyStatus: types.IAMPolicyStatus_IAM_POLICY_STATUS_SUCCESS, - Region: "us-west-2", - }, }, }, }) require.NoError(t, err) - db2, err := types.NewDatabaseServerV3(types.Metadata{ - Name: "dbServer2", - }, types.DatabaseServerSpecV3{ - Hostname: "test-hostname", - HostID: "test-hostID", - Database: &types.DatabaseV3{ - Metadata: types.Metadata{ - Name: "db2", - }, - Spec: types.DatabaseSpecV3{ - Protocol: "test-protocol", - URI: "test-uri:1234", - }, - }, - }) require.NoError(t, err) _, err = env.server.Auth().UpsertDatabaseServer(context.Background(), db) require.NoError(t, err) - _, err = env.server.Auth().UpsertDatabaseServer(context.Background(), db2) - require.NoError(t, err) - - // Test without defined database names or users in role. - re, err = pack.clt.Get(context.Background(), endpoint, query) - require.NoError(t, err) - - resp = testResponse{} - require.NoError(t, json.Unmarshal(re.Bytes(), &resp)) - require.Len(t, resp.Items, 2) - require.Equal(t, 2, resp.TotalCount) - require.ElementsMatch(t, resp.Items, []ui.Database{{ - Name: "db1", - Desc: "test-description", - Protocol: "test-protocol", - Type: types.DatabaseTypeRDS, - Labels: []ui.Label{{Name: "test-field", Value: "test-value"}}, - Hostname: "test-uri", - URI: "test-uri:1234", - AWS: &ui.AWS{ - AWS: types.AWS{ - IAMPolicyStatus: types.IAMPolicyStatus_IAM_POLICY_STATUS_SUCCESS, - Region: "us-west-2", - }, - }, - }, { - Name: "db2", - Type: types.DatabaseTypeSelfHosted, - Labels: []ui.Label{}, - Protocol: "test-protocol", - Hostname: "test-uri", - URI: "test-uri:1234", - }}) // Test with a role that defines database names and users. extraRole := &types.RoleV6{ @@ -3873,215 +3964,15 @@ func TestClusterDatabasesGet(t *testing.T) { }, } - pack = proxy.authPack(t, "test-user2@example.com", services.NewRoleSet(extraRole)) - endpoint = pack.clt.Endpoint("webapi", "sites", env.server.ClusterName(), "databases") - re, err = pack.clt.Get(context.Background(), endpoint, query) + query := url.Values{"sort": []string{"name"}} + pack := proxy.authPack(t, "test-user2@example.com", services.NewRoleSet(extraRole)) + endpoint := pack.clt.Endpoint("webapi", "sites", env.server.ClusterName(), "databases") + re, err := pack.clt.Get(context.Background(), endpoint, query) require.NoError(t, err) - resp = testResponse{} + resp := testResponse{} require.NoError(t, json.Unmarshal(re.Bytes(), &resp)) - require.Len(t, resp.Items, 2) - require.Equal(t, 2, resp.TotalCount) - require.ElementsMatch(t, resp.Items, []ui.Database{{ - Name: "db1", - Desc: "test-description", - Protocol: "test-protocol", - Type: types.DatabaseTypeRDS, - Labels: []ui.Label{{Name: "test-field", Value: "test-value"}}, - Hostname: "test-uri", - DatabaseUsers: []string{"user1"}, - DatabaseNames: []string{"name1"}, - URI: "test-uri:1234", - AWS: &ui.AWS{ - AWS: types.AWS{ - IAMPolicyStatus: types.IAMPolicyStatus_IAM_POLICY_STATUS_SUCCESS, - Region: "us-west-2", - }, - }, - }, { - Name: "db2", - Type: types.DatabaseTypeSelfHosted, - Labels: []ui.Label{}, - Protocol: "test-protocol", - Hostname: "test-uri", - DatabaseUsers: []string{"user1"}, - DatabaseNames: []string{"name1"}, - URI: "test-uri:1234", - }}) -} - -func TestClusterDatabaseGet(t *testing.T) { - env := newWebPack(t, 1) - ctx := context.Background() - - proxy := env.proxies[0] - - dbNames := []string{"db1", "db2"} - dbUsers := []string{"user1", "user2"} - - for _, tt := range []struct { - name string - preRegisterDB bool - databaseName string - userRoles func(*testing.T) []types.Role - expectedDBUsers []string - expectedDBNames []string - requireError require.ErrorAssertionFunc - }{ - { - name: "valid", - preRegisterDB: true, - databaseName: "valid", - requireError: require.NoError, - }, - { - name: "notfound", - preRegisterDB: true, - databaseName: "otherdb", - requireError: func(tt require.TestingT, err error, i ...interface{}) { - require.True(tt, trace.IsNotFound(err), "expected a not found error, got %v", err) - }, - }, - { - name: "notauthorized", - preRegisterDB: true, - databaseName: "notauthorized", - userRoles: func(tt *testing.T) []types.Role { - role, err := types.NewRole( - "myrole", - types.RoleSpecV6{ - Allow: types.RoleConditions{ - DatabaseLabels: types.Labels{ - "env": apiutils.Strings{"staging"}, - }, - }, - }, - ) - require.NoError(tt, err) - return []types.Role{role} - }, - requireError: func(tt require.TestingT, err error, i ...interface{}) { - require.True(tt, trace.IsNotFound(err), "expected a not found error, got %v", err) - }, - }, - { - name: "nodb", - preRegisterDB: false, - databaseName: "nodb", - userRoles: func(tt *testing.T) []types.Role { - roleWithDBName, err := types.NewRole( - "myroleWithDBName", - types.RoleSpecV6{ - Allow: types.RoleConditions{ - DatabaseLabels: types.Labels{ - "env": apiutils.Strings{"prod"}, - }, - DatabaseNames: dbNames, - }, - }, - ) - require.NoError(tt, err) - - return []types.Role{roleWithDBName} - }, - expectedDBNames: dbNames, - expectedDBUsers: dbUsers, - requireError: func(tt require.TestingT, err error, i ...interface{}) { - require.True(tt, trace.IsNotFound(err), "expected a not found error, got %v", err) - }, - }, - { - name: "authorizedDBNamesUsers", - preRegisterDB: true, - databaseName: "authorizedDBNamesUsers", - userRoles: func(tt *testing.T) []types.Role { - roleWithDBName, err := types.NewRole( - "myroleWithDBName", - types.RoleSpecV6{ - Allow: types.RoleConditions{ - DatabaseLabels: types.Labels{ - "env": apiutils.Strings{"prod"}, - }, - DatabaseNames: dbNames, - }, - }, - ) - require.NoError(tt, err) - - roleWithDBUser, err := types.NewRole( - "myroleWithDBUser", - types.RoleSpecV6{ - Allow: types.RoleConditions{ - DatabaseLabels: types.Labels{ - "env": apiutils.Strings{"prod"}, - }, - DatabaseUsers: dbUsers, - }, - }, - ) - require.NoError(tt, err) - - return []types.Role{roleWithDBUser, roleWithDBName} - }, - expectedDBNames: dbNames, - expectedDBUsers: dbUsers, - requireError: require.NoError, - }, - } { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - // Create default pre-registerDB - if tt.preRegisterDB { - db, err := types.NewDatabaseV3(types.Metadata{ - Name: tt.name, - Labels: map[string]string{ - "env": "prod", - }, - }, types.DatabaseSpecV3{ - Protocol: "test-protocol", - URI: "test-uri", - }) - require.NoError(t, err) - - dbServer, err := types.NewDatabaseServerV3(types.Metadata{ - Name: tt.name, - }, types.DatabaseServerSpecV3{ - Hostname: tt.name, - HostID: uuid.NewString(), - Database: db, - }) - require.NoError(t, err) - - _, err = env.server.Auth().UpsertDatabaseServer(context.Background(), dbServer) - require.NoError(t, err) - } - - var roles []types.Role - if tt.userRoles != nil { - roles = tt.userRoles(t) - } - - pack := proxy.authPack(t, tt.name+"_user@example.com", roles) - - endpoint := pack.clt.Endpoint("webapi", "sites", env.server.ClusterName(), "databases", tt.databaseName) - re, err := pack.clt.Get(ctx, endpoint, nil) - tt.requireError(t, err) - if err != nil { - return - } - - resp := ui.Database{} - require.NoError(t, json.Unmarshal(re.Bytes(), &resp)) - - require.Equal(t, tt.databaseName, resp.Name, "database name") - require.Equal(t, types.DatabaseTypeSelfHosted, resp.Type, "database type") - require.EqualValues(t, []ui.Label{{Name: "env", Value: "prod"}}, resp.Labels) - require.ElementsMatch(t, tt.expectedDBUsers, resp.DatabaseUsers) - require.ElementsMatch(t, tt.expectedDBNames, resp.DatabaseNames) - }) - } + require.Len(t, resp.Items, 1) } func TestClusterKubesGet(t *testing.T) { @@ -4383,6 +4274,7 @@ func TestClusterAppsGet(t *testing.T) { require.Len(t, resp.Items, 3) require.Equal(t, 3, resp.TotalCount) require.ElementsMatch(t, resp.Items, []ui.App{{ + Kind: types.KindAppServer, Name: "app1", Description: resource.Spec.App.GetDescription(), URI: resource.Spec.App.GetURI(), @@ -4393,6 +4285,7 @@ func TestClusterAppsGet(t *testing.T) { AWSConsole: true, UserGroups: []ui.UserGroupAndDescription{{Name: "ug1", Description: "ug1-description"}}, }, { + Kind: types.KindAppServer, Name: "app2", URI: "uri", Labels: []ui.Label{}, @@ -4401,6 +4294,7 @@ func TestClusterAppsGet(t *testing.T) { PublicAddr: "publicaddrs", AWSConsole: false, }, { + Kind: types.KindSAMLIdPServiceProvider, Name: "test-saml-app", Description: "SAML Application", URI: "", @@ -5822,12 +5716,14 @@ func TestClusterDesktopsGet(t *testing.T) { require.Len(t, resp.Items, 2) require.Equal(t, 2, resp.TotalCount) require.ElementsMatch(t, resp.Items, []ui.Desktop{{ + Kind: types.KindWindowsDesktop, OS: constants.WindowsOS, Name: "desktop1", Addr: "addr", Labels: []ui.Label{{Name: "test-field", Value: "test-value"}}, HostID: "host", }, { + Kind: types.KindWindowsDesktop, OS: constants.WindowsOS, Name: "desktop2", Addr: "addr", @@ -6940,6 +6836,7 @@ func TestCreateDatabase(t *testing.T) { result := ui.Database{} require.NoError(t, json.Unmarshal(resp.Bytes(), &result)) require.Equal(t, result, ui.Database{ + Kind: types.KindDatabase, Name: tt.req.Name, Protocol: tt.req.Protocol, Type: types.DatabaseTypeSelfHosted, @@ -7104,6 +7001,7 @@ func TestUpdateDatabase_NonErrors(t *testing.T) { CACert: &fakeValidTLSCert, }, expectedFields: ui.Database{ + Kind: types.KindDatabase, Name: databaseName, Protocol: dbProtocol, Type: "self-hosted", @@ -7118,6 +7016,7 @@ func TestUpdateDatabase_NonErrors(t *testing.T) { URI: "something-else:3306", }, expectedFields: ui.Database{ + Kind: types.KindDatabase, Name: databaseName, Protocol: dbProtocol, Type: "self-hosted", @@ -7140,6 +7039,7 @@ func TestUpdateDatabase_NonErrors(t *testing.T) { ResourceID: "db-1234", }, expectedFields: ui.Database{ + Kind: types.KindDatabase, Name: databaseName, Protocol: dbProtocol, Type: "rds", @@ -7168,6 +7068,7 @@ func TestUpdateDatabase_NonErrors(t *testing.T) { ResourceID: "db-1234", }, expectedFields: ui.Database{ + Kind: types.KindDatabase, Name: databaseName, Protocol: dbProtocol, Type: "rds", @@ -7200,6 +7101,7 @@ func TestUpdateDatabase_NonErrors(t *testing.T) { ResourceID: "db-0000", }, expectedFields: ui.Database{ + Kind: types.KindDatabase, Name: databaseName, Protocol: dbProtocol, Type: "rds", diff --git a/lib/web/ui/app.go b/lib/web/ui/app.go index 97056e0eaa984..a1d5070b3c625 100644 --- a/lib/web/ui/app.go +++ b/lib/web/ui/app.go @@ -18,6 +18,9 @@ package ui import ( "fmt" + "sort" + + "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/services" @@ -27,6 +30,8 @@ import ( // App describes an application type App struct { + // Kind is the kind of resource. Used to parse which kind in a list of unified resources in the UI + Kind string `json:"kind"` // Name is the name of the application. Name string `json:"name"` // Description is the app description. @@ -75,6 +80,72 @@ type MakeAppsConfig struct { AppServersAndSAMLIdPServiceProviders types.AppServersOrSAMLIdPServiceProviders // Identity is identity of the logged in user. Identity *tlsca.Identity + // UserGroupLookup is a map of user groups to provide to each App + UserGroupLookup map[string]types.UserGroup + // Logger is a logger used for debugging while making an app + Logger logrus.FieldLogger +} + +// MakeApp creates an application object for the WebUI. +func MakeApp(app types.Application, c MakeAppsConfig) App { + labels := makeLabels(app.GetAllLabels()) + fqdn := AssembleAppFQDN(c.LocalClusterName, c.LocalProxyDNSName, c.AppClusterName, app) + userGroups := c.AppsToUserGroups[app.GetName()] + appsToUserGroups := map[string]types.UserGroups{} + var ugs types.UserGroups + for _, userGroupName := range app.GetUserGroups() { + userGroup := c.UserGroupLookup[userGroupName] + if userGroup == nil { + c.Logger.Debugf("Unable to find user group %s when creating user groups, skipping", userGroupName) + continue + } + + ugs = append(ugs, userGroup) + } + sort.Sort(ugs) + appsToUserGroups[app.GetName()] = ugs + + userGroupAndDescriptions := make([]UserGroupAndDescription, len(userGroups)) + for i, userGroup := range userGroups { + userGroupAndDescriptions[i] = UserGroupAndDescription{ + Name: userGroup.GetName(), + Description: userGroup.GetMetadata().Description, + } + } + + resultApp := App{ + Kind: app.GetKind(), + Name: app.GetName(), + Description: app.GetDescription(), + URI: app.GetURI(), + PublicAddr: app.GetPublicAddr(), + Labels: labels, + ClusterID: c.AppClusterName, + FQDN: fqdn, + AWSConsole: app.IsAWSConsole(), + FriendlyName: services.FriendlyName(app), + UserGroups: userGroupAndDescriptions, + SAMLApp: false, + } + + return resultApp +} + +// MakeSAMLApp creates a SAMLIdPServiceProvider object for the WebUI. +func MakeSAMLApp(app types.SAMLIdPServiceProvider, c MakeAppsConfig) App { + labels := makeLabels(app.GetAllLabels()) + resultApp := App{ + Kind: app.GetKind(), + Name: app.GetName(), + Description: app.GetMetadata().Description, + PublicAddr: "", + Labels: labels, + ClusterID: c.AppClusterName, + FriendlyName: services.FriendlyName(app), + SAMLApp: true, + } + + return resultApp } // MakeApps creates application objects (either Application Servers or SAML IdP Service Provider) for the WebUI. @@ -97,6 +168,7 @@ func MakeApps(c MakeAppsConfig) []App { } resultApp := App{ + Kind: appOrSP.GetKind(), Name: appOrSP.GetName(), Description: appOrSP.GetDescription(), URI: app.GetURI(), @@ -119,6 +191,7 @@ func MakeApps(c MakeAppsConfig) []App { } else { labels := makeLabels(appOrSP.GetSAMLIdPServiceProvider().GetAllLabels()) resultApp := App{ + Kind: appOrSP.GetKind(), Name: appOrSP.GetName(), Description: appOrSP.GetDescription(), PublicAddr: appOrSP.GetPublicAddr(), diff --git a/lib/web/ui/app_test.go b/lib/web/ui/app_test.go index 9479bed6b8776..eea5a0ec17aa7 100644 --- a/lib/web/ui/app_test.go +++ b/lib/web/ui/app_test.go @@ -94,6 +94,7 @@ func TestMakeApps(t *testing.T) { }))}, expected: []App{ { + Kind: types.KindAppServer, Name: "1", URI: "1.com", PublicAddr: "1.com", @@ -102,6 +103,7 @@ func TestMakeApps(t *testing.T) { UserGroups: []UserGroupAndDescription{}, }, { + Kind: types.KindAppServer, Name: "2", Description: "group 2 friendly name", URI: "2.com", @@ -139,6 +141,7 @@ func TestMakeApps(t *testing.T) { }, expected: []App{ { + Kind: types.KindAppServer, Name: "1", URI: "1.com", PublicAddr: "1.com", @@ -150,6 +153,7 @@ func TestMakeApps(t *testing.T) { }, }, { + Kind: types.KindAppServer, Name: "2", Description: "group 2 friendly name", URI: "2.com", @@ -178,6 +182,7 @@ func TestMakeApps(t *testing.T) { })}, expected: []App{ { + Kind: types.KindSAMLIdPServiceProvider, Name: "grafana_saml", Description: "SAML Application", PublicAddr: "", diff --git a/lib/web/ui/server.go b/lib/web/ui/server.go index b7c6725f8d93c..f1ef49ad3d8eb 100644 --- a/lib/web/ui/server.go +++ b/lib/web/ui/server.go @@ -38,6 +38,8 @@ type Label struct { // Server describes a server for webapp type Server struct { + // Kind is the kind of resource. Used to parse which kind in a list of unified resources in the UI + Kind string `json:"kind"` // Tunnel indicates of this server is connected over a reverse tunnel. Tunnel bool `json:"tunnel"` // Name is this server name @@ -71,33 +73,38 @@ func (s sortedLabels) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +// MakeServer creates a server object for the web ui +func MakeServer(clusterName string, server types.Server, accessChecker services.AccessChecker) (Server, error) { + serverLabels := server.GetStaticLabels() + serverCmdLabels := server.GetCmdLabels() + uiLabels := makeLabels(serverLabels, transformCommandLabels(serverCmdLabels)) + + serverLogins, err := accessChecker.GetAllowedLoginsForResource(server) + if err != nil { + return Server{}, trace.Wrap(err) + } + + return Server{ + Kind: server.GetKind(), + ClusterName: clusterName, + Labels: uiLabels, + Name: server.GetName(), + Hostname: server.GetHostname(), + Addr: server.GetAddr(), + Tunnel: server.GetUseTunnel(), + SSHLogins: serverLogins, + }, nil +} + // MakeServers creates server objects for webapp func MakeServers(clusterName string, servers []types.Server, accessChecker services.AccessChecker) ([]Server, error) { uiServers := []Server{} - for _, server := range servers { - serverLabels := server.GetStaticLabels() - serverCmdLabels := server.GetCmdLabels() - uiLabels := makeLabels(serverLabels, transformCommandLabels(serverCmdLabels)) - - serverLogins, err := accessChecker.GetAllowedLoginsForResource(server) + for _, s := range servers { + server, err := MakeServer(clusterName, s, accessChecker) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(err, "making server for ui") } - - s := Server{ - ClusterName: clusterName, - Labels: uiLabels, - Name: server.GetName(), - Hostname: server.GetHostname(), - Addr: server.GetAddr(), - Tunnel: server.GetUseTunnel(), - SSHLogins: serverLogins, - } - if server.GetCloudMetadata() != nil { - s.AWS = server.GetCloudMetadata().AWS - } - - uiServers = append(uiServers, s) + uiServers = append(uiServers, server) } return uiServers, nil @@ -105,6 +112,8 @@ func MakeServers(clusterName string, servers []types.Server, accessChecker servi // KubeCluster describes a kube cluster. type KubeCluster struct { + // Kind is the kind of resource. Used to parse which kind in a list of unified resources in the UI + Kind string `json:"kind"` // Name is the name of the kube cluster. Name string `json:"name"` // Labels is a map of static and dynamic labels associated with an kube cluster. @@ -115,6 +124,21 @@ type KubeCluster struct { KubeGroups []string `json:"kubernetes_groups"` } +// MakeKubeCluster creates a kube cluster object for the web ui +func MakeKubeCluster(cluster types.KubeCluster, accessChecker services.AccessChecker) KubeCluster { + staticLabels := cluster.GetStaticLabels() + dynamicLabels := cluster.GetDynamicLabels() + uiLabels := makeLabels(staticLabels, transformCommandLabels(dynamicLabels)) + kubeUsers, kubeGroups := getAllowedKubeUsersAndGroupsForCluster(accessChecker, cluster) + return KubeCluster{ + Kind: cluster.GetKind(), + Name: cluster.GetName(), + Labels: uiLabels, + KubeUsers: kubeUsers, + KubeGroups: kubeGroups, + } +} + // MakeKubeClusters creates ui kube objects and returns a list. func MakeKubeClusters(clusters []types.KubeCluster, accessChecker services.AccessChecker) []KubeCluster { uiKubeClusters := make([]KubeCluster, 0, len(clusters)) @@ -233,6 +257,8 @@ func ConnectionDiagnosticTraceUIFromTypes(traces []*types.ConnectionDiagnosticTr // Database describes a database server. type Database struct { + // Kind is the kind of resource. Used to parse which kind in a list of unified resources in the UI + Kind string `json:"kind"` // Name is the name of the database. Name string `json:"name"` // Desc is the database description. @@ -274,6 +300,7 @@ func MakeDatabase(database types.Database, dbUsers, dbNames []string) Database { uiLabels := makeLabels(database.GetAllLabels()) db := Database{ + Kind: database.GetKind(), Name: database.GetName(), Desc: database.GetDescription(), Protocol: database.GetProtocol(), @@ -339,6 +366,8 @@ func MakeDatabaseServices(databaseServices []types.DatabaseService) []DatabaseSe // Desktop describes a desktop to pass to the ui. type Desktop struct { + // Kind is the kind of resource. Used to parse which kind in a list of unified resources in the UI + Kind string `json:"kind"` // OS is the os of this desktop. Should be one of constants.WindowsOS, constants.LinuxOS, or constants.DarwinOS. OS string `json:"os"` // Name is name (uuid) of the windows desktop. @@ -372,6 +401,7 @@ func MakeDesktop(windowsDesktop types.WindowsDesktop, accessChecker services.Acc } return Desktop{ + Kind: windowsDesktop.GetKind(), OS: constants.WindowsOS, Name: windowsDesktop.GetName(), Addr: stripRdpPort(windowsDesktop.GetAddr()),