diff --git a/api/proto/teleport/legacy/types/types.proto b/api/proto/teleport/legacy/types/types.proto index 42b6852923223..64f85e92956af 100644 --- a/api/proto/teleport/legacy/types/types.proto +++ b/api/proto/teleport/legacy/types/types.proto @@ -8025,12 +8025,14 @@ message OktaOptions { message AccessGraphSync { // AWS is a configuration for AWS Access Graph service poll service. repeated AccessGraphAWSSync AWS = 1 [(gogoproto.jsontag) = "aws,omitempty"]; - // PollInterval is the frequency at which to poll for AWS resources + // PollInterval is the frequency at which to poll for resources google.protobuf.Duration PollInterval = 2 [ (gogoproto.jsontag) = "poll_interval,omitempty", (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + // Azure is a configuration for Azure Access Graph service poll service. + repeated AccessGraphAzureSync Azure = 3 [(gogoproto.jsontag) = "azure,omitempty"]; } // AccessGraphAWSSync is a configuration for AWS Access Graph service poll service. @@ -8042,3 +8044,9 @@ message AccessGraphAWSSync { // Integration is the integration name used to generate credentials to interact with AWS APIs. string Integration = 4 [(gogoproto.jsontag) = "integration,omitempty"]; } + +// AccessGraphAzureSync is a configuration for Azure Access Graph service poll service. +message AccessGraphAzureSync { + string SubscriptionID = 2 [(gogoproto.jsontag) = "subscription_id,omitempty"]; + string Integration = 3 [(gogoproto.jsontag) = "integration,omitempty"]; +} diff --git a/api/types/discoveryconfig/derived.gen.go b/api/types/discoveryconfig/derived.gen.go index 9053fdd312473..0855486f8af8c 100644 --- a/api/types/discoveryconfig/derived.gen.go +++ b/api/types/discoveryconfig/derived.gen.go @@ -117,7 +117,8 @@ func deriveTeleportEqual_6(this, that *types.AccessGraphSync) bool { return (this == nil && that == nil) || this != nil && that != nil && deriveTeleportEqual_12(this.AWS, that.AWS) && - this.PollInterval == that.PollInterval + this.PollInterval == that.PollInterval && + deriveTeleportEqual_13(this.Azure, that.Azure) } // deriveTeleportEqual_7 returns whether this and that are equal. @@ -144,12 +145,12 @@ func deriveTeleportEqual_7(this, that map[string]string) bool { func deriveTeleportEqual_8(this, that *types.AWSMatcher) bool { return (this == nil && that == nil) || this != nil && that != nil && - deriveTeleportEqual_13(this.Types, that.Types) && - deriveTeleportEqual_13(this.Regions, that.Regions) && - deriveTeleportEqual_14(this.AssumeRole, that.AssumeRole) && - deriveTeleportEqual_15(this.Tags, that.Tags) && - deriveTeleportEqual_16(this.Params, that.Params) && - deriveTeleportEqual_17(this.SSM, that.SSM) && + deriveTeleportEqual_14(this.Types, that.Types) && + deriveTeleportEqual_14(this.Regions, that.Regions) && + deriveTeleportEqual_15(this.AssumeRole, that.AssumeRole) && + deriveTeleportEqual_16(this.Tags, that.Tags) && + deriveTeleportEqual_17(this.Params, that.Params) && + deriveTeleportEqual_18(this.SSM, that.SSM) && this.Integration == that.Integration && this.KubeAppDiscovery == that.KubeAppDiscovery && this.SetupAccessForARN == that.SetupAccessForARN @@ -159,34 +160,34 @@ func deriveTeleportEqual_8(this, that *types.AWSMatcher) bool { func deriveTeleportEqual_9(this, that *types.AzureMatcher) bool { return (this == nil && that == nil) || this != nil && that != nil && - deriveTeleportEqual_13(this.Subscriptions, that.Subscriptions) && - deriveTeleportEqual_13(this.ResourceGroups, that.ResourceGroups) && - deriveTeleportEqual_13(this.Types, that.Types) && - deriveTeleportEqual_13(this.Regions, that.Regions) && - deriveTeleportEqual_15(this.ResourceTags, that.ResourceTags) && - deriveTeleportEqual_16(this.Params, that.Params) + deriveTeleportEqual_14(this.Subscriptions, that.Subscriptions) && + deriveTeleportEqual_14(this.ResourceGroups, that.ResourceGroups) && + deriveTeleportEqual_14(this.Types, that.Types) && + deriveTeleportEqual_14(this.Regions, that.Regions) && + deriveTeleportEqual_16(this.ResourceTags, that.ResourceTags) && + deriveTeleportEqual_17(this.Params, that.Params) } // deriveTeleportEqual_10 returns whether this and that are equal. func deriveTeleportEqual_10(this, that *types.GCPMatcher) bool { return (this == nil && that == nil) || this != nil && that != nil && - deriveTeleportEqual_13(this.Types, that.Types) && - deriveTeleportEqual_13(this.Locations, that.Locations) && - deriveTeleportEqual_15(this.Tags, that.Tags) && - deriveTeleportEqual_13(this.ProjectIDs, that.ProjectIDs) && - deriveTeleportEqual_13(this.ServiceAccounts, that.ServiceAccounts) && - deriveTeleportEqual_16(this.Params, that.Params) && - deriveTeleportEqual_15(this.Labels, that.Labels) + deriveTeleportEqual_14(this.Types, that.Types) && + deriveTeleportEqual_14(this.Locations, that.Locations) && + deriveTeleportEqual_16(this.Tags, that.Tags) && + deriveTeleportEqual_14(this.ProjectIDs, that.ProjectIDs) && + deriveTeleportEqual_14(this.ServiceAccounts, that.ServiceAccounts) && + deriveTeleportEqual_17(this.Params, that.Params) && + deriveTeleportEqual_16(this.Labels, that.Labels) } // deriveTeleportEqual_11 returns whether this and that are equal. func deriveTeleportEqual_11(this, that *types.KubernetesMatcher) bool { return (this == nil && that == nil) || this != nil && that != nil && - deriveTeleportEqual_13(this.Types, that.Types) && - deriveTeleportEqual_13(this.Namespaces, that.Namespaces) && - deriveTeleportEqual_15(this.Labels, that.Labels) + deriveTeleportEqual_14(this.Types, that.Types) && + deriveTeleportEqual_14(this.Namespaces, that.Namespaces) && + deriveTeleportEqual_16(this.Labels, that.Labels) } // deriveTeleportEqual_12 returns whether this and that are equal. @@ -198,7 +199,7 @@ func deriveTeleportEqual_12(this, that []*types.AccessGraphAWSSync) bool { return false } for i := 0; i < len(this); i++ { - if !(deriveTeleportEqual_18(this[i], that[i])) { + if !(deriveTeleportEqual_19(this[i], that[i])) { return false } } @@ -206,7 +207,7 @@ func deriveTeleportEqual_12(this, that []*types.AccessGraphAWSSync) bool { } // deriveTeleportEqual_13 returns whether this and that are equal. -func deriveTeleportEqual_13(this, that []string) bool { +func deriveTeleportEqual_13(this, that []*types.AccessGraphAzureSync) bool { if this == nil || that == nil { return this == nil && that == nil } @@ -214,7 +215,7 @@ func deriveTeleportEqual_13(this, that []string) bool { return false } for i := 0; i < len(this); i++ { - if !(this[i] == that[i]) { + if !(deriveTeleportEqual_20(this[i], that[i])) { return false } } @@ -222,15 +223,31 @@ func deriveTeleportEqual_13(this, that []string) bool { } // deriveTeleportEqual_14 returns whether this and that are equal. -func deriveTeleportEqual_14(this, that *types.AssumeRole) bool { +func deriveTeleportEqual_14(this, that []string) bool { + if this == nil || that == nil { + return this == nil && that == nil + } + if len(this) != len(that) { + return false + } + for i := 0; i < len(this); i++ { + if !(this[i] == that[i]) { + return false + } + } + return true +} + +// deriveTeleportEqual_15 returns whether this and that are equal. +func deriveTeleportEqual_15(this, that *types.AssumeRole) bool { return (this == nil && that == nil) || this != nil && that != nil && this.RoleARN == that.RoleARN && this.ExternalID == that.ExternalID } -// deriveTeleportEqual_15 returns whether this and that are equal. -func deriveTeleportEqual_15(this, that map[string]utils.Strings) bool { +// deriveTeleportEqual_16 returns whether this and that are equal. +func deriveTeleportEqual_16(this, that map[string]utils.Strings) bool { if this == nil || that == nil { return this == nil && that == nil } @@ -242,15 +259,15 @@ func deriveTeleportEqual_15(this, that map[string]utils.Strings) bool { if !ok { return false } - if !(deriveTeleportEqual_13(v, thatv)) { + if !(deriveTeleportEqual_14(v, thatv)) { return false } } return true } -// deriveTeleportEqual_16 returns whether this and that are equal. -func deriveTeleportEqual_16(this, that *types.InstallerParams) bool { +// deriveTeleportEqual_17 returns whether this and that are equal. +func deriveTeleportEqual_17(this, that *types.InstallerParams) bool { return (this == nil && that == nil) || this != nil && that != nil && this.JoinMethod == that.JoinMethod && @@ -259,28 +276,37 @@ func deriveTeleportEqual_16(this, that *types.InstallerParams) bool { this.InstallTeleport == that.InstallTeleport && this.SSHDConfig == that.SSHDConfig && this.PublicProxyAddr == that.PublicProxyAddr && - deriveTeleportEqual_19(this.Azure, that.Azure) && + deriveTeleportEqual_21(this.Azure, that.Azure) && this.EnrollMode == that.EnrollMode } -// deriveTeleportEqual_17 returns whether this and that are equal. -func deriveTeleportEqual_17(this, that *types.AWSSSM) bool { +// deriveTeleportEqual_18 returns whether this and that are equal. +func deriveTeleportEqual_18(this, that *types.AWSSSM) bool { return (this == nil && that == nil) || this != nil && that != nil && this.DocumentName == that.DocumentName } -// deriveTeleportEqual_18 returns whether this and that are equal. -func deriveTeleportEqual_18(this, that *types.AccessGraphAWSSync) bool { +// deriveTeleportEqual_19 returns whether this and that are equal. +func deriveTeleportEqual_19(this, that *types.AccessGraphAWSSync) bool { return (this == nil && that == nil) || this != nil && that != nil && - deriveTeleportEqual_13(this.Regions, that.Regions) && - deriveTeleportEqual_14(this.AssumeRole, that.AssumeRole) && + deriveTeleportEqual_14(this.Regions, that.Regions) && + deriveTeleportEqual_15(this.AssumeRole, that.AssumeRole) && this.Integration == that.Integration } -// deriveTeleportEqual_19 returns whether this and that are equal. -func deriveTeleportEqual_19(this, that *types.AzureInstallerParams) bool { +// deriveTeleportEqual_20 returns whether this and that are equal. +func deriveTeleportEqual_20(this, that *types.AccessGraphAzureSync) bool { + return (this == nil && that == nil) || + this != nil && that != nil && + deriveTeleportEqual_14(this.Regions, that.Regions) && + this.SubscriptionID == that.SubscriptionID && + this.Integration == that.Integration +} + +// deriveTeleportEqual_21 returns whether this and that are equal. +func deriveTeleportEqual_21(this, that *types.AzureInstallerParams) bool { return (this == nil && that == nil) || this != nil && that != nil && this.ClientID == that.ClientID diff --git a/api/types/types.pb.go b/api/types/types.pb.go index 5a5743a778c11..05e442cedce16 100644 --- a/api/types/types.pb.go +++ b/api/types/types.pb.go @@ -21377,11 +21377,13 @@ var xxx_messageInfo_OktaOptions proto.InternalMessageInfo type AccessGraphSync struct { // AWS is a configuration for AWS Access Graph service poll service. AWS []*AccessGraphAWSSync `protobuf:"bytes,1,rep,name=AWS,proto3" json:"aws,omitempty"` - // PollInterval is the frequency at which to poll for AWS resources - PollInterval time.Duration `protobuf:"bytes,2,opt,name=PollInterval,proto3,stdduration" json:"poll_interval,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // PollInterval is the frequency at which to poll for resources + PollInterval time.Duration `protobuf:"bytes,2,opt,name=PollInterval,proto3,stdduration" json:"poll_interval,omitempty"` + // Azure is a configuration for Azure Access Graph service poll service. + Azure []*AccessGraphAzureSync `protobuf:"bytes,3,rep,name=Azure,proto3" json:"azure,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *AccessGraphSync) Reset() { *m = AccessGraphSync{} } @@ -21463,6 +21465,49 @@ func (m *AccessGraphAWSSync) XXX_DiscardUnknown() { var xxx_messageInfo_AccessGraphAWSSync proto.InternalMessageInfo +// AccessGraphAzureSync is a configuration for Azure Access Graph service poll service. +type AccessGraphAzureSync struct { + Regions []string `protobuf:"bytes,1,rep,name=Regions,proto3" json:"regions,omitempty"` + SubscriptionID string `protobuf:"bytes,2,opt,name=SubscriptionID,proto3" json:"subscription_id,omitempty"` + Integration string `protobuf:"bytes,3,opt,name=Integration,proto3" json:"integration,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AccessGraphAzureSync) Reset() { *m = AccessGraphAzureSync{} } +func (m *AccessGraphAzureSync) String() string { return proto.CompactTextString(m) } +func (*AccessGraphAzureSync) ProtoMessage() {} +func (*AccessGraphAzureSync) Descriptor() ([]byte, []int) { + return fileDescriptor_9198ee693835762e, []int{372} +} +func (m *AccessGraphAzureSync) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AccessGraphAzureSync) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AccessGraphAzureSync.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 *AccessGraphAzureSync) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccessGraphAzureSync.Merge(m, src) +} +func (m *AccessGraphAzureSync) XXX_Size() int { + return m.Size() +} +func (m *AccessGraphAzureSync) XXX_DiscardUnknown() { + xxx_messageInfo_AccessGraphAzureSync.DiscardUnknown(m) +} + +var xxx_messageInfo_AccessGraphAzureSync proto.InternalMessageInfo + func init() { proto.RegisterEnum("types.IAMPolicyStatus", IAMPolicyStatus_name, IAMPolicyStatus_value) proto.RegisterEnum("types.DatabaseTLSMode", DatabaseTLSMode_name, DatabaseTLSMode_value) @@ -21900,12 +21945,13 @@ func init() { proto.RegisterType((*OktaOptions)(nil), "types.OktaOptions") proto.RegisterType((*AccessGraphSync)(nil), "types.AccessGraphSync") proto.RegisterType((*AccessGraphAWSSync)(nil), "types.AccessGraphAWSSync") + proto.RegisterType((*AccessGraphAzureSync)(nil), "types.AccessGraphAzureSync") } func init() { proto.RegisterFile("teleport/legacy/types/types.proto", fileDescriptor_9198ee693835762e) } var fileDescriptor_9198ee693835762e = []byte{ - // 30381 bytes of a gzipped FileDescriptorProto + // 30427 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6d, 0x70, 0x1c, 0x49, 0x76, 0x20, 0x36, 0xdd, 0x8d, 0x8f, 0xc6, 0xc3, 0x57, 0x23, 0x01, 0x92, 0x20, 0x66, 0x86, 0xcd, 0xa9, 0x99, 0xe1, 0x90, 0xb3, 0x33, 0xe4, 0x12, 0xdc, 0xe1, 0xee, 0xec, 0x7c, 0x6d, 0xa3, 0x1b, @@ -23390,421 +23436,424 @@ var fileDescriptor_9198ee693835762e = []byte{ 0x93, 0xb5, 0x1a, 0xfa, 0x9c, 0xa8, 0x9f, 0x28, 0xd0, 0xc9, 0x25, 0x80, 0xe8, 0x15, 0x9f, 0xbf, 0xb9, 0x98, 0x0a, 0xe4, 0xf3, 0x43, 0xff, 0xe7, 0x2f, 0x16, 0x33, 0x4b, 0x00, 0x79, 0x19, 0x21, 0xc7, 0x58, 0x85, 0x8b, 0x3d, 0xd7, 0x3d, 0xb9, 0x06, 0x85, 0x5d, 0x5b, 0x68, 0xfd, 0xea, 0xfb, - 0x76, 0xbb, 0xfd, 0xff, 0xb3, 0xf7, 0x2d, 0x31, 0x6e, 0x64, 0xd7, 0xa1, 0x2a, 0x92, 0xdd, 0xcd, - 0x3e, 0xec, 0x4f, 0xf5, 0xd5, 0xa7, 0x7b, 0x5a, 0x1a, 0x69, 0x54, 0xa3, 0x91, 0x25, 0x8e, 0x67, - 0x6c, 0x69, 0xde, 0x78, 0x66, 0x6c, 0x8f, 0xc7, 0xd5, 0xec, 0xea, 0x26, 0x25, 0xfe, 0x5c, 0x45, - 0xb6, 0x2c, 0xcb, 0x76, 0xb9, 0x44, 0x56, 0x77, 0x97, 0xcd, 0x66, 0xd1, 0x2c, 0x72, 0x64, 0x19, - 0x0f, 0x78, 0x36, 0x1e, 0x60, 0x03, 0xef, 0x25, 0x71, 0xe2, 0x24, 0xc8, 0x20, 0x1b, 0x2f, 0x62, - 0x04, 0x59, 0x64, 0x1b, 0x24, 0x88, 0xb3, 0xf1, 0xce, 0x80, 0x61, 0xc0, 0x40, 0x76, 0x4e, 0x30, - 0x48, 0x06, 0x48, 0x80, 0x7c, 0x76, 0x41, 0xb2, 0xf0, 0x2a, 0xb8, 0xe7, 0xde, 0x5b, 0x75, 0xeb, - 0x43, 0xaa, 0xe5, 0x19, 0x27, 0x31, 0xe0, 0x55, 0x37, 0xcf, 0x3d, 0xe7, 0xd4, 0xfd, 0xdf, 0x73, - 0xcf, 0x3d, 0x1f, 0x77, 0xc0, 0x77, 0xdc, 0x75, 0x01, 0xaf, 0x30, 0x30, 0xe3, 0xac, 0xbd, 0x05, - 0xe7, 0xb2, 0x06, 0x9c, 0x5c, 0x85, 0x15, 0x39, 0x18, 0x10, 0x67, 0x52, 0x72, 0x46, 0x9e, 0x08, - 0x07, 0xc4, 0x19, 0xfc, 0x40, 0x81, 0x4b, 0xf3, 0xb6, 0x0f, 0xb2, 0x0d, 0xc5, 0xd1, 0xd8, 0xf3, - 0x51, 0x4c, 0xe5, 0xd9, 0x16, 0xc4, 0x6f, 0x4c, 0xa4, 0x80, 0xf2, 0xd4, 0xc4, 0x39, 0xe2, 0x0e, - 0x1e, 0xe6, 0x32, 0x42, 0x3a, 0xce, 0x51, 0x40, 0x5e, 0x84, 0x8d, 0xbe, 0x7b, 0xe8, 0x4c, 0x07, - 0x13, 0x3b, 0xe8, 0x1d, 0xbb, 0x7d, 0x74, 0xc1, 0x42, 0xc3, 0x3d, 0x53, 0xe5, 0x05, 0x96, 0x80, - 0xa7, 0x6a, 0xbc, 0x30, 0xa3, 0xc6, 0x77, 0x0a, 0x45, 0x45, 0xcd, 0x99, 0x68, 0x29, 0xa5, 0x7d, - 0x23, 0x07, 0x5b, 0xb3, 0xd6, 0x0b, 0x79, 0x33, 0xab, 0x0f, 0xd8, 0xc3, 0x85, 0x0c, 0x97, 0x1f, - 0x2e, 0xa4, 0xaf, 0x91, 0xdb, 0x10, 0x3a, 0x50, 0x3d, 0x29, 0x18, 0x82, 0x80, 0x51, 0x9a, 0x91, - 0x13, 0x04, 0x8f, 0xe8, 0x96, 0x90, 0x97, 0x02, 0xea, 0x72, 0x98, 0x4c, 0x23, 0x60, 0xe4, 0x35, - 0x80, 0xde, 0xc0, 0x0f, 0x5c, 0xb4, 0x0f, 0xe0, 0xb2, 0x06, 0x33, 0x0b, 0x0f, 0xa1, 0xf2, 0x83, - 0x30, 0x42, 0x2b, 0x7e, 0xdf, 0xe5, 0x03, 0xe8, 0xc0, 0xe6, 0x8c, 0x0d, 0x92, 0x0e, 0x4f, 0x94, - 0x9d, 0x5e, 0xe4, 0xba, 0x9a, 0x86, 0x39, 0xea, 0x93, 0x3d, 0x9e, 0x9b, 0x35, 0x47, 0x1e, 0x03, - 0x49, 0xef, 0x82, 0x94, 0x3b, 0x37, 0x6e, 0x9e, 0x8e, 0x43, 0xee, 0x0c, 0xd2, 0x1d, 0x0f, 0xc8, - 0x15, 0x28, 0x89, 0x5c, 0x96, 0x54, 0x96, 0x67, 0xcc, 0x81, 0x83, 0xee, 0xba, 0x38, 0x79, 0x30, - 0x62, 0x2a, 0xba, 0xc9, 0x71, 0x29, 0x61, 0x19, 0x21, 0x9d, 0xc7, 0x23, 0xd1, 0xba, 0x4b, 0x62, - 0x7e, 0xc7, 0xcf, 0x26, 0x5e, 0xfa, 0xfb, 0x8a, 0x18, 0xfe, 0xf4, 0xe6, 0xfe, 0xa4, 0xfa, 0x11, - 0x40, 0x2f, 0x25, 0x5e, 0x31, 0xfc, 0x9f, 0x4a, 0x2d, 0x62, 0xd5, 0x71, 0xa9, 0x85, 0xff, 0x24, - 0xd7, 0x61, 0x7d, 0xcc, 0xec, 0x58, 0x27, 0x3e, 0xef, 0x4f, 0x96, 0x37, 0x64, 0x95, 0x81, 0x3b, - 0x3e, 0xf6, 0x29, 0xaf, 0xd7, 0x9d, 0xb0, 0xc3, 0xa4, 0xb3, 0x8e, 0xbc, 0x0c, 0xcb, 0xf4, 0xac, - 0xc3, 0x48, 0x3b, 0x09, 0xf7, 0x08, 0xc4, 0x43, 0xc9, 0xc1, 0x2c, 0x7e, 0x99, 0xff, 0xcf, 0x79, - 0xbd, 0x93, 0x13, 0xcc, 0xe4, 0x93, 0x96, 0x6c, 0xc2, 0x92, 0x3f, 0x3e, 0x92, 0x9a, 0xb6, 0xe8, - 0x8f, 0x8f, 0x68, 0xbb, 0x6e, 0x80, 0xca, 0xbc, 0x75, 0x58, 0xd4, 0x84, 0xe0, 0xf1, 0x90, 0x5d, - 0xc5, 0x8b, 0xe6, 0x1a, 0x83, 0x63, 0xc2, 0xfe, 0xc7, 0xc3, 0x1e, 0xc5, 0x0c, 0x02, 0xdf, 0x96, - 0x03, 0x6c, 0xf1, 0x66, 0xaf, 0x05, 0x81, 0x1f, 0x45, 0xda, 0xea, 0x93, 0x1d, 0x58, 0xa5, 0x7c, - 0xc2, 0x30, 0x5f, 0x5c, 0x10, 0x78, 0x36, 0x2d, 0x08, 0x3c, 0x1e, 0xf6, 0x44, 0x15, 0xcd, 0x95, - 0x40, 0xfa, 0x45, 0xee, 0x82, 0x2a, 0x49, 0x4c, 0xe8, 0xbe, 0x99, 0xb0, 0xa9, 0x8e, 0xd8, 0x48, - 0x92, 0x56, 0x6d, 0x78, 0xe8, 0x9b, 0xeb, 0xbd, 0x38, 0x80, 0x77, 0xcd, 0xf7, 0x14, 0xb1, 0x97, - 0x66, 0x10, 0x11, 0x0d, 0x56, 0x8f, 0x9d, 0xc0, 0x0e, 0x82, 0x13, 0x66, 0x23, 0xc6, 0x03, 0x0b, - 0x97, 0x8e, 0x9d, 0xc0, 0x0a, 0x4e, 0x44, 0xe2, 0x92, 0xf3, 0x14, 0xc7, 0x77, 0xa6, 0x93, 0x63, - 0x5b, 0x96, 0xff, 0x58, 0x8f, 0x9d, 0x3d, 0x76, 0x82, 0x16, 0x2d, 0x93, 0x78, 0x93, 0x6b, 0xb0, - 0x86, 0x7c, 0x7b, 0x9e, 0x60, 0x8c, 0x91, 0x2f, 0xcc, 0x15, 0xca, 0xb8, 0xe7, 0x31, 0xce, 0xbc, - 0x86, 0xff, 0x94, 0x83, 0x0b, 0xd9, 0xbd, 0x83, 0xd3, 0x93, 0xf6, 0x29, 0xfa, 0xe8, 0xf1, 0xba, - 0x2d, 0x53, 0x08, 0x8b, 0x5a, 0x92, 0x35, 0x38, 0xb9, 0xcc, 0xc1, 0x29, 0xc3, 0x06, 0x32, 0xe2, - 0x92, 0xe6, 0xc0, 0x0b, 0x26, 0x3c, 0x18, 0x87, 0xb9, 0x4e, 0x0b, 0xd8, 0x7e, 0x5e, 0xa7, 0x60, - 0xf2, 0x02, 0xac, 0x89, 0x1d, 0xd9, 0x7f, 0x34, 0xa4, 0x1f, 0x66, 0xdb, 0xf1, 0x2a, 0x87, 0xb6, - 0x10, 0x48, 0xce, 0xc3, 0xa2, 0x33, 0x1a, 0xd1, 0x4f, 0xb2, 0x5d, 0x78, 0xc1, 0x19, 0x8d, 0x58, - 0x72, 0x1d, 0xf4, 0x48, 0xb4, 0x0f, 0xd1, 0x4a, 0x88, 0x9b, 0x24, 0x9a, 0x2b, 0x08, 0x64, 0x96, - 0x43, 0x01, 0x5d, 0xf7, 0x94, 0x56, 0xa0, 0x2c, 0x21, 0x0a, 0x38, 0xa3, 0x10, 0xe1, 0x19, 0x28, - 0x8a, 0xf7, 0x6a, 0xe6, 0x58, 0x61, 0x2e, 0x39, 0xfc, 0xad, 0xfa, 0x55, 0xd8, 0xec, 0x7b, 0x01, - 0x4e, 0x5e, 0xd6, 0xa4, 0xd1, 0x88, 0xfb, 0x40, 0xb2, 0x20, 0xbd, 0xe6, 0x39, 0x5e, 0x4c, 0x7b, - 0x52, 0x1f, 0x8d, 0x98, 0x27, 0x24, 0xef, 0xeb, 0xd7, 0x61, 0x9d, 0x4b, 0x5c, 0xfc, 0x88, 0xc4, - 0xba, 0xf0, 0x05, 0x4c, 0xaf, 0x42, 0x3c, 0x9d, 0x11, 0x70, 0x50, 0xad, 0x2f, 0x28, 0xff, 0x56, - 0x81, 0xf3, 0x99, 0x22, 0x1b, 0xf9, 0x12, 0x30, 0x97, 0xaf, 0x89, 0x6f, 0x8f, 0xdd, 0x9e, 0x37, - 0xf2, 0x30, 0x86, 0x06, 0x53, 0x69, 0xde, 0x9e, 0x27, 0xec, 0xa1, 0xfb, 0x58, 0xc7, 0x37, 0x43, - 0x22, 0xa6, 0x6b, 0x51, 0xc7, 0x09, 0xf0, 0xf6, 0x03, 0x38, 0x9f, 0x89, 0x9a, 0xa1, 0x03, 0xf9, - 0x70, 0x3c, 0x99, 0xb4, 0x78, 0xa4, 0x4a, 0x34, 0x5a, 0xd2, 0x8d, 0xf0, 0xe6, 0xfd, 0x30, 0x6c, - 0x5e, 0x42, 0xb8, 0x23, 0x46, 0x72, 0x5d, 0x67, 0xdd, 0x4f, 0x04, 0xd1, 0xec, 0xa5, 0xfd, 0x00, - 0xce, 0xf3, 0xc9, 0x77, 0x34, 0x76, 0x46, 0xc7, 0x11, 0x3b, 0x56, 0xd1, 0x0f, 0x65, 0xb1, 0x63, - 0xb3, 0x72, 0x9f, 0xe2, 0x87, 0x5c, 0xcf, 0x3a, 0x69, 0x20, 0x6f, 0xc3, 0x37, 0x73, 0x62, 0xa9, - 0x67, 0x54, 0x27, 0x63, 0x5a, 0x2b, 0x59, 0xd3, 0xfa, 0xf4, 0x6b, 0xaa, 0x09, 0x44, 0xde, 0xac, - 0x98, 0xd6, 0x93, 0x1b, 0x54, 0x09, 0x39, 0x9d, 0x57, 0x44, 0xda, 0x1a, 0x2c, 0x96, 0xcc, 0x73, - 0xa3, 0x97, 0x04, 0x91, 0x8b, 0xb0, 0x1c, 0xe6, 0xcb, 0xe6, 0x07, 0x47, 0x91, 0x01, 0x6a, 0x7d, - 0xf2, 0x1c, 0xac, 0x30, 0x91, 0x3c, 0xb6, 0xe6, 0x00, 0x61, 0x3a, 0x5d, 0x78, 0xa2, 0x0f, 0x14, - 0x78, 0xee, 0x49, 0x7d, 0x48, 0xee, 0xc1, 0x05, 0x34, 0xeb, 0x08, 0xfc, 0x70, 0x18, 0xec, 0x9e, - 0xd3, 0x3b, 0x76, 0xf9, 0xac, 0xd5, 0x32, 0x07, 0x63, 0x34, 0xb2, 0xac, 0x96, 0x34, 0x0e, 0xa3, - 0x91, 0x15, 0xf8, 0xe2, 0x77, 0x85, 0x92, 0xf3, 0x3a, 0xf4, 0xe1, 0xe2, 0x1c, 0x4a, 0x69, 0xe3, - 0x50, 0xe4, 0x8d, 0xe3, 0x06, 0xa8, 0x87, 0x6e, 0x9f, 0xca, 0xc4, 0x6e, 0x1f, 0xab, 0xf6, 0xf6, - 0x6d, 0x96, 0x21, 0xde, 0x5c, 0x0b, 0xe1, 0x56, 0xe0, 0x1f, 0xdc, 0xe6, 0x5f, 0x39, 0x11, 0x47, - 0x9e, 0x7c, 0xad, 0x20, 0x2f, 0xc3, 0xd9, 0x44, 0x7c, 0x92, 0xc8, 0xe1, 0xdd, 0xdc, 0xa0, 0x45, - 0xf1, 0x68, 0x56, 0x57, 0x61, 0x45, 0xcc, 0x8a, 0x71, 0xe8, 0x07, 0x67, 0x96, 0x38, 0x8c, 0xae, - 0x3a, 0xfe, 0xb9, 0xa9, 0x68, 0x54, 0xe6, 0x8d, 0xe4, 0x14, 0xb2, 0x34, 0x79, 0x09, 0x48, 0x28, - 0xb7, 0x87, 0x1b, 0x05, 0xff, 0xe0, 0x86, 0x28, 0x09, 0x57, 0x38, 0xff, 0xec, 0x5f, 0xe5, 0xe0, - 0x6c, 0xc6, 0x55, 0x86, 0x5e, 0x02, 0xbc, 0xe1, 0xc4, 0x3d, 0x62, 0x57, 0x08, 0xb9, 0x91, 0xeb, - 0x12, 0x9c, 0xeb, 0xa7, 0x16, 0x59, 0x06, 0x74, 0xfe, 0x2d, 0xfe, 0x8b, 0x6e, 0x1e, 0xce, 0x58, - 0xa8, 0x5e, 0xe8, 0xbf, 0xa4, 0x06, 0x1b, 0x98, 0xd6, 0x21, 0xf0, 0x7c, 0xcc, 0x0e, 0x81, 0x42, - 0x48, 0x21, 0x76, 0xd9, 0xc1, 0x5a, 0xb4, 0x25, 0x24, 0x2a, 0x85, 0x98, 0xea, 0x28, 0x01, 0x21, - 0x9f, 0x80, 0x6d, 0xe9, 0xac, 0xb1, 0x13, 0x2b, 0x0f, 0x2d, 0xdd, 0xcd, 0x4d, 0x27, 0x3c, 0x75, - 0x76, 0x63, 0x6b, 0x70, 0x07, 0x2e, 0xe3, 0x20, 0x7a, 0xfd, 0x91, 0x9d, 0xca, 0x03, 0x82, 0x4d, - 0x65, 0x81, 0xf3, 0xb7, 0x29, 0x56, 0xad, 0x3f, 0x4a, 0xa4, 0x04, 0xa1, 0xad, 0xe6, 0xdd, 0xf7, - 0x00, 0xce, 0x67, 0xd6, 0x98, 0x1e, 0x30, 0x68, 0x48, 0x15, 0xc9, 0x46, 0x4b, 0xf4, 0x37, 0x15, - 0x8e, 0xae, 0xc2, 0xca, 0x43, 0xd7, 0x19, 0xbb, 0x63, 0x7e, 0x72, 0xf3, 0x29, 0xc1, 0x60, 0xf2, - 0xc1, 0xdd, 0x8f, 0x0f, 0x0d, 0xd7, 0x19, 0x91, 0x06, 0x9c, 0x65, 0x27, 0xa0, 0x77, 0x82, 0xc2, - 0x20, 0xd7, 0x33, 0x29, 0x31, 0x71, 0x08, 0x49, 0xf0, 0x68, 0xaa, 0x21, 0x16, 0xa3, 0x36, 0x37, - 0x8e, 0x92, 0x20, 0xba, 0xa2, 0x2f, 0x64, 0x63, 0x93, 0x1d, 0x28, 0x31, 0xe6, 0xec, 0x5a, 0xc0, - 0x1e, 0x08, 0xae, 0xce, 0xfd, 0x42, 0x05, 0xed, 0x8b, 0x83, 0xf0, 0x7f, 0x7a, 0x5e, 0xe3, 0x5b, - 0xac, 0x7d, 0x22, 0xbf, 0x7f, 0x98, 0x2b, 0x08, 0xe4, 0xef, 0x1e, 0xda, 0x5f, 0x2b, 0xa2, 0xa9, - 0xb1, 0xcb, 0x31, 0x9d, 0x5a, 0x81, 0x3b, 0x14, 0x6f, 0x40, 0xcb, 0x26, 0xff, 0xf5, 0x94, 0x53, - 0x9d, 0xbc, 0x06, 0x2b, 0x94, 0xed, 0xd1, 0x74, 0xc8, 0xa6, 0x5c, 0x3e, 0x16, 0x97, 0xa7, 0xc1, - 0x8a, 0xe8, 0xb0, 0x55, 0xcf, 0x98, 0xa5, 0x93, 0xe8, 0x27, 0x95, 0x96, 0x83, 0x93, 0xc9, 0x48, - 0x9e, 0xa8, 0x42, 0x51, 0x68, 0x35, 0x3a, 0x6d, 0x4e, 0x52, 0xa4, 0x38, 0x91, 0xb4, 0xbc, 0xb3, - 0xc8, 0x54, 0x85, 0xda, 0x8b, 0x50, 0x92, 0x78, 0xd3, 0xc6, 0x30, 0xcf, 0x19, 0xd1, 0x18, 0xf6, - 0x8b, 0x0f, 0xf6, 0x43, 0x28, 0x0a, 0x96, 0xf4, 0x5a, 0x70, 0xec, 0x07, 0x62, 0x91, 0xe3, 0xff, - 0x14, 0x46, 0x7b, 0x19, 0x1b, 0xb9, 0x60, 0xe2, 0xff, 0x78, 0x96, 0x4c, 0x1c, 0x7a, 0x1f, 0x18, - 0x04, 0xf6, 0x08, 0x2d, 0xb0, 0x42, 0xe1, 0x99, 0xc2, 0x3b, 0x83, 0x80, 0xd9, 0x65, 0xf1, 0x6f, - 0xfc, 0x45, 0x78, 0x08, 0x27, 0xb4, 0x09, 0xb3, 0xf6, 0xcc, 0xd8, 0x91, 0x91, 0x4b, 0x1f, 0x19, - 0x2c, 0xde, 0x0a, 0xa7, 0x64, 0x5f, 0x06, 0x84, 0xe1, 0x91, 0x21, 0xed, 0x0c, 0x85, 0xd8, 0xce, - 0x20, 0xdd, 0xc9, 0xa3, 0xd1, 0x63, 0x27, 0x8e, 0xb8, 0x93, 0x27, 0xf7, 0xa9, 0x3f, 0xce, 0x09, - 0x15, 0xc1, 0x8e, 0xef, 0x4f, 0x82, 0xc9, 0xd8, 0x19, 0xc5, 0x54, 0xa1, 0xe4, 0x04, 0x9e, 0x41, - 0x09, 0xfa, 0x36, 0xa6, 0xd0, 0xf0, 0xc7, 0x22, 0xc4, 0x47, 0x38, 0x73, 0x4b, 0xb7, 0x3f, 0x12, - 0x97, 0xf1, 0x75, 0x8a, 0xad, 0xcb, 0xc8, 0x74, 0xc2, 0x4a, 0x5c, 0xab, 0x67, 0xcc, 0x4d, 0xc6, - 0x33, 0x85, 0x45, 0xaa, 0x19, 0x8b, 0x38, 0xa9, 0x0b, 0xdd, 0x89, 0x56, 0x74, 0x9c, 0xab, 0xbc, - 0xd6, 0xc9, 0xa7, 0x60, 0xd9, 0xeb, 0xcb, 0x99, 0x22, 0x93, 0x5a, 0xb8, 0x5a, 0x9f, 0x45, 0xab, - 0x8e, 0x78, 0xd0, 0x39, 0xe7, 0x71, 0xe8, 0xce, 0x6a, 0x4c, 0x69, 0xac, 0xed, 0x88, 0xdb, 0x68, - 0x9a, 0x8c, 0xac, 0x41, 0x2e, 0x1c, 0xe1, 0x9c, 0xd7, 0x67, 0xcb, 0x2b, 0x8a, 0x97, 0x6d, 0xf2, - 0x5f, 0xda, 0xff, 0x86, 0x1b, 0xa7, 0xed, 0x23, 0xba, 0x14, 0x67, 0x74, 0xf8, 0x32, 0x0b, 0x55, - 0x19, 0xef, 0xb7, 0xab, 0x20, 0x87, 0xfb, 0xf5, 0xc4, 0xe6, 0x27, 0x60, 0xdd, 0xb1, 0xa7, 0xfd, - 0x79, 0x1e, 0xd6, 0xe2, 0x6a, 0x72, 0xf2, 0x22, 0x14, 0xa4, 0x1d, 0x68, 0x33, 0x43, 0x97, 0x8e, - 0xfb, 0x0e, 0x22, 0x9d, 0x6a, 0xc7, 0x21, 0x77, 0x60, 0x0d, 0x0d, 0xf7, 0x50, 0xf4, 0x9c, 0x78, - 0xfc, 0xf1, 0x65, 0xfe, 0xfb, 0x59, 0xf1, 0x47, 0xef, 0x5e, 0x39, 0x83, 0x4f, 0x65, 0x2b, 0x94, - 0x96, 0x4a, 0x7f, 0xb4, 0x50, 0xd2, 0x82, 0x16, 0x66, 0x6b, 0x41, 0x79, 0x53, 0x66, 0x68, 0x41, - 0x17, 0xe6, 0x68, 0x41, 0x23, 0x4a, 0x59, 0x0b, 0x8a, 0xba, 0xf0, 0xa5, 0x59, 0xba, 0xf0, 0x88, - 0x86, 0xe9, 0xc2, 0x23, 0x2d, 0x66, 0x71, 0xa6, 0x16, 0x33, 0xa2, 0xe1, 0x5a, 0xcc, 0x6b, 0xbc, - 0x8f, 0xc6, 0xce, 0x23, 0x1b, 0x3b, 0x8f, 0x1f, 0x8b, 0xd8, 0x7a, 0xd3, 0x79, 0x84, 0xc6, 0x35, - 0x3b, 0xcb, 0x20, 0x2c, 0x72, 0xb4, 0xdf, 0x55, 0x12, 0x9a, 0x40, 0x31, 0x7e, 0x2f, 0xc0, 0x1a, - 0x3b, 0xac, 0x78, 0x38, 0x53, 0x76, 0x5a, 0xad, 0x9a, 0xab, 0x02, 0xca, 0xee, 0x9b, 0x1f, 0x82, - 0xf5, 0x10, 0x8d, 0x5f, 0xb9, 0xd0, 0x53, 0xcf, 0x0c, 0xa9, 0x79, 0xd8, 0x99, 0x17, 0x61, 0x23, - 0x44, 0xe4, 0xda, 0x1c, 0x76, 0xdd, 0x5c, 0x35, 0x55, 0x51, 0xd0, 0xe6, 0x70, 0xed, 0x28, 0x79, - 0xf3, 0xf8, 0x25, 0xd5, 0x4a, 0xfb, 0x61, 0x3e, 0xa6, 0x25, 0x11, 0x9f, 0xa1, 0xa7, 0x68, 0xe0, - 0xdb, 0xbc, 0x93, 0xf8, 0x5e, 0x74, 0x75, 0xc6, 0x98, 0x71, 0x9b, 0x26, 0xcb, 0x6a, 0x99, 0x10, - 0x04, 0xbe, 0x30, 0x71, 0xb2, 0x99, 0x44, 0xcd, 0xce, 0x7d, 0x9c, 0xb3, 0x82, 0x1d, 0xdb, 0x78, - 0xca, 0xf3, 0xd9, 0x89, 0x6b, 0x2a, 0x9d, 0xb2, 0x28, 0x59, 0x87, 0xbf, 0xc4, 0x07, 0xba, 0x80, - 0x4a, 0xc5, 0x20, 0xce, 0x3c, 0x9f, 0x71, 0x77, 0x4a, 0x31, 0xc7, 0x5e, 0x42, 0xce, 0xea, 0x54, - 0xfc, 0x2b, 0xd8, 0x1a, 0xb0, 0x82, 0x3a, 0x0a, 0xc1, 0xb0, 0x90, 0xa1, 0x82, 0x4f, 0x37, 0xbe, - 0x52, 0x6b, 0x98, 0x25, 0x4a, 0x27, 0xd8, 0x1c, 0xc3, 0x33, 0xb2, 0x66, 0x21, 0x5e, 0xc9, 0x05, - 0x11, 0xc5, 0x77, 0x6e, 0x0f, 0x44, 0x0a, 0x08, 0xac, 0xea, 0x05, 0x27, 0x0e, 0xe0, 0x68, 0xda, - 0x31, 0x6c, 0xcf, 0x1e, 0x92, 0x39, 0x19, 0xa2, 0xa2, 0x03, 0x34, 0x27, 0x1f, 0xa0, 0xb2, 0x9e, - 0x21, 0x1f, 0xd3, 0x33, 0x68, 0x7f, 0x94, 0x87, 0xe7, 0x4f, 0x31, 0x5c, 0x73, 0xbe, 0xf9, 0xe9, - 0xb8, 0x78, 0x96, 0x8b, 0xdd, 0x0c, 0x29, 0x53, 0xbe, 0x41, 0xd2, 0x5b, 0x6a, 0xb6, 0x70, 0xf6, - 0x25, 0x58, 0x67, 0xbb, 0x20, 0x33, 0x4b, 0x3c, 0x9c, 0x0e, 0x4e, 0xb1, 0x0d, 0x5e, 0x14, 0x3e, - 0x54, 0x09, 0x52, 0xdc, 0x19, 0x71, 0xc7, 0xb0, 0x42, 0x18, 0xe9, 0x40, 0x09, 0xd1, 0x0e, 0x1d, - 0x6f, 0x70, 0x2a, 0x67, 0x1e, 0xe1, 0xa1, 0x25, 0x93, 0x31, 0x6b, 0x6a, 0x0a, 0xd8, 0xc3, 0xdf, - 0xe4, 0x3a, 0xac, 0x0f, 0xa7, 0x27, 0x54, 0xf0, 0x60, 0x73, 0x81, 0x5b, 0x7f, 0x2c, 0x98, 0xab, - 0xc3, 0xe9, 0x89, 0x3e, 0x1a, 0xe1, 0x90, 0xa2, 0x99, 0xc8, 0x06, 0xc5, 0x63, 0xab, 0x56, 0x60, - 0x2e, 0x22, 0x26, 0x65, 0xc0, 0xd6, 0x2d, 0xc7, 0x3d, 0x07, 0xcc, 0x68, 0x90, 0x67, 0xc8, 0x62, - 0x3f, 0xb4, 0xff, 0xc8, 0x89, 0xfb, 0xee, 0xec, 0x79, 0xff, 0xeb, 0x21, 0xca, 0x18, 0xa2, 0x1b, - 0xa0, 0xd2, 0xae, 0x8f, 0x36, 0x95, 0x70, 0x8c, 0xd6, 0x86, 0xd3, 0x93, 0xb0, 0xef, 0xe4, 0x8e, - 0x5f, 0x94, 0x3b, 0xfe, 0x35, 0x71, 0x1f, 0xce, 0xdc, 0x1e, 0x66, 0x77, 0xb9, 0xf6, 0xaf, 0x79, - 0xb8, 0x7e, 0xba, 0x4d, 0xe0, 0xd7, 0xe3, 0x96, 0x31, 0x6e, 0x09, 0xd5, 0xe9, 0x42, 0x4a, 0x75, - 0x9a, 0xb1, 0xf6, 0x16, 0xb3, 0xd6, 0x5e, 0x4a, 0x51, 0xbb, 0x94, 0xa1, 0xa8, 0xcd, 0x5c, 0xa0, - 0xc5, 0x27, 0x2c, 0xd0, 0x65, 0x79, 0x9e, 0xfc, 0x63, 0xa8, 0xc0, 0x88, 0xdf, 0x07, 0x1e, 0xc0, - 0x59, 0x71, 0x1f, 0x60, 0x27, 0x47, 0xa4, 0x7f, 0x2f, 0xdd, 0xbe, 0x99, 0x75, 0x13, 0x40, 0xb4, - 0x0c, 0x69, 0x7d, 0x83, 0xdf, 0x01, 0xa2, 0xf2, 0xff, 0x39, 0xd2, 0x3f, 0xb9, 0x0f, 0x17, 0x30, - 0xbe, 0x7c, 0x4f, 0x7e, 0x39, 0xb0, 0xc7, 0xee, 0x21, 0x9f, 0x0f, 0x57, 0x53, 0xb2, 0xb2, 0xd7, - 0x93, 0xaa, 0x63, 0xba, 0x87, 0xd5, 0x33, 0xe6, 0xb9, 0x20, 0x03, 0x9e, 0xbc, 0x58, 0xfc, 0xa9, - 0x02, 0xda, 0x93, 0xfb, 0x0b, 0x15, 0x55, 0xc9, 0x0e, 0x5f, 0x36, 0x4b, 0x8e, 0xd4, 0x7b, 0xcf, - 0xc3, 0xea, 0xd8, 0x3d, 0x1c, 0xbb, 0xc1, 0x71, 0x4c, 0x03, 0xb2, 0xc2, 0x81, 0xa2, 0x63, 0x44, - 0x50, 0xca, 0xa7, 0x92, 0xcc, 0x05, 0x91, 0xb6, 0x17, 0xde, 0x17, 0x33, 0xc7, 0x81, 0xce, 0x26, - 0xb9, 0x82, 0xec, 0xc7, 0x9d, 0x42, 0x31, 0xa7, 0xe6, 0x4d, 0x1e, 0x3a, 0xf3, 0xd0, 0x1b, 0xb8, - 0xda, 0x5f, 0x2a, 0x42, 0x22, 0xc8, 0xea, 0x3c, 0xf2, 0x40, 0x32, 0xe6, 0xcd, 0xa7, 0xc4, 0x90, - 0x2c, 0x12, 0xd9, 0xee, 0x91, 0x87, 0x67, 0x44, 0x40, 0x2c, 0x3c, 0x23, 0x42, 0xde, 0x87, 0x45, - 0x22, 0xbf, 0x35, 0xbf, 0x21, 0x2c, 0x82, 0xe8, 0x9e, 0x77, 0x70, 0x8b, 0xdc, 0x84, 0x25, 0x66, - 0x04, 0x24, 0xaa, 0xbb, 0x1e, 0xab, 0xee, 0xc1, 0x2d, 0x53, 0x94, 0x6b, 0xef, 0x84, 0xef, 0x5a, - 0xa9, 0x46, 0x1c, 0xdc, 0x22, 0xaf, 0x9d, 0xce, 0x38, 0xb7, 0x28, 0x8c, 0x73, 0x43, 0xc3, 0xdc, - 0xd7, 0x63, 0x86, 0xb9, 0xd7, 0xe6, 0xf7, 0x16, 0x7f, 0x8d, 0x64, 0xe1, 0x08, 0xa3, 0x30, 0x55, - 0x3f, 0xcb, 0xc1, 0xb3, 0x73, 0x29, 0xc8, 0x25, 0x28, 0xea, 0xed, 0x5a, 0x27, 0x1a, 0x5f, 0xba, - 0x66, 0x04, 0x84, 0xec, 0xc3, 0xf2, 0x8e, 0x13, 0x78, 0x3d, 0x3a, 0x8d, 0x33, 0x9f, 0x07, 0x52, - 0x6c, 0x43, 0xf4, 0xea, 0x19, 0x33, 0xa2, 0x25, 0x36, 0x6c, 0xe0, 0x5a, 0x88, 0xa5, 0x9e, 0xca, - 0x67, 0xe8, 0x1a, 0x52, 0x0c, 0x53, 0x64, 0x74, 0x9f, 0x49, 0x01, 0xc9, 0x43, 0x20, 0x96, 0x55, - 0xad, 0xb8, 0xe3, 0x09, 0xbf, 0x83, 0x4f, 0xbc, 0xd0, 0xd2, 0xf3, 0xa3, 0x4f, 0xe8, 0xbb, 0x14, - 0x5d, 0xf5, 0x8c, 0x99, 0xc1, 0x2d, 0xb9, 0xcc, 0xdf, 0x16, 0xf2, 0xce, 0xec, 0x4e, 0x78, 0x8a, - 0x50, 0xaf, 0x37, 0xa0, 0xd8, 0x16, 0xb6, 0x08, 0x92, 0xc5, 0xbc, 0xb0, 0x3b, 0x30, 0xc3, 0x52, - 0xed, 0x37, 0x14, 0xa1, 0x74, 0x78, 0x72, 0x67, 0x49, 0x99, 0xc1, 0xfa, 0xf3, 0x33, 0x83, 0xf5, - 0x7f, 0xc1, 0xcc, 0x60, 0x9a, 0x07, 0x37, 0x4f, 0xdd, 0xb1, 0xe4, 0x93, 0xa0, 0x62, 0x12, 0x25, - 0x47, 0x1a, 0x24, 0xb6, 0xbe, 0x36, 0xc2, 0xd8, 0xdf, 0x55, 0x9e, 0xa9, 0xce, 0x5c, 0xef, 0xc5, - 0xa9, 0xb5, 0x3f, 0xe1, 0x31, 0xdf, 0x6b, 0xfd, 0x76, 0x42, 0xd1, 0xfc, 0x7e, 0x9d, 0x2c, 0x8c, - 0xd8, 0x62, 0x7b, 0x5e, 0x4a, 0x62, 0x99, 0xfe, 0xd6, 0x6c, 0x5f, 0x0b, 0x69, 0xe5, 0xfd, 0x41, - 0x1e, 0x2e, 0xcd, 0x23, 0xcf, 0x4c, 0x93, 0xad, 0x3c, 0x5d, 0x9a, 0xec, 0x9b, 0x50, 0x64, 0xb0, - 0xd0, 0x83, 0x00, 0xc7, 0x96, 0x93, 0xd2, 0xb1, 0x15, 0xc5, 0xe4, 0x79, 0x58, 0xd4, 0x2b, 0x56, - 0x94, 0xb9, 0x0d, 0x4d, 0x7d, 0x9d, 0x5e, 0x80, 0x46, 0xa4, 0xbc, 0x88, 0x7c, 0x31, 0x9d, 0xac, - 0x90, 0xa7, 0x6c, 0xbb, 0x28, 0x75, 0x48, 0x2a, 0x1d, 0x03, 0xd6, 0x37, 0x4a, 0x1f, 0xc0, 0x23, - 0x72, 0x9b, 0xe9, 0xc4, 0x87, 0x1a, 0x2c, 0xb6, 0xc7, 0x6e, 0xe0, 0x4e, 0x64, 0x33, 0xdc, 0x11, - 0x42, 0x4c, 0x5e, 0xc2, 0x8d, 0x64, 0x9d, 0xc7, 0x2c, 0x26, 0xc2, 0xa2, 0x1c, 0xa7, 0x06, 0xad, - 0x6a, 0x29, 0xd8, 0x94, 0x50, 0x28, 0x41, 0xdd, 0x99, 0x0e, 0x7b, 0xc7, 0x5d, 0xb3, 0xce, 0x25, - 0x27, 0x46, 0x30, 0x40, 0x28, 0x6d, 0x60, 0x60, 0x4a, 0x28, 0xda, 0xb7, 0x15, 0x38, 0x97, 0xd5, - 0x0e, 0x72, 0x09, 0x0a, 0xc3, 0xcc, 0xbc, 0x8c, 0x43, 0xe6, 0xca, 0x5d, 0xa2, 0x7f, 0xed, 0x43, - 0x7f, 0x7c, 0xe2, 0x4c, 0x64, 0x63, 0x65, 0x09, 0x6c, 0x02, 0xfd, 0xb1, 0x87, 0xff, 0x93, 0x2b, - 0xe2, 0xc8, 0xc9, 0xa7, 0x32, 0x39, 0xe2, 0x1f, 0x4d, 0x07, 0xa8, 0xf5, 0xdb, 0xad, 0x11, 0x4b, - 0x07, 0xf0, 0x0a, 0x14, 0x68, 0xb5, 0x12, 0xb3, 0x97, 0xce, 0x1f, 0xbd, 0x51, 0xe7, 0x48, 0xac, - 0x56, 0x81, 0x73, 0x32, 0x30, 0x11, 0x59, 0xbb, 0x07, 0x6b, 0x71, 0x0c, 0x62, 0xc4, 0x23, 0xc2, - 0x96, 0x6e, 0xab, 0x9c, 0xd3, 0x8e, 0xef, 0x33, 0x87, 0x99, 0x9d, 0x67, 0x7e, 0xf6, 0xee, 0x15, - 0xa0, 0x3f, 0x19, 0x4d, 0x56, 0xc4, 0x58, 0xed, 0x3b, 0x39, 0x38, 0x17, 0xf9, 0xe8, 0x8b, 0x35, - 0xf4, 0x2b, 0xeb, 0x30, 0xaa, 0xc7, 0x1c, 0x1a, 0x85, 0xdc, 0x98, 0x6e, 0xe0, 0x1c, 0x3f, 0xaa, - 0x7d, 0xd8, 0x9a, 0x85, 0x4f, 0x5e, 0x84, 0x65, 0x0c, 0xeb, 0x34, 0x72, 0x7a, 0xae, 0xbc, 0xcd, - 0x0e, 0x05, 0xd0, 0x8c, 0xca, 0xb5, 0x9f, 0x28, 0xb0, 0xcd, 0xdd, 0x3c, 0x1a, 0x8e, 0x37, 0xc4, - 0x57, 0x82, 0x9e, 0xfb, 0xc1, 0x38, 0x3c, 0xef, 0xc7, 0xf6, 0xb1, 0x17, 0xe2, 0xde, 0x3c, 0xa9, - 0xaf, 0xcd, 0x6e, 0x2d, 0xb9, 0x89, 0xa1, 0xca, 0xf8, 0x2b, 0x7a, 0x81, 0x05, 0x98, 0x18, 0x52, - 0x80, 0x1c, 0x60, 0x02, 0x31, 0xb4, 0xff, 0x03, 0x97, 0xe7, 0x7f, 0x80, 0x7c, 0x01, 0x56, 0x31, - 0xf7, 0x56, 0x77, 0x74, 0x34, 0x76, 0xfa, 0xae, 0xd0, 0xec, 0x09, 0x6d, 0xac, 0x5c, 0xc6, 0x22, - 0xaf, 0xf1, 0x80, 0x07, 0x47, 0x98, 0xd5, 0x8b, 0x13, 0xc5, 0x7c, 0xa9, 0x64, 0x6e, 0xda, 0x37, - 0x14, 0x20, 0x69, 0x1e, 0xe4, 0x63, 0xb0, 0xd2, 0xed, 0x54, 0xac, 0x89, 0x33, 0x9e, 0x54, 0xfd, - 0xe9, 0x98, 0x87, 0x3d, 0x63, 0xfe, 0xef, 0x93, 0x9e, 0xcd, 0xde, 0x83, 0x8e, 0xfd, 0xe9, 0xd8, - 0x8c, 0xe1, 0x61, 0x8e, 0x27, 0xd7, 0xfd, 0x4a, 0xdf, 0x79, 0x1c, 0xcf, 0xf1, 0xc4, 0x61, 0xb1, - 0x1c, 0x4f, 0x1c, 0xa6, 0x7d, 0x5f, 0x81, 0x8b, 0xc2, 0x38, 0xb2, 0x9f, 0x51, 0x97, 0x0a, 0x46, - 0x79, 0x19, 0x8b, 0x38, 0xbb, 0xf3, 0x24, 0xf4, 0x0d, 0x11, 0x08, 0x09, 0x2b, 0x88, 0xa2, 0x3a, - 0xa3, 0x25, 0x9f, 0x86, 0x82, 0x35, 0xf1, 0x47, 0xa7, 0x88, 0x84, 0xa4, 0x86, 0x23, 0x3a, 0xf1, - 0x47, 0xc8, 0x02, 0x29, 0x35, 0x17, 0xce, 0xc9, 0x95, 0x13, 0x35, 0x26, 0x0d, 0x58, 0xe2, 0x21, - 0xef, 0x12, 0x76, 0x07, 0x73, 0xda, 0xb4, 0xb3, 0x2e, 0xc2, 0x2d, 0xf1, 0x38, 0xaf, 0xa6, 0xe0, - 0xa1, 0xfd, 0x96, 0x02, 0x25, 0x2a, 0xd8, 0xe0, 0xa5, 0xf4, 0xfd, 0x4e, 0xe9, 0xb8, 0x1c, 0x2c, - 0xcc, 0x68, 0x42, 0xf6, 0xa7, 0x3a, 0x8d, 0x5f, 0x85, 0xf5, 0x04, 0x01, 0xd1, 0x30, 0xd0, 0xc6, - 0xc0, 0xeb, 0x39, 0x2c, 0x65, 0x0c, 0x33, 0x41, 0x89, 0xc1, 0xb4, 0xff, 0xa7, 0xc0, 0xb9, 0xd6, - 0x57, 0x26, 0x0e, 0x7b, 0xb6, 0x35, 0xa7, 0x03, 0xb1, 0xde, 0xa9, 0xb0, 0x26, 0xac, 0x6c, 0x59, - 0x10, 0x00, 0x26, 0xac, 0x71, 0x98, 0x19, 0x96, 0x92, 0x2a, 0x14, 0xf9, 0xf9, 0x12, 0xf0, 0xf0, - 0xac, 0x97, 0x25, 0xdd, 0x48, 0xc4, 0x98, 0x23, 0xd1, 0x96, 0xe0, 0x16, 0xc6, 0x69, 0xcc, 0x90, - 0x5a, 0xfb, 0x37, 0x05, 0x36, 0x67, 0xd0, 0x90, 0x37, 0x61, 0x01, 0x1d, 0x14, 0xf9, 0xe8, 0x5d, - 0x9a, 0xf1, 0x89, 0x49, 0xef, 0xf8, 0xe0, 0x16, 0x3b, 0x88, 0x4e, 0xe8, 0x0f, 0x93, 0x51, 0x91, - 0x07, 0xb0, 0xac, 0xf7, 0xfb, 0xfc, 0x76, 0x96, 0x8b, 0xdd, 0xce, 0x66, 0x7c, 0xf1, 0xe5, 0x10, - 0x9f, 0xdd, 0xce, 0x98, 0xab, 0x4c, 0xbf, 0x6f, 0x73, 0xe7, 0xcb, 0x88, 0xdf, 0xf6, 0x27, 0x61, - 0x2d, 0x8e, 0xfc, 0x54, 0xfe, 0x62, 0xef, 0x28, 0xa0, 0xc6, 0xeb, 0xf0, 0xcb, 0x09, 0x14, 0x95, - 0x35, 0xcc, 0x4f, 0x98, 0x54, 0xbf, 0x93, 0x83, 0xf3, 0x99, 0x3d, 0x4c, 0x5e, 0x82, 0x45, 0x7d, - 0x34, 0xaa, 0xed, 0xf2, 0x59, 0xc5, 0x25, 0x24, 0x54, 0x7a, 0xc7, 0x2e, 0xaf, 0x0c, 0x89, 0xbc, - 0x02, 0x45, 0x66, 0x1d, 0xb0, 0x2b, 0x36, 0x1c, 0x8c, 0x7c, 0xc3, 0x4d, 0x17, 0xe2, 0x81, 0x52, - 0x05, 0x22, 0xd9, 0x83, 0x35, 0x1e, 0x33, 0xc6, 0x74, 0x8f, 0xdc, 0xaf, 0x85, 0x11, 0xfb, 0x31, - 0xa9, 0x80, 0xd0, 0xa4, 0xdb, 0x63, 0x56, 0x26, 0x47, 0x4d, 0x89, 0x53, 0x91, 0x3a, 0xa8, 0xc8, - 0x53, 0xe6, 0xc4, 0xa2, 0xb5, 0x62, 0x14, 0x1f, 0x56, 0x89, 0x19, 0xbc, 0x52, 0x94, 0xe1, 0x70, - 0xe9, 0x41, 0xe0, 0x1d, 0x0d, 0x4f, 0xdc, 0xe1, 0xe4, 0x97, 0x37, 0x5c, 0xd1, 0x37, 0x4e, 0x35, - 0x5c, 0xbf, 0x57, 0x60, 0x8b, 0x39, 0x49, 0x46, 0x25, 0x1a, 0x29, 0x40, 0x37, 0x4a, 0x34, 0xf4, - 0x7e, 0xc6, 0xa3, 0xa2, 0xec, 0xc2, 0x12, 0x8b, 0x56, 0x23, 0x56, 0xc6, 0xb3, 0x99, 0x55, 0x60, - 0x38, 0x07, 0xb7, 0x98, 0xf8, 0xc2, 0x3c, 0x25, 0x03, 0x53, 0x90, 0x92, 0x03, 0x28, 0x55, 0x06, - 0xae, 0x33, 0x9c, 0x8e, 0x3a, 0xa7, 0x7b, 0x41, 0xdd, 0xe2, 0x6d, 0x59, 0xe9, 0x31, 0x32, 0x7c, - 0x79, 0xc5, 0x9d, 0x5c, 0x66, 0x44, 0x3a, 0xa1, 0xf3, 0x54, 0x01, 0x15, 0xaf, 0x1f, 0x9d, 0xd3, - 0x3f, 0x49, 0x20, 0xd2, 0xc5, 0x3d, 0x03, 0xb9, 0x77, 0x95, 0x0d, 0x6b, 0x75, 0x27, 0x98, 0x74, - 0xc6, 0xce, 0x30, 0xc0, 0x28, 0x97, 0xa7, 0x88, 0x02, 0x76, 0x51, 0x64, 0x70, 0x46, 0x95, 0xe9, - 0x24, 0x24, 0x65, 0x0a, 0xd9, 0x38, 0x3b, 0x2a, 0x2f, 0xed, 0x79, 0x43, 0x67, 0xe0, 0x7d, 0x5d, - 0xf8, 0x98, 0x32, 0x79, 0xe9, 0x50, 0x00, 0xcd, 0xa8, 0x5c, 0xfb, 0x7c, 0x6a, 0xdc, 0x58, 0x2d, - 0x4b, 0xb0, 0xc4, 0x23, 0x10, 0x30, 0x8f, 0xfc, 0xb6, 0xd1, 0xdc, 0xad, 0x35, 0xf7, 0x55, 0x85, - 0xac, 0x01, 0xb4, 0xcd, 0x56, 0xc5, 0xb0, 0x2c, 0xfa, 0x3b, 0x47, 0x7f, 0x73, 0x77, 0xfd, 0xbd, - 0x6e, 0x5d, 0xcd, 0x4b, 0x1e, 0xfb, 0x05, 0xed, 0xc7, 0x0a, 0x5c, 0xc8, 0x1e, 0x4a, 0xd2, 0x01, - 0x8c, 0xd9, 0xc0, 0xdf, 0xd2, 0x3f, 0x36, 0x77, 0xdc, 0x33, 0xc1, 0xc9, 0xd8, 0x0f, 0x13, 0x16, - 0x53, 0x20, 0x27, 0xde, 0xbe, 0x98, 0x93, 0xa2, 0xd7, 0x37, 0x73, 0x5e, 0x5f, 0xab, 0xc0, 0xd6, - 0x2c, 0x1e, 0xf1, 0xa6, 0xae, 0x43, 0x49, 0x6f, 0xb7, 0xeb, 0xb5, 0x8a, 0xde, 0xa9, 0xb5, 0x9a, - 0xaa, 0x42, 0x96, 0x61, 0x61, 0xdf, 0x6c, 0x75, 0xdb, 0x6a, 0x4e, 0xfb, 0xae, 0x02, 0xab, 0xb5, - 0xc8, 0xea, 0xec, 0xfd, 0x2e, 0xbe, 0x8f, 0xc7, 0x16, 0xdf, 0x56, 0x18, 0xdd, 0x24, 0xfc, 0xc0, - 0xa9, 0x56, 0xde, 0x7b, 0x39, 0xd8, 0x48, 0xd1, 0x10, 0x0b, 0x96, 0xf4, 0x7b, 0x56, 0xab, 0xb6, - 0x5b, 0xe1, 0x35, 0xbb, 0x12, 0x99, 0x4b, 0x61, 0xbe, 0xab, 0xd4, 0x57, 0x98, 0x47, 0xf0, 0xa3, - 0xc0, 0xf6, 0xbd, 0xbe, 0x94, 0xfc, 0xb6, 0x7a, 0xc6, 0x14, 0x9c, 0xf0, 0x24, 0xfb, 0xfa, 0x74, - 0xec, 0x22, 0xdb, 0x5c, 0x4c, 0xaf, 0x1b, 0xc2, 0xd3, 0x8c, 0xd1, 0x7f, 0xc3, 0xa1, 0xe5, 0x69, - 0xd6, 0x11, 0x3f, 0xd2, 0x84, 0xc5, 0x7d, 0x6f, 0x52, 0x9d, 0x3e, 0xe4, 0xeb, 0xf7, 0x72, 0x94, - 0xfd, 0xa8, 0x3a, 0x7d, 0x98, 0x66, 0x8b, 0x2a, 0x4b, 0x16, 0xbd, 0x27, 0xc6, 0x92, 0x73, 0x49, - 0x3a, 0x31, 0x16, 0x9e, 0xca, 0x89, 0x71, 0x67, 0x15, 0x4a, 0xfc, 0x0e, 0x85, 0xd7, 0x93, 0x1f, - 0x2a, 0xb0, 0x35, 0xab, 0xe7, 0xe8, 0xb5, 0x2c, 0x1e, 0xac, 0xe0, 0x42, 0x98, 0x1e, 0x23, 0x1e, - 0xa5, 0x40, 0xa0, 0x91, 0xb7, 0xa0, 0x54, 0x0b, 0x82, 0xa9, 0x3b, 0xb6, 0x5e, 0xe9, 0x9a, 0x35, - 0x3e, 0x5d, 0x9f, 0xfd, 0xe7, 0x77, 0xaf, 0x6c, 0xa2, 0xcf, 0xc7, 0xd8, 0x0e, 0x5e, 0xb1, 0xa7, - 0x63, 0x2f, 0x96, 0x4a, 0x40, 0xa6, 0xa0, 0x52, 0xb4, 0x33, 0xed, 0x7b, 0xae, 0xb8, 0x43, 0x08, - 0x87, 0x6e, 0x0e, 0x93, 0xcf, 0x34, 0x01, 0xd3, 0xbe, 0xa5, 0xc0, 0xf6, 0xec, 0x61, 0xa2, 0xe7, - 0x64, 0x87, 0x99, 0x54, 0x09, 0x97, 0x6a, 0x3c, 0x27, 0x43, 0xbb, 0x2b, 0x99, 0xa7, 0x40, 0xa4, - 0x44, 0x61, 0x6a, 0xfc, 0x5c, 0x2a, 0x1f, 0x76, 0x9c, 0x48, 0x20, 0x6a, 0xf7, 0x61, 0x73, 0xc6, - 0xa0, 0x92, 0x4f, 0x65, 0x26, 0xdd, 0x41, 0x37, 0x25, 0x39, 0xe9, 0x4e, 0x2c, 0x7b, 0x9b, 0x04, - 0xd7, 0xfe, 0x25, 0x07, 0x17, 0xe8, 0xea, 0x1a, 0xb8, 0x41, 0xa0, 0x47, 0xf9, 0x69, 0xe9, 0xae, - 0xf8, 0x1a, 0x2c, 0x1e, 0x3f, 0x9d, 0xaa, 0x98, 0xa1, 0x13, 0x02, 0x78, 0x62, 0x09, 0xe7, 0x18, - 0xfa, 0x3f, 0xb9, 0x0a, 0x72, 0x72, 0xf3, 0x3c, 0x86, 0x37, 0xcd, 0x6d, 0x29, 0xe6, 0xf2, 0x28, - 0xcc, 0x43, 0xfc, 0x3a, 0x2c, 0xa0, 0x3e, 0x85, 0x9f, 0x1d, 0x42, 0xe6, 0xcf, 0xae, 0x1d, 0x6a, - 0x5b, 0x4c, 0x46, 0x40, 0x3e, 0x02, 0x10, 0x65, 0x86, 0xe0, 0x87, 0x83, 0xd0, 0x33, 0x84, 0xc9, - 0x21, 0xcc, 0xe5, 0x93, 0x43, 0x87, 0xa7, 0x5b, 0x28, 0xc3, 0x86, 0xe8, 0xf1, 0x91, 0x88, 0x8a, - 0xc8, 0x5f, 0x31, 0xd7, 0x59, 0x41, 0x6d, 0x24, 0x22, 0x23, 0x5e, 0x4b, 0x25, 0x68, 0xc6, 0xe0, - 0xc8, 0x89, 0x2c, 0xcc, 0xd7, 0x52, 0x59, 0x98, 0x8b, 0x0c, 0x4b, 0x4e, 0xb5, 0xac, 0xfd, 0x43, - 0x0e, 0x96, 0xef, 0x51, 0xa9, 0x0c, 0x75, 0x0d, 0xf3, 0x75, 0x17, 0xb7, 0xa1, 0x54, 0xf7, 0x1d, - 0xfe, 0x5c, 0xc4, 0x7d, 0x4a, 0x98, 0x4f, 0xf7, 0xc0, 0x77, 0xc4, 0xcb, 0x53, 0x60, 0xca, 0x48, - 0x4f, 0xf0, 0x47, 0xbf, 0x03, 0x8b, 0xec, 0xf9, 0x8e, 0xab, 0xd1, 0x84, 0x5c, 0x1e, 0xd6, 0xe8, - 0x65, 0x56, 0x2c, 0xbd, 0x70, 0xb0, 0x27, 0x40, 0x59, 0x48, 0xe4, 0x31, 0x5e, 0x25, 0xcd, 0xca, - 0xc2, 0xe9, 0x34, 0x2b, 0x52, 0x2c, 0xbb, 0xc5, 0xd3, 0xc4, 0xb2, 0xdb, 0x7e, 0x03, 0x4a, 0x52, - 0x7d, 0x9e, 0x4a, 0x4c, 0xff, 0x66, 0x0e, 0x56, 0xb1, 0x55, 0xa1, 0x2d, 0xcf, 0xaf, 0xa6, 0x9e, - 0xe8, 0xe3, 0x31, 0x3d, 0xd1, 0x96, 0x3c, 0x5e, 0xac, 0x65, 0x73, 0x14, 0x44, 0x77, 0x60, 0x23, - 0x85, 0x48, 0x5e, 0x85, 0x05, 0x5a, 0x7d, 0x71, 0xaf, 0x56, 0x93, 0x33, 0x20, 0x8a, 0x7b, 0x4c, - 0x1b, 0x1e, 0x98, 0x0c, 0x5b, 0xfb, 0x77, 0x05, 0x56, 0x78, 0xda, 0x91, 0xe1, 0xa1, 0xff, 0xc4, - 0xee, 0xbc, 0x9e, 0xec, 0x4e, 0x16, 0x5d, 0x85, 0x77, 0xe7, 0x7f, 0x75, 0x27, 0xbe, 0x11, 0xeb, - 0xc4, 0xcd, 0x30, 0x0a, 0xa2, 0x68, 0xce, 0x9c, 0x3e, 0xfc, 0x01, 0xc6, 0x05, 0x8e, 0x23, 0x92, - 0x2f, 0xc2, 0x72, 0xd3, 0x7d, 0x14, 0xbb, 0x9e, 0x5e, 0x9f, 0xc1, 0xf4, 0xe5, 0x10, 0x91, 0xad, - 0x29, 0x3c, 0xd9, 0x87, 0xee, 0x23, 0x3b, 0xf5, 0x72, 0x18, 0xb1, 0xa4, 0x37, 0xd4, 0x38, 0xd9, - 0xd3, 0x4c, 0x7d, 0xee, 0xe0, 0x8a, 0x01, 0x83, 0xbe, 0x9d, 0x07, 0x88, 0x7c, 0x03, 0xe9, 0x02, - 0x8c, 0x19, 0x4d, 0x08, 0xcd, 0x3e, 0x82, 0xe4, 0x39, 0x2e, 0x6c, 0x29, 0xae, 0x73, 0x0d, 0x74, - 0x6e, 0x76, 0x94, 0x4a, 0xd4, 0x45, 0x57, 0xb8, 0x33, 0x5a, 0xdf, 0x1d, 0x38, 0x6c, 0x6f, 0xcf, - 0xef, 0x5c, 0xc3, 0xa0, 0xc4, 0x21, 0x74, 0x46, 0xba, 0x69, 0x74, 0x59, 0xdb, 0xa5, 0x08, 0x29, - 0x7f, 0xdb, 0xc2, 0xd3, 0xf9, 0xdb, 0xb6, 0x61, 0xd9, 0x1b, 0xbe, 0xed, 0x0e, 0x27, 0xfe, 0xf8, - 0x31, 0xaa, 0xdd, 0x23, 0x7d, 0x1e, 0xed, 0x82, 0x9a, 0x28, 0x63, 0xe3, 0x80, 0x67, 0x6e, 0x88, - 0x2f, 0x0f, 0x43, 0x08, 0x0c, 0xfd, 0x85, 0x17, 0xd4, 0xc5, 0x3b, 0x85, 0xe2, 0xa2, 0xba, 0x74, - 0xa7, 0x50, 0x2c, 0xaa, 0xcb, 0x77, 0x0a, 0xc5, 0x65, 0x15, 0x4c, 0xe9, 0xcd, 0x2c, 0x7c, 0x13, + 0x76, 0xbb, 0xfd, 0xff, 0xb3, 0xf7, 0x6d, 0x31, 0x6e, 0x24, 0xd7, 0xa1, 0x6a, 0x92, 0x33, 0xc3, + 0x39, 0x9c, 0x47, 0x4f, 0xe9, 0x31, 0xb3, 0x92, 0x56, 0x5a, 0xf5, 0x6a, 0x65, 0x89, 0xeb, 0x5d, + 0x5b, 0xda, 0xbb, 0xde, 0x5d, 0xdb, 0xeb, 0x75, 0x0f, 0xa7, 0x67, 0x48, 0x89, 0x2f, 0x77, 0x93, + 0x23, 0xcb, 0xb2, 0xdd, 0x6e, 0x91, 0x3d, 0x33, 0x6d, 0x73, 0xd8, 0x34, 0x9b, 0x5c, 0x59, 0xc6, + 0x05, 0xae, 0x8d, 0x0b, 0xd8, 0xc0, 0xbd, 0x49, 0x9c, 0x38, 0x09, 0xb2, 0xc8, 0x8f, 0x3f, 0x62, + 0x04, 0xf9, 0xc8, 0x7f, 0x82, 0x38, 0x3f, 0xfe, 0x33, 0x60, 0x18, 0x30, 0x90, 0x3f, 0x27, 0x58, + 0x24, 0x0b, 0x24, 0x40, 0x1e, 0x7f, 0x41, 0xf2, 0x61, 0x20, 0x40, 0x50, 0xa7, 0xaa, 0xba, 0xab, + 0x1f, 0xa4, 0x66, 0xbc, 0xeb, 0x24, 0x06, 0xfc, 0x35, 0xc3, 0x53, 0xe7, 0x9c, 0xae, 0x77, 0x9d, + 0x3a, 0x75, 0x1e, 0xee, 0x80, 0xef, 0xb8, 0xeb, 0x02, 0x5e, 0x61, 0x60, 0xc6, 0x59, 0x7b, 0x0b, + 0xce, 0x65, 0x0d, 0x38, 0xb9, 0x06, 0x2b, 0x72, 0x30, 0x20, 0xce, 0xa4, 0xe4, 0x8c, 0x3c, 0x11, + 0x0e, 0x88, 0x33, 0xf8, 0x81, 0x02, 0x97, 0xe7, 0x6d, 0x1f, 0xe4, 0x22, 0x14, 0x47, 0x63, 0xcf, + 0x47, 0x31, 0x95, 0x67, 0x5b, 0x10, 0xbf, 0x31, 0x91, 0x02, 0xca, 0x53, 0x13, 0xe7, 0x90, 0x3b, + 0x78, 0x98, 0xcb, 0x08, 0xe9, 0x38, 0x87, 0x01, 0x79, 0x11, 0x36, 0xfa, 0xee, 0x81, 0x33, 0x1d, + 0x4c, 0xec, 0xa0, 0x77, 0xe4, 0xf6, 0xd1, 0x05, 0x0b, 0x0d, 0xf7, 0x4c, 0x95, 0x17, 0x58, 0x02, + 0x9e, 0xaa, 0xf1, 0xc2, 0x8c, 0x1a, 0xdf, 0x2d, 0x14, 0x15, 0x35, 0x67, 0xa2, 0xa5, 0x94, 0xf6, + 0x8d, 0x1c, 0x6c, 0xcd, 0x5a, 0x2f, 0xe4, 0xcd, 0xac, 0x3e, 0x60, 0x0f, 0x17, 0x32, 0x5c, 0x7e, + 0xb8, 0x90, 0xbe, 0x46, 0xee, 0x40, 0xe8, 0x40, 0xf5, 0xb4, 0x60, 0x08, 0x02, 0x46, 0x69, 0x46, + 0x4e, 0x10, 0x3c, 0xa6, 0x5b, 0x42, 0x5e, 0x0a, 0xa8, 0xcb, 0x61, 0x32, 0x8d, 0x80, 0x91, 0xd7, + 0x00, 0x7a, 0x03, 0x3f, 0x70, 0xd1, 0x3e, 0x80, 0xcb, 0x1a, 0xcc, 0x2c, 0x3c, 0x84, 0xca, 0x0f, + 0xc2, 0x08, 0xad, 0xf8, 0x7d, 0x97, 0x0f, 0xa0, 0x03, 0x9b, 0x33, 0x36, 0x48, 0x3a, 0x3c, 0x51, + 0x76, 0x7a, 0x91, 0xeb, 0x6a, 0x1a, 0xe6, 0xa8, 0x4f, 0xf6, 0x78, 0x6e, 0xd6, 0x1c, 0x79, 0x02, + 0x24, 0xbd, 0x0b, 0x52, 0xee, 0xdc, 0xb8, 0x79, 0x3a, 0x0e, 0xb9, 0x33, 0x48, 0x77, 0x3c, 0x20, + 0x57, 0xa1, 0x24, 0x72, 0x59, 0x52, 0x59, 0x9e, 0x31, 0x07, 0x0e, 0xba, 0xe7, 0xe2, 0xe4, 0xc1, + 0x88, 0xa9, 0xe8, 0x26, 0xc7, 0xa5, 0x84, 0x65, 0x84, 0x74, 0x9e, 0x8c, 0x44, 0xeb, 0x2e, 0x8b, + 0xf9, 0x1d, 0x3f, 0x9b, 0x78, 0xe9, 0xef, 0x2b, 0x62, 0xf8, 0xd3, 0x9b, 0xfb, 0xd3, 0xea, 0x47, + 0x00, 0xbd, 0x94, 0x78, 0xc5, 0xf0, 0x7f, 0x2a, 0xb5, 0x88, 0x55, 0xc7, 0xa5, 0x16, 0xfe, 0x93, + 0xdc, 0x80, 0xf5, 0x31, 0xb3, 0x63, 0x9d, 0xf8, 0xbc, 0x3f, 0x59, 0xde, 0x90, 0x55, 0x06, 0xee, + 0xf8, 0xd8, 0xa7, 0xbc, 0x5e, 0x77, 0xc3, 0x0e, 0x93, 0xce, 0x3a, 0xf2, 0x32, 0x2c, 0xd3, 0xb3, + 0x0e, 0x23, 0xed, 0x24, 0xdc, 0x23, 0x10, 0x0f, 0x25, 0x07, 0xb3, 0xf8, 0x65, 0xfe, 0x3f, 0xe7, + 0xf5, 0x4e, 0x4e, 0x30, 0x93, 0x4f, 0x5a, 0xb2, 0x09, 0x4b, 0xfe, 0xf8, 0x50, 0x6a, 0xda, 0xa2, + 0x3f, 0x3e, 0xa4, 0xed, 0xba, 0x09, 0x2a, 0xf3, 0xd6, 0x61, 0x51, 0x13, 0x82, 0x27, 0x43, 0x76, + 0x15, 0x2f, 0x9a, 0x6b, 0x0c, 0x8e, 0x09, 0xfb, 0x9f, 0x0c, 0x7b, 0x14, 0x33, 0x08, 0x7c, 0x5b, + 0x0e, 0xb0, 0xc5, 0x9b, 0xbd, 0x16, 0x04, 0x7e, 0x14, 0x69, 0xab, 0x4f, 0xb6, 0x61, 0x95, 0xf2, + 0x09, 0xc3, 0x7c, 0x71, 0x41, 0xe0, 0xd9, 0xb4, 0x20, 0xf0, 0x64, 0xd8, 0x13, 0x55, 0x34, 0x57, + 0x02, 0xe9, 0x17, 0xb9, 0x07, 0xaa, 0x24, 0x31, 0xa1, 0xfb, 0x66, 0xc2, 0xa6, 0x3a, 0x62, 0x23, + 0x49, 0x5a, 0xb5, 0xe1, 0x81, 0x6f, 0xae, 0xf7, 0xe2, 0x00, 0xde, 0x35, 0xdf, 0x53, 0xc4, 0x5e, + 0x9a, 0x41, 0x44, 0x34, 0x58, 0x3d, 0x72, 0x02, 0x3b, 0x08, 0x8e, 0x99, 0x8d, 0x18, 0x0f, 0x2c, + 0x5c, 0x3a, 0x72, 0x02, 0x2b, 0x38, 0x16, 0x89, 0x4b, 0xce, 0x53, 0x1c, 0xdf, 0x99, 0x4e, 0x8e, + 0x6c, 0x59, 0xfe, 0x63, 0x3d, 0x76, 0xf6, 0xc8, 0x09, 0x5a, 0xb4, 0x4c, 0xe2, 0x4d, 0xae, 0xc3, + 0x1a, 0xf2, 0xed, 0x79, 0x82, 0x31, 0x46, 0xbe, 0x30, 0x57, 0x28, 0xe3, 0x9e, 0xc7, 0x38, 0xf3, + 0x1a, 0xfe, 0x63, 0x0e, 0x2e, 0x64, 0xf7, 0x0e, 0x4e, 0x4f, 0xda, 0xa7, 0xe8, 0xa3, 0xc7, 0xeb, + 0xb6, 0x4c, 0x21, 0x2c, 0x6a, 0x49, 0xd6, 0xe0, 0xe4, 0x32, 0x07, 0xa7, 0x0c, 0x1b, 0xc8, 0x88, + 0x4b, 0x9a, 0x03, 0x2f, 0x98, 0xf0, 0x60, 0x1c, 0xe6, 0x3a, 0x2d, 0x60, 0xfb, 0x79, 0x9d, 0x82, + 0xc9, 0x0b, 0xb0, 0x26, 0x76, 0x64, 0xff, 0xf1, 0x90, 0x7e, 0x98, 0x6d, 0xc7, 0xab, 0x1c, 0xda, + 0x42, 0x20, 0x39, 0x0f, 0x8b, 0xce, 0x68, 0x44, 0x3f, 0xc9, 0x76, 0xe1, 0x05, 0x67, 0x34, 0x62, + 0xc9, 0x75, 0xd0, 0x23, 0xd1, 0x3e, 0x40, 0x2b, 0x21, 0x6e, 0x92, 0x68, 0xae, 0x20, 0x90, 0x59, + 0x0e, 0x05, 0x74, 0xdd, 0x53, 0x5a, 0x81, 0xb2, 0x84, 0x28, 0xe0, 0x8c, 0x42, 0x84, 0x67, 0xa0, + 0x28, 0xde, 0xab, 0x99, 0x63, 0x85, 0xb9, 0xe4, 0xf0, 0xb7, 0xea, 0x57, 0x61, 0xb3, 0xef, 0x05, + 0x38, 0x79, 0x59, 0x93, 0x46, 0x23, 0xee, 0x03, 0xc9, 0x82, 0xf4, 0x9a, 0xe7, 0x78, 0x31, 0xed, + 0x49, 0x7d, 0x34, 0x62, 0x9e, 0x90, 0xbc, 0xaf, 0x5f, 0x87, 0x75, 0x2e, 0x71, 0xf1, 0x23, 0x12, + 0xeb, 0xc2, 0x17, 0x30, 0xbd, 0x0a, 0xf1, 0x74, 0x46, 0xc0, 0x41, 0xb5, 0xbe, 0xa0, 0xfc, 0x1b, + 0x05, 0xce, 0x67, 0x8a, 0x6c, 0xe4, 0x4b, 0xc0, 0x5c, 0xbe, 0x26, 0xbe, 0x3d, 0x76, 0x7b, 0xde, + 0xc8, 0xc3, 0x18, 0x1a, 0x4c, 0xa5, 0x79, 0x67, 0x9e, 0xb0, 0x87, 0xee, 0x63, 0x1d, 0xdf, 0x0c, + 0x89, 0x98, 0xae, 0x45, 0x1d, 0x27, 0xc0, 0x17, 0x1f, 0xc2, 0xf9, 0x4c, 0xd4, 0x0c, 0x1d, 0xc8, + 0x87, 0xe3, 0xc9, 0xa4, 0xc5, 0x23, 0x55, 0xa2, 0xd1, 0x92, 0x6e, 0x84, 0x37, 0xef, 0x87, 0x61, + 0xf3, 0x12, 0xc2, 0x1d, 0x31, 0x92, 0xeb, 0x3a, 0xeb, 0x7e, 0x22, 0x88, 0x66, 0x2f, 0xed, 0x87, + 0x70, 0x9e, 0x4f, 0xbe, 0xc3, 0xb1, 0x33, 0x3a, 0x8a, 0xd8, 0xb1, 0x8a, 0x7e, 0x28, 0x8b, 0x1d, + 0x9b, 0x95, 0x7b, 0x14, 0x3f, 0xe4, 0x7a, 0xd6, 0x49, 0x03, 0x79, 0x1b, 0xbe, 0x99, 0x13, 0x4b, + 0x3d, 0xa3, 0x3a, 0x19, 0xd3, 0x5a, 0xc9, 0x9a, 0xd6, 0x27, 0x5f, 0x53, 0x4d, 0x20, 0xf2, 0x66, + 0xc5, 0xb4, 0x9e, 0xdc, 0xa0, 0x4a, 0xc8, 0xe9, 0xbc, 0x22, 0xd2, 0xd6, 0x60, 0xb1, 0x64, 0x9e, + 0x1b, 0xbd, 0x24, 0x88, 0x5c, 0x82, 0xe5, 0x30, 0x5f, 0x36, 0x3f, 0x38, 0x8a, 0x0c, 0x50, 0xeb, + 0x93, 0xe7, 0x60, 0x85, 0x89, 0xe4, 0xb1, 0x35, 0x07, 0x08, 0xd3, 0xe9, 0xc2, 0x13, 0x7d, 0xa0, + 0xc0, 0x73, 0x4f, 0xeb, 0x43, 0x72, 0x1f, 0x2e, 0xa0, 0x59, 0x47, 0xe0, 0x87, 0xc3, 0x60, 0xf7, + 0x9c, 0xde, 0x91, 0xcb, 0x67, 0xad, 0x96, 0x39, 0x18, 0xa3, 0x91, 0x65, 0xb5, 0xa4, 0x71, 0x18, + 0x8d, 0xac, 0xc0, 0x17, 0xbf, 0x2b, 0x94, 0x9c, 0xd7, 0xa1, 0x0f, 0x97, 0xe6, 0x50, 0x4a, 0x1b, + 0x87, 0x22, 0x6f, 0x1c, 0x37, 0x41, 0x3d, 0x70, 0xfb, 0x54, 0x26, 0x76, 0xfb, 0x58, 0xb5, 0xb7, + 0xef, 0xb0, 0x0c, 0xf1, 0xe6, 0x5a, 0x08, 0xb7, 0x02, 0x7f, 0xff, 0x0e, 0xff, 0xca, 0xb1, 0x38, + 0xf2, 0xe4, 0x6b, 0x05, 0x79, 0x19, 0xce, 0x26, 0xe2, 0x93, 0x44, 0x0e, 0xef, 0xe6, 0x06, 0x2d, + 0x8a, 0x47, 0xb3, 0xba, 0x06, 0x2b, 0x62, 0x56, 0x8c, 0x43, 0x3f, 0x38, 0xb3, 0xc4, 0x61, 0x74, + 0xd5, 0xf1, 0xcf, 0x4d, 0x45, 0xa3, 0x32, 0x6f, 0x24, 0x27, 0x90, 0xa5, 0xc9, 0x4b, 0x40, 0x42, + 0xb9, 0x3d, 0xdc, 0x28, 0xf8, 0x07, 0x37, 0x44, 0x49, 0xb8, 0xc2, 0xf9, 0x67, 0xff, 0x32, 0x07, + 0x67, 0x33, 0xae, 0x32, 0xf4, 0x12, 0xe0, 0x0d, 0x27, 0xee, 0x21, 0xbb, 0x42, 0xc8, 0x8d, 0x5c, + 0x97, 0xe0, 0x5c, 0x3f, 0xb5, 0xc8, 0x32, 0xa0, 0xf3, 0x6f, 0xf1, 0x5f, 0x74, 0xf3, 0x70, 0xc6, + 0x42, 0xf5, 0x42, 0xff, 0x25, 0x35, 0xd8, 0xc0, 0xb4, 0x0e, 0x81, 0xe7, 0x63, 0x76, 0x08, 0x14, + 0x42, 0x0a, 0xb1, 0xcb, 0x0e, 0xd6, 0xa2, 0x2d, 0x21, 0x51, 0x29, 0xc4, 0x54, 0x47, 0x09, 0x08, + 0xf9, 0x04, 0x5c, 0x94, 0xce, 0x1a, 0x3b, 0xb1, 0xf2, 0xd0, 0xd2, 0xdd, 0xdc, 0x74, 0xc2, 0x53, + 0x67, 0x27, 0xb6, 0x06, 0xb7, 0xe1, 0x0a, 0x0e, 0xa2, 0xd7, 0x1f, 0xd9, 0xa9, 0x3c, 0x20, 0xd8, + 0x54, 0x16, 0x38, 0xff, 0x22, 0xc5, 0xaa, 0xf5, 0x47, 0x89, 0x94, 0x20, 0xb4, 0xd5, 0xbc, 0xfb, + 0x1e, 0xc2, 0xf9, 0xcc, 0x1a, 0xd3, 0x03, 0x06, 0x0d, 0xa9, 0x22, 0xd9, 0x68, 0x89, 0xfe, 0xa6, + 0xc2, 0xd1, 0x35, 0x58, 0x79, 0xe4, 0x3a, 0x63, 0x77, 0xcc, 0x4f, 0x6e, 0x3e, 0x25, 0x18, 0x4c, + 0x3e, 0xb8, 0xfb, 0xf1, 0xa1, 0xe1, 0x3a, 0x23, 0xd2, 0x80, 0xb3, 0xec, 0x04, 0xf4, 0x8e, 0x51, + 0x18, 0xe4, 0x7a, 0x26, 0x25, 0x26, 0x0e, 0x21, 0x09, 0x1e, 0x4d, 0x35, 0xc4, 0x62, 0xd4, 0xe6, + 0xc6, 0x61, 0x12, 0x44, 0x57, 0xf4, 0x85, 0x6c, 0x6c, 0xb2, 0x0d, 0x25, 0xc6, 0x9c, 0x5d, 0x0b, + 0xd8, 0x03, 0xc1, 0xb5, 0xb9, 0x5f, 0xa8, 0xa0, 0x7d, 0x71, 0x10, 0xfe, 0x4f, 0xcf, 0x6b, 0x7c, + 0x8b, 0xb5, 0x8f, 0xe5, 0xf7, 0x0f, 0x73, 0x05, 0x81, 0xfc, 0xdd, 0x43, 0xfb, 0x2b, 0x45, 0x34, + 0x35, 0x76, 0x39, 0xa6, 0x53, 0x2b, 0x70, 0x87, 0xe2, 0x0d, 0x68, 0xd9, 0xe4, 0xbf, 0x4e, 0x39, + 0xd5, 0xc9, 0x6b, 0xb0, 0x42, 0xd9, 0x1e, 0x4e, 0x87, 0x6c, 0xca, 0xe5, 0x63, 0x71, 0x79, 0x1a, + 0xac, 0x88, 0x0e, 0x5b, 0xf5, 0x8c, 0x59, 0x3a, 0x8e, 0x7e, 0x52, 0x69, 0x39, 0x38, 0x9e, 0x8c, + 0xe4, 0x89, 0x2a, 0x14, 0x85, 0x56, 0xa3, 0xd3, 0xe6, 0x24, 0x45, 0x8a, 0x13, 0x49, 0xcb, 0xdb, + 0x8b, 0x4c, 0x55, 0xa8, 0xbd, 0x08, 0x25, 0x89, 0x37, 0x6d, 0x0c, 0xf3, 0x9c, 0x11, 0x8d, 0x61, + 0xbf, 0xf8, 0x60, 0x3f, 0x82, 0xa2, 0x60, 0x49, 0xaf, 0x05, 0x47, 0x7e, 0x20, 0x16, 0x39, 0xfe, + 0x4f, 0x61, 0xb4, 0x97, 0xb1, 0x91, 0x0b, 0x26, 0xfe, 0x8f, 0x67, 0xc9, 0xc4, 0xa1, 0xf7, 0x81, + 0x41, 0x60, 0x8f, 0xd0, 0x02, 0x2b, 0x14, 0x9e, 0x29, 0xbc, 0x33, 0x08, 0x98, 0x5d, 0x16, 0xff, + 0xc6, 0x9f, 0x87, 0x87, 0x70, 0x42, 0x9b, 0x30, 0x6b, 0xcf, 0x8c, 0x1d, 0x19, 0xb9, 0xf4, 0x91, + 0xc1, 0xe2, 0xad, 0x70, 0x4a, 0xf6, 0x65, 0x40, 0x18, 0x1e, 0x19, 0xd2, 0xce, 0x50, 0x88, 0xed, + 0x0c, 0xd2, 0x9d, 0x3c, 0x1a, 0x3d, 0x76, 0xe2, 0x88, 0x3b, 0x79, 0x72, 0x9f, 0xfa, 0xe3, 0x9c, + 0x50, 0x11, 0x6c, 0xfb, 0xfe, 0x24, 0x98, 0x8c, 0x9d, 0x51, 0x4c, 0x15, 0x4a, 0x8e, 0xe1, 0x19, + 0x94, 0xa0, 0xef, 0x60, 0x0a, 0x0d, 0x7f, 0x2c, 0x42, 0x7c, 0x84, 0x33, 0xb7, 0x74, 0xe7, 0x23, + 0x71, 0x19, 0x5f, 0xa7, 0xd8, 0xba, 0x8c, 0x4c, 0x27, 0xac, 0xc4, 0xb5, 0x7a, 0xc6, 0xdc, 0x64, + 0x3c, 0x53, 0x58, 0xa4, 0x9a, 0xb1, 0x88, 0x93, 0xba, 0xd0, 0xed, 0x68, 0x45, 0xc7, 0xb9, 0xca, + 0x6b, 0x9d, 0x7c, 0x0a, 0x96, 0xbd, 0xbe, 0x9c, 0x29, 0x32, 0xa9, 0x85, 0xab, 0xf5, 0x59, 0xb4, + 0xea, 0x88, 0x07, 0x9d, 0x73, 0x1e, 0x87, 0x6e, 0xaf, 0xc6, 0x94, 0xc6, 0xda, 0xb6, 0xb8, 0x8d, + 0xa6, 0xc9, 0xc8, 0x1a, 0xe4, 0xc2, 0x11, 0xce, 0x79, 0x7d, 0xb6, 0xbc, 0xa2, 0x78, 0xd9, 0x26, + 0xff, 0xa5, 0xfd, 0x6f, 0xb8, 0x79, 0xd2, 0x3e, 0xa2, 0x4b, 0x71, 0x46, 0x87, 0x2f, 0xb3, 0x50, + 0x95, 0xf1, 0x7e, 0xbb, 0x06, 0x72, 0xb8, 0x5f, 0x4f, 0x6c, 0x7e, 0x02, 0xd6, 0x1d, 0x7b, 0xda, + 0x9f, 0xe5, 0x61, 0x2d, 0xae, 0x26, 0x27, 0x2f, 0x42, 0x41, 0xda, 0x81, 0x36, 0x33, 0x74, 0xe9, + 0xb8, 0xef, 0x20, 0xd2, 0x89, 0x76, 0x1c, 0x72, 0x17, 0xd6, 0xd0, 0x70, 0x0f, 0x45, 0xcf, 0x89, + 0xc7, 0x1f, 0x5f, 0xe6, 0xbf, 0x9f, 0x15, 0x7f, 0xf4, 0xee, 0xd5, 0x33, 0xf8, 0x54, 0xb6, 0x42, + 0x69, 0xa9, 0xf4, 0x47, 0x0b, 0x25, 0x2d, 0x68, 0x61, 0xb6, 0x16, 0x94, 0x37, 0x65, 0x86, 0x16, + 0x74, 0x61, 0x8e, 0x16, 0x34, 0xa2, 0x94, 0xb5, 0xa0, 0xa8, 0x0b, 0x5f, 0x9a, 0xa5, 0x0b, 0x8f, + 0x68, 0x98, 0x2e, 0x3c, 0xd2, 0x62, 0x16, 0x67, 0x6a, 0x31, 0x23, 0x1a, 0xae, 0xc5, 0xbc, 0xce, + 0xfb, 0x68, 0xec, 0x3c, 0xb6, 0xb1, 0xf3, 0xf8, 0xb1, 0x88, 0xad, 0x37, 0x9d, 0xc7, 0x68, 0x5c, + 0xb3, 0xbd, 0x0c, 0xc2, 0x22, 0x47, 0xfb, 0x5d, 0x25, 0xa1, 0x09, 0x14, 0xe3, 0xf7, 0x02, 0xac, + 0xb1, 0xc3, 0x8a, 0x87, 0x33, 0x65, 0xa7, 0xd5, 0xaa, 0xb9, 0x2a, 0xa0, 0xec, 0xbe, 0xf9, 0x21, + 0x58, 0x0f, 0xd1, 0xf8, 0x95, 0x0b, 0x3d, 0xf5, 0xcc, 0x90, 0x9a, 0x87, 0x9d, 0x79, 0x11, 0x36, + 0x42, 0x44, 0xae, 0xcd, 0x61, 0xd7, 0xcd, 0x55, 0x53, 0x15, 0x05, 0x6d, 0x0e, 0xd7, 0x0e, 0x93, + 0x37, 0x8f, 0x5f, 0x52, 0xad, 0xb4, 0x1f, 0xe6, 0x63, 0x5a, 0x12, 0xf1, 0x19, 0x7a, 0x8a, 0x06, + 0xbe, 0xcd, 0x3b, 0x89, 0xef, 0x45, 0xd7, 0x66, 0x8c, 0x19, 0xb7, 0x69, 0xb2, 0xac, 0x96, 0x09, + 0x41, 0xe0, 0x0b, 0x13, 0x27, 0x9b, 0x49, 0xd4, 0xec, 0xdc, 0xc7, 0x39, 0x2b, 0xd8, 0xb1, 0x8d, + 0xa7, 0x3c, 0x9f, 0x9d, 0xb8, 0xa6, 0xd2, 0x29, 0x8b, 0x92, 0x75, 0xf8, 0x4b, 0x7c, 0xa0, 0x0b, + 0xa8, 0x54, 0x0c, 0xe2, 0xcc, 0xf3, 0x19, 0x77, 0xa7, 0x14, 0x73, 0xec, 0x25, 0xe4, 0xac, 0x4e, + 0xc5, 0xbf, 0x82, 0xad, 0x01, 0x2b, 0xa8, 0xa3, 0x10, 0x0c, 0x0b, 0x19, 0x2a, 0xf8, 0x74, 0xe3, + 0x2b, 0xb5, 0x86, 0x59, 0xa2, 0x74, 0x82, 0xcd, 0x11, 0x3c, 0x23, 0x6b, 0x16, 0xe2, 0x95, 0x5c, + 0x10, 0x51, 0x7c, 0xe7, 0xf6, 0x40, 0xa4, 0x80, 0xc0, 0xaa, 0x5e, 0x70, 0xe2, 0x00, 0x8e, 0xa6, + 0x1d, 0xc1, 0xc5, 0xd9, 0x43, 0x32, 0x27, 0x43, 0x54, 0x74, 0x80, 0xe6, 0xe4, 0x03, 0x54, 0xd6, + 0x33, 0xe4, 0x63, 0x7a, 0x06, 0xed, 0x8f, 0xf2, 0xf0, 0xfc, 0x09, 0x86, 0x6b, 0xce, 0x37, 0x3f, + 0x1d, 0x17, 0xcf, 0x72, 0xb1, 0x9b, 0x21, 0x65, 0xca, 0x37, 0x48, 0x7a, 0x4b, 0xcd, 0x16, 0xce, + 0xbe, 0x04, 0xeb, 0x6c, 0x17, 0x64, 0x66, 0x89, 0x07, 0xd3, 0xc1, 0x09, 0xb6, 0xc1, 0x4b, 0xc2, + 0x87, 0x2a, 0x41, 0x8a, 0x3b, 0x23, 0xee, 0x18, 0x56, 0x08, 0x23, 0x1d, 0x28, 0x21, 0xda, 0x81, + 0xe3, 0x0d, 0x4e, 0xe4, 0xcc, 0x23, 0x3c, 0xb4, 0x64, 0x32, 0x66, 0x4d, 0x4d, 0x01, 0xbb, 0xf8, + 0x9b, 0xdc, 0x80, 0xf5, 0xe1, 0xf4, 0x98, 0x0a, 0x1e, 0x6c, 0x2e, 0x70, 0xeb, 0x8f, 0x05, 0x73, + 0x75, 0x38, 0x3d, 0xd6, 0x47, 0x23, 0x1c, 0x52, 0x34, 0x13, 0xd9, 0xa0, 0x78, 0x6c, 0xd5, 0x0a, + 0xcc, 0x45, 0xc4, 0xa4, 0x0c, 0xd8, 0xba, 0xe5, 0xb8, 0xe7, 0x80, 0x19, 0x0d, 0xf2, 0x0c, 0x59, + 0xec, 0x87, 0xf6, 0xef, 0x39, 0x71, 0xdf, 0x9d, 0x3d, 0xef, 0x7f, 0x3d, 0x44, 0x19, 0x43, 0x74, + 0x13, 0x54, 0xda, 0xf5, 0xd1, 0xa6, 0x12, 0x8e, 0xd1, 0xda, 0x70, 0x7a, 0x1c, 0xf6, 0x9d, 0xdc, + 0xf1, 0x8b, 0x72, 0xc7, 0xbf, 0x26, 0xee, 0xc3, 0x99, 0xdb, 0xc3, 0xec, 0x2e, 0xd7, 0xfe, 0x25, + 0x0f, 0x37, 0x4e, 0xb6, 0x09, 0xfc, 0x7a, 0xdc, 0x32, 0xc6, 0x2d, 0xa1, 0x3a, 0x5d, 0x48, 0xa9, + 0x4e, 0x33, 0xd6, 0xde, 0x62, 0xd6, 0xda, 0x4b, 0x29, 0x6a, 0x97, 0x32, 0x14, 0xb5, 0x99, 0x0b, + 0xb4, 0xf8, 0x94, 0x05, 0xba, 0x2c, 0xcf, 0x93, 0x7f, 0x08, 0x15, 0x18, 0xf1, 0xfb, 0xc0, 0x43, + 0x38, 0x2b, 0xee, 0x03, 0xec, 0xe4, 0x88, 0xf4, 0xef, 0xa5, 0x3b, 0xb7, 0xb2, 0x6e, 0x02, 0x88, + 0x96, 0x21, 0xad, 0x6f, 0xf0, 0x3b, 0x40, 0x54, 0xfe, 0x3f, 0x47, 0xfa, 0x27, 0x0f, 0xe0, 0x02, + 0xc6, 0x97, 0xef, 0xc9, 0x2f, 0x07, 0xf6, 0xd8, 0x3d, 0xe0, 0xf3, 0xe1, 0x5a, 0x4a, 0x56, 0xf6, + 0x7a, 0x52, 0x75, 0x4c, 0xf7, 0xa0, 0x7a, 0xc6, 0x3c, 0x17, 0x64, 0xc0, 0x93, 0x17, 0x8b, 0x3f, + 0x55, 0x40, 0x7b, 0x7a, 0x7f, 0xa1, 0xa2, 0x2a, 0xd9, 0xe1, 0xcb, 0x66, 0xc9, 0x91, 0x7a, 0xef, + 0x79, 0x58, 0x1d, 0xbb, 0x07, 0x63, 0x37, 0x38, 0x8a, 0x69, 0x40, 0x56, 0x38, 0x50, 0x74, 0x8c, + 0x08, 0x4a, 0x79, 0x2a, 0xc9, 0x5c, 0x10, 0x69, 0xbb, 0xe1, 0x7d, 0x31, 0x73, 0x1c, 0xe8, 0x6c, + 0x92, 0x2b, 0xc8, 0x7e, 0xdc, 0x2d, 0x14, 0x73, 0x6a, 0xde, 0xe4, 0xa1, 0x33, 0x0f, 0xbc, 0x81, + 0xab, 0xfd, 0x85, 0x22, 0x24, 0x82, 0xac, 0xce, 0x23, 0x0f, 0x25, 0x63, 0xde, 0x7c, 0x4a, 0x0c, + 0xc9, 0x22, 0x91, 0xed, 0x1e, 0x79, 0x78, 0x46, 0x04, 0xc4, 0xc2, 0x33, 0x22, 0xe4, 0x7d, 0x58, + 0x24, 0xf2, 0x5b, 0xf3, 0x1b, 0xc2, 0x22, 0x88, 0xee, 0x79, 0xfb, 0xb7, 0xc9, 0x2d, 0x58, 0x62, + 0x46, 0x40, 0xa2, 0xba, 0xeb, 0xb1, 0xea, 0xee, 0xdf, 0x36, 0x45, 0xb9, 0xf6, 0x4e, 0xf8, 0xae, + 0x95, 0x6a, 0xc4, 0xfe, 0x6d, 0xf2, 0xda, 0xc9, 0x8c, 0x73, 0x8b, 0xc2, 0x38, 0x37, 0x34, 0xcc, + 0x7d, 0x3d, 0x66, 0x98, 0x7b, 0x7d, 0x7e, 0x6f, 0xf1, 0xd7, 0x48, 0x16, 0x8e, 0x30, 0x0a, 0x53, + 0xf5, 0xb3, 0x1c, 0x3c, 0x3b, 0x97, 0x82, 0x5c, 0x86, 0xa2, 0xde, 0xae, 0x75, 0xa2, 0xf1, 0xa5, + 0x6b, 0x46, 0x40, 0xc8, 0x1e, 0x2c, 0x6f, 0x3b, 0x81, 0xd7, 0xa3, 0xd3, 0x38, 0xf3, 0x79, 0x20, + 0xc5, 0x36, 0x44, 0xaf, 0x9e, 0x31, 0x23, 0x5a, 0x62, 0xc3, 0x06, 0xae, 0x85, 0x58, 0xea, 0xa9, + 0x7c, 0x86, 0xae, 0x21, 0xc5, 0x30, 0x45, 0x46, 0xf7, 0x99, 0x14, 0x90, 0x3c, 0x02, 0x62, 0x59, + 0xd5, 0x8a, 0x3b, 0x9e, 0xf0, 0x3b, 0xf8, 0xc4, 0x0b, 0x2d, 0x3d, 0x3f, 0xfa, 0x94, 0xbe, 0x4b, + 0xd1, 0x55, 0xcf, 0x98, 0x19, 0xdc, 0x92, 0xcb, 0xfc, 0x6d, 0x21, 0xef, 0xcc, 0xee, 0x84, 0x53, + 0x84, 0x7a, 0xbd, 0x09, 0xc5, 0xb6, 0xb0, 0x45, 0x90, 0x2c, 0xe6, 0x85, 0xdd, 0x81, 0x19, 0x96, + 0x6a, 0xbf, 0xa1, 0x08, 0xa5, 0xc3, 0xd3, 0x3b, 0x4b, 0xca, 0x0c, 0xd6, 0x9f, 0x9f, 0x19, 0xac, + 0xff, 0x0b, 0x66, 0x06, 0xd3, 0x3c, 0xb8, 0x75, 0xe2, 0x8e, 0x25, 0x9f, 0x04, 0x15, 0x93, 0x28, + 0x39, 0xd2, 0x20, 0xb1, 0xf5, 0xb5, 0x11, 0xc6, 0xfe, 0xae, 0xf2, 0x4c, 0x75, 0xe6, 0x7a, 0x2f, + 0x4e, 0xad, 0xfd, 0x09, 0x8f, 0xf9, 0x5e, 0xeb, 0xb7, 0x13, 0x8a, 0xe6, 0xf7, 0xeb, 0x64, 0x61, + 0xc4, 0x16, 0xdb, 0xf3, 0x52, 0x12, 0xcb, 0xf4, 0xb7, 0x66, 0xfb, 0x5a, 0x48, 0x2b, 0xef, 0x0f, + 0xf2, 0x70, 0x79, 0x1e, 0x79, 0x66, 0x9a, 0x6c, 0xe5, 0x74, 0x69, 0xb2, 0x6f, 0x41, 0x91, 0xc1, + 0x42, 0x0f, 0x02, 0x1c, 0x5b, 0x4e, 0x4a, 0xc7, 0x56, 0x14, 0x93, 0xe7, 0x61, 0x51, 0xaf, 0x58, + 0x51, 0xe6, 0x36, 0x34, 0xf5, 0x75, 0x7a, 0x01, 0x1a, 0x91, 0xf2, 0x22, 0xf2, 0xc5, 0x74, 0xb2, + 0x42, 0x9e, 0xb2, 0xed, 0x92, 0xd4, 0x21, 0xa9, 0x74, 0x0c, 0x58, 0xdf, 0x28, 0x7d, 0x00, 0x8f, + 0xc8, 0x6d, 0xa6, 0x13, 0x1f, 0x6a, 0xb0, 0xd8, 0x1e, 0xbb, 0x81, 0x3b, 0x91, 0xcd, 0x70, 0x47, + 0x08, 0x31, 0x79, 0x09, 0x37, 0x92, 0x75, 0x9e, 0xb0, 0x98, 0x08, 0x8b, 0x72, 0x9c, 0x1a, 0xb4, + 0xaa, 0xa5, 0x60, 0x53, 0x42, 0xa1, 0x04, 0x75, 0x67, 0x3a, 0xec, 0x1d, 0x75, 0xcd, 0x3a, 0x97, + 0x9c, 0x18, 0xc1, 0x00, 0xa1, 0xb4, 0x81, 0x81, 0x29, 0xa1, 0x68, 0xdf, 0x56, 0xe0, 0x5c, 0x56, + 0x3b, 0xc8, 0x65, 0x28, 0x0c, 0x33, 0xf3, 0x32, 0x0e, 0x99, 0x2b, 0x77, 0x89, 0xfe, 0xb5, 0x0f, + 0xfc, 0xf1, 0xb1, 0x33, 0x91, 0x8d, 0x95, 0x25, 0xb0, 0x09, 0xf4, 0xc7, 0x2e, 0xfe, 0x4f, 0xae, + 0x8a, 0x23, 0x27, 0x9f, 0xca, 0xe4, 0x88, 0x7f, 0x34, 0x1d, 0xa0, 0xd6, 0x6f, 0xb7, 0x46, 0x2c, + 0x1d, 0xc0, 0x2b, 0x50, 0xa0, 0xd5, 0x4a, 0xcc, 0x5e, 0x3a, 0x7f, 0xf4, 0x46, 0x9d, 0x23, 0xb1, + 0x5a, 0x05, 0xce, 0xf1, 0xc0, 0x44, 0x64, 0xed, 0x3e, 0xac, 0xc5, 0x31, 0x88, 0x11, 0x8f, 0x08, + 0x5b, 0xba, 0xa3, 0x72, 0x4e, 0xdb, 0xbe, 0xcf, 0x1c, 0x66, 0xb6, 0x9f, 0xf9, 0xd9, 0xbb, 0x57, + 0x81, 0xfe, 0x64, 0x34, 0x59, 0x11, 0x63, 0xb5, 0xef, 0xe4, 0xe0, 0x5c, 0xe4, 0xa3, 0x2f, 0xd6, + 0xd0, 0xaf, 0xac, 0xc3, 0xa8, 0x1e, 0x73, 0x68, 0x14, 0x72, 0x63, 0xba, 0x81, 0x73, 0xfc, 0xa8, + 0xf6, 0x60, 0x6b, 0x16, 0x3e, 0x79, 0x11, 0x96, 0x31, 0xac, 0xd3, 0xc8, 0xe9, 0xb9, 0xf2, 0x36, + 0x3b, 0x14, 0x40, 0x33, 0x2a, 0xd7, 0x7e, 0xa2, 0xc0, 0x45, 0xee, 0xe6, 0xd1, 0x70, 0xbc, 0x21, + 0xbe, 0x12, 0xf4, 0xdc, 0x0f, 0xc6, 0xe1, 0x79, 0x2f, 0xb6, 0x8f, 0xbd, 0x10, 0xf7, 0xe6, 0x49, + 0x7d, 0x6d, 0x76, 0x6b, 0xc9, 0x2d, 0x0c, 0x55, 0xc6, 0x5f, 0xd1, 0x0b, 0x2c, 0xc0, 0xc4, 0x90, + 0x02, 0xe4, 0x00, 0x13, 0x88, 0xa1, 0xfd, 0x1f, 0xb8, 0x32, 0xff, 0x03, 0xe4, 0x0b, 0xb0, 0x8a, + 0xb9, 0xb7, 0xba, 0xa3, 0xc3, 0xb1, 0xd3, 0x77, 0x85, 0x66, 0x4f, 0x68, 0x63, 0xe5, 0x32, 0x16, + 0x79, 0x8d, 0x07, 0x3c, 0x38, 0xc4, 0xac, 0x5e, 0x9c, 0x28, 0xe6, 0x4b, 0x25, 0x73, 0xd3, 0xbe, + 0xa1, 0x00, 0x49, 0xf3, 0x20, 0x1f, 0x83, 0x95, 0x6e, 0xa7, 0x62, 0x4d, 0x9c, 0xf1, 0xa4, 0xea, + 0x4f, 0xc7, 0x3c, 0xec, 0x19, 0xf3, 0x7f, 0x9f, 0xf4, 0x6c, 0xf6, 0x1e, 0x74, 0xe4, 0x4f, 0xc7, + 0x66, 0x0c, 0x0f, 0x73, 0x3c, 0xb9, 0xee, 0x57, 0xfa, 0xce, 0x93, 0x78, 0x8e, 0x27, 0x0e, 0x8b, + 0xe5, 0x78, 0xe2, 0x30, 0xed, 0xfb, 0x0a, 0x5c, 0x12, 0xc6, 0x91, 0xfd, 0x8c, 0xba, 0x54, 0x30, + 0xca, 0xcb, 0x58, 0xc4, 0xd9, 0x9d, 0x27, 0xa1, 0x6f, 0x88, 0x40, 0x48, 0x58, 0x41, 0x14, 0xd5, + 0x19, 0x2d, 0xf9, 0x34, 0x14, 0xac, 0x89, 0x3f, 0x3a, 0x41, 0x24, 0x24, 0x35, 0x1c, 0xd1, 0x89, + 0x3f, 0x42, 0x16, 0x48, 0xa9, 0xb9, 0x70, 0x4e, 0xae, 0x9c, 0xa8, 0x31, 0x69, 0xc0, 0x12, 0x0f, + 0x79, 0x97, 0xb0, 0x3b, 0x98, 0xd3, 0xa6, 0xed, 0x75, 0x11, 0x6e, 0x89, 0xc7, 0x79, 0x35, 0x05, + 0x0f, 0xed, 0xb7, 0x14, 0x28, 0x51, 0xc1, 0x06, 0x2f, 0xa5, 0xef, 0x77, 0x4a, 0xc7, 0xe5, 0x60, + 0x61, 0x46, 0x13, 0xb2, 0x3f, 0xd1, 0x69, 0xfc, 0x2a, 0xac, 0x27, 0x08, 0x88, 0x86, 0x81, 0x36, + 0x06, 0x5e, 0xcf, 0x61, 0x29, 0x63, 0x98, 0x09, 0x4a, 0x0c, 0xa6, 0xfd, 0x3f, 0x05, 0xce, 0xb5, + 0xbe, 0x32, 0x71, 0xd8, 0xb3, 0xad, 0x39, 0x1d, 0x88, 0xf5, 0x4e, 0x85, 0x35, 0x61, 0x65, 0xcb, + 0x82, 0x00, 0x30, 0x61, 0x8d, 0xc3, 0xcc, 0xb0, 0x94, 0x54, 0xa1, 0xc8, 0xcf, 0x97, 0x80, 0x87, + 0x67, 0xbd, 0x22, 0xe9, 0x46, 0x22, 0xc6, 0x1c, 0x89, 0xb6, 0x04, 0xb7, 0x30, 0x4e, 0x63, 0x86, + 0xd4, 0xda, 0xbf, 0x2a, 0xb0, 0x39, 0x83, 0x86, 0xbc, 0x09, 0x0b, 0xe8, 0xa0, 0xc8, 0x47, 0xef, + 0xf2, 0x8c, 0x4f, 0x4c, 0x7a, 0x47, 0xfb, 0xb7, 0xd9, 0x41, 0x74, 0x4c, 0x7f, 0x98, 0x8c, 0x8a, + 0x3c, 0x84, 0x65, 0xbd, 0xdf, 0xe7, 0xb7, 0xb3, 0x5c, 0xec, 0x76, 0x36, 0xe3, 0x8b, 0x2f, 0x87, + 0xf8, 0xec, 0x76, 0xc6, 0x5c, 0x65, 0xfa, 0x7d, 0x9b, 0x3b, 0x5f, 0x46, 0xfc, 0x2e, 0x7e, 0x12, + 0xd6, 0xe2, 0xc8, 0xa7, 0xf2, 0x17, 0x7b, 0x47, 0x01, 0x35, 0x5e, 0x87, 0x5f, 0x4e, 0xa0, 0xa8, + 0xac, 0x61, 0x7e, 0xca, 0xa4, 0xfa, 0x9d, 0x1c, 0x9c, 0xcf, 0xec, 0x61, 0xf2, 0x12, 0x2c, 0xea, + 0xa3, 0x51, 0x6d, 0x87, 0xcf, 0x2a, 0x2e, 0x21, 0xa1, 0xd2, 0x3b, 0x76, 0x79, 0x65, 0x48, 0xe4, + 0x15, 0x28, 0x32, 0xeb, 0x80, 0x1d, 0xb1, 0xe1, 0x60, 0xe4, 0x1b, 0x6e, 0xba, 0x10, 0x0f, 0x94, + 0x2a, 0x10, 0xc9, 0x2e, 0xac, 0xf1, 0x98, 0x31, 0xa6, 0x7b, 0xe8, 0x7e, 0x2d, 0x8c, 0xd8, 0x8f, + 0x49, 0x05, 0x84, 0x26, 0xdd, 0x1e, 0xb3, 0x32, 0x39, 0x6a, 0x4a, 0x9c, 0x8a, 0xd4, 0x41, 0x45, + 0x9e, 0x32, 0x27, 0x16, 0xad, 0x15, 0xa3, 0xf8, 0xb0, 0x4a, 0xcc, 0xe0, 0x95, 0xa2, 0x0c, 0x87, + 0x4b, 0x0f, 0x02, 0xef, 0x70, 0x78, 0xec, 0x0e, 0x27, 0xbf, 0xbc, 0xe1, 0x8a, 0xbe, 0x71, 0xa2, + 0xe1, 0xfa, 0xbd, 0x02, 0x5b, 0xcc, 0x49, 0x32, 0x2a, 0xd1, 0x48, 0x01, 0xba, 0x51, 0xa2, 0xa1, + 0xf7, 0x33, 0x1e, 0x15, 0x65, 0x07, 0x96, 0x58, 0xb4, 0x1a, 0xb1, 0x32, 0x9e, 0xcd, 0xac, 0x02, + 0xc3, 0xd9, 0xbf, 0xcd, 0xc4, 0x17, 0xe6, 0x29, 0x19, 0x98, 0x82, 0x94, 0xec, 0x43, 0xa9, 0x32, + 0x70, 0x9d, 0xe1, 0x74, 0xd4, 0x39, 0xd9, 0x0b, 0xea, 0x16, 0x6f, 0xcb, 0x4a, 0x8f, 0x91, 0xe1, + 0xcb, 0x2b, 0xee, 0xe4, 0x32, 0x23, 0xd2, 0x09, 0x9d, 0xa7, 0x0a, 0xa8, 0x78, 0xfd, 0xe8, 0x9c, + 0xfe, 0x49, 0x02, 0x91, 0x2e, 0xee, 0x19, 0xc8, 0xbd, 0xab, 0x6c, 0x58, 0xab, 0x3b, 0xc1, 0xa4, + 0x33, 0x76, 0x86, 0x01, 0x46, 0xb9, 0x3c, 0x41, 0x14, 0xb0, 0x4b, 0x22, 0x83, 0x33, 0xaa, 0x4c, + 0x27, 0x21, 0x29, 0x53, 0xc8, 0xc6, 0xd9, 0x51, 0x79, 0x69, 0xd7, 0x1b, 0x3a, 0x03, 0xef, 0xeb, + 0xc2, 0xc7, 0x94, 0xc9, 0x4b, 0x07, 0x02, 0x68, 0x46, 0xe5, 0xda, 0xe7, 0x53, 0xe3, 0xc6, 0x6a, + 0x59, 0x82, 0x25, 0x1e, 0x81, 0x80, 0x79, 0xe4, 0xb7, 0x8d, 0xe6, 0x4e, 0xad, 0xb9, 0xa7, 0x2a, + 0x64, 0x0d, 0xa0, 0x6d, 0xb6, 0x2a, 0x86, 0x65, 0xd1, 0xdf, 0x39, 0xfa, 0x9b, 0xbb, 0xeb, 0xef, + 0x76, 0xeb, 0x6a, 0x5e, 0xf2, 0xd8, 0x2f, 0x68, 0x3f, 0x56, 0xe0, 0x42, 0xf6, 0x50, 0x92, 0x0e, + 0x60, 0xcc, 0x06, 0xfe, 0x96, 0xfe, 0xb1, 0xb9, 0xe3, 0x9e, 0x09, 0x4e, 0xc6, 0x7e, 0x98, 0xb0, + 0x98, 0x02, 0x39, 0xf1, 0xf6, 0xc5, 0x9c, 0x14, 0xbd, 0xbe, 0x99, 0xf3, 0xfa, 0x5a, 0x05, 0xb6, + 0x66, 0xf1, 0x88, 0x37, 0x75, 0x1d, 0x4a, 0x7a, 0xbb, 0x5d, 0xaf, 0x55, 0xf4, 0x4e, 0xad, 0xd5, + 0x54, 0x15, 0xb2, 0x0c, 0x0b, 0x7b, 0x66, 0xab, 0xdb, 0x56, 0x73, 0xda, 0x77, 0x15, 0x58, 0xad, + 0x45, 0x56, 0x67, 0xef, 0x77, 0xf1, 0x7d, 0x3c, 0xb6, 0xf8, 0xb6, 0xc2, 0xe8, 0x26, 0xe1, 0x07, + 0x4e, 0xb4, 0xf2, 0xde, 0xcb, 0xc1, 0x46, 0x8a, 0x86, 0x58, 0xb0, 0xa4, 0xdf, 0xb7, 0x5a, 0xb5, + 0x9d, 0x0a, 0xaf, 0xd9, 0xd5, 0xc8, 0x5c, 0x0a, 0xf3, 0x5d, 0xa5, 0xbe, 0xc2, 0x3c, 0x82, 0x1f, + 0x07, 0xb6, 0xef, 0xf5, 0xa5, 0xe4, 0xb7, 0xd5, 0x33, 0xa6, 0xe0, 0x84, 0x27, 0xd9, 0xd7, 0xa7, + 0x63, 0x17, 0xd9, 0xe6, 0x62, 0x7a, 0xdd, 0x10, 0x9e, 0x66, 0x8c, 0xfe, 0x1b, 0x0e, 0x2d, 0x4f, + 0xb3, 0x8e, 0xf8, 0x91, 0x26, 0x2c, 0xee, 0x79, 0x93, 0xea, 0xf4, 0x11, 0x5f, 0xbf, 0x57, 0xa2, + 0xec, 0x47, 0xd5, 0xe9, 0xa3, 0x34, 0x5b, 0x54, 0x59, 0xb2, 0xe8, 0x3d, 0x31, 0x96, 0x9c, 0x4b, + 0xd2, 0x89, 0xb1, 0x70, 0x2a, 0x27, 0xc6, 0xed, 0x55, 0x28, 0xf1, 0x3b, 0x14, 0x5e, 0x4f, 0x7e, + 0xa8, 0xc0, 0xd6, 0xac, 0x9e, 0xa3, 0xd7, 0xb2, 0x78, 0xb0, 0x82, 0x0b, 0x61, 0x7a, 0x8c, 0x78, + 0x94, 0x02, 0x81, 0x46, 0xde, 0x82, 0x52, 0x2d, 0x08, 0xa6, 0xee, 0xd8, 0x7a, 0xa5, 0x6b, 0xd6, + 0xf8, 0x74, 0x7d, 0xf6, 0x9f, 0xde, 0xbd, 0xba, 0x89, 0x3e, 0x1f, 0x63, 0x3b, 0x78, 0xc5, 0x9e, + 0x8e, 0xbd, 0x58, 0x2a, 0x01, 0x99, 0x82, 0x4a, 0xd1, 0xce, 0xb4, 0xef, 0xb9, 0xe2, 0x0e, 0x21, + 0x1c, 0xba, 0x39, 0x4c, 0x3e, 0xd3, 0x04, 0x4c, 0xfb, 0x96, 0x02, 0x17, 0x67, 0x0f, 0x13, 0x3d, + 0x27, 0x3b, 0xcc, 0xa4, 0x4a, 0xb8, 0x54, 0xe3, 0x39, 0x19, 0xda, 0x5d, 0xc9, 0x3c, 0x05, 0x22, + 0x25, 0x0a, 0x53, 0xe3, 0xe7, 0x52, 0xf9, 0xb0, 0xe3, 0x44, 0x02, 0x51, 0x7b, 0x00, 0x9b, 0x33, + 0x06, 0x95, 0x7c, 0x2a, 0x33, 0xe9, 0x0e, 0xba, 0x29, 0xc9, 0x49, 0x77, 0x62, 0xd9, 0xdb, 0x24, + 0xb8, 0xf6, 0xcf, 0x39, 0xb8, 0x40, 0x57, 0xd7, 0xc0, 0x0d, 0x02, 0x3d, 0xca, 0x4f, 0x4b, 0x77, + 0xc5, 0xd7, 0x60, 0xf1, 0xe8, 0x74, 0xaa, 0x62, 0x86, 0x4e, 0x08, 0xe0, 0x89, 0x25, 0x9c, 0x63, + 0xe8, 0xff, 0xe4, 0x1a, 0xc8, 0xc9, 0xcd, 0xf3, 0x18, 0xde, 0x34, 0xb7, 0xa5, 0x98, 0xcb, 0xa3, + 0x30, 0x0f, 0xf1, 0xeb, 0xb0, 0x80, 0xfa, 0x14, 0x7e, 0x76, 0x08, 0x99, 0x3f, 0xbb, 0x76, 0xa8, + 0x6d, 0x31, 0x19, 0x01, 0xf9, 0x08, 0x40, 0x94, 0x19, 0x82, 0x1f, 0x0e, 0x42, 0xcf, 0x10, 0x26, + 0x87, 0x30, 0x97, 0x8f, 0x0f, 0x1c, 0x9e, 0x6e, 0xa1, 0x0c, 0x1b, 0xa2, 0xc7, 0x47, 0x22, 0x2a, + 0x22, 0x7f, 0xc5, 0x5c, 0x67, 0x05, 0xb5, 0x91, 0x88, 0x8c, 0x78, 0x3d, 0x95, 0xa0, 0x19, 0x83, + 0x23, 0x27, 0xb2, 0x30, 0x5f, 0x4f, 0x65, 0x61, 0x2e, 0x32, 0x2c, 0x39, 0xd5, 0xb2, 0xf6, 0xf7, + 0x39, 0x58, 0xbe, 0x4f, 0xa5, 0x32, 0xd4, 0x35, 0xcc, 0xd7, 0x5d, 0xdc, 0x81, 0x52, 0xdd, 0x77, + 0xf8, 0x73, 0x11, 0xf7, 0x29, 0x61, 0x3e, 0xdd, 0x03, 0xdf, 0x11, 0x2f, 0x4f, 0x81, 0x29, 0x23, + 0x3d, 0xc5, 0x1f, 0xfd, 0x2e, 0x2c, 0xb2, 0xe7, 0x3b, 0xae, 0x46, 0x13, 0x72, 0x79, 0x58, 0xa3, + 0x97, 0x59, 0xb1, 0xf4, 0xc2, 0xc1, 0x9e, 0x00, 0x65, 0x21, 0x91, 0xc7, 0x78, 0x95, 0x34, 0x2b, + 0x0b, 0x27, 0xd3, 0xac, 0x48, 0xb1, 0xec, 0x16, 0x4f, 0x12, 0xcb, 0xee, 0xe2, 0x1b, 0x50, 0x92, + 0xea, 0x73, 0x2a, 0x31, 0xfd, 0x9b, 0x39, 0x58, 0xc5, 0x56, 0x85, 0xb6, 0x3c, 0xbf, 0x9a, 0x7a, + 0xa2, 0x8f, 0xc7, 0xf4, 0x44, 0x5b, 0xf2, 0x78, 0xb1, 0x96, 0xcd, 0x51, 0x10, 0xdd, 0x85, 0x8d, + 0x14, 0x22, 0x79, 0x15, 0x16, 0x68, 0xf5, 0xc5, 0xbd, 0x5a, 0x4d, 0xce, 0x80, 0x28, 0xee, 0x31, + 0x6d, 0x78, 0x60, 0x32, 0x6c, 0xed, 0xdf, 0x14, 0x58, 0xe1, 0x69, 0x47, 0x86, 0x07, 0xfe, 0x53, + 0xbb, 0xf3, 0x46, 0xb2, 0x3b, 0x59, 0x74, 0x15, 0xde, 0x9d, 0xff, 0xd5, 0x9d, 0xf8, 0x46, 0xac, + 0x13, 0x37, 0xc3, 0x28, 0x88, 0xa2, 0x39, 0x73, 0xfa, 0xf0, 0x07, 0x18, 0x17, 0x38, 0x8e, 0x48, + 0xbe, 0x08, 0xcb, 0x4d, 0xf7, 0x71, 0xec, 0x7a, 0x7a, 0x63, 0x06, 0xd3, 0x97, 0x43, 0x44, 0xb6, + 0xa6, 0xf0, 0x64, 0x1f, 0xba, 0x8f, 0xed, 0xd4, 0xcb, 0x61, 0xc4, 0x92, 0xde, 0x50, 0xe3, 0x64, + 0xa7, 0x99, 0xfa, 0xdc, 0xc1, 0x15, 0x03, 0x06, 0x7d, 0x3b, 0x0f, 0x10, 0xf9, 0x06, 0xd2, 0x05, + 0x18, 0x33, 0x9a, 0x10, 0x9a, 0x7d, 0x04, 0xc9, 0x73, 0x5c, 0xd8, 0x52, 0xdc, 0xe0, 0x1a, 0xe8, + 0xdc, 0xec, 0x28, 0x95, 0xa8, 0x8b, 0xae, 0x70, 0x67, 0xb4, 0xbe, 0x3b, 0x70, 0xd8, 0xde, 0x9e, + 0xdf, 0xbe, 0x8e, 0x41, 0x89, 0x43, 0xe8, 0x8c, 0x74, 0xd3, 0xe8, 0xb2, 0xb6, 0x43, 0x11, 0x52, + 0xfe, 0xb6, 0x85, 0xd3, 0xf9, 0xdb, 0xb6, 0x61, 0xd9, 0x1b, 0xbe, 0xed, 0x0e, 0x27, 0xfe, 0xf8, + 0x09, 0xaa, 0xdd, 0x23, 0x7d, 0x1e, 0xed, 0x82, 0x9a, 0x28, 0x63, 0xe3, 0x80, 0x67, 0x6e, 0x88, + 0x2f, 0x0f, 0x43, 0x08, 0x0c, 0xfd, 0x85, 0x17, 0xd4, 0xc5, 0xbb, 0x85, 0xe2, 0xa2, 0xba, 0x74, + 0xb7, 0x50, 0x2c, 0xaa, 0xcb, 0x77, 0x0b, 0xc5, 0x65, 0x15, 0x4c, 0xe9, 0xcd, 0x2c, 0x7c, 0x13, 0x93, 0x9e, 0xb1, 0xe2, 0x4f, 0x54, 0xda, 0xcf, 0x73, 0x40, 0xd2, 0xd5, 0x20, 0x1f, 0x87, 0x12, 0xdb, 0x60, 0xed, 0x71, 0xf0, 0x55, 0xee, 0x6e, 0xc0, 0xc2, 0x2e, 0x49, 0x60, 0x39, 0xec, 0x12, 0x03, 0x9b, 0xc1, 0x57, 0x07, 0xe4, 0x0b, 0x70, 0x16, 0xbb, 0x77, 0xe4, 0x8e, 0x3d, 0xbf, 0x6f, 0x63, 0x8c, 0x5c, 0x67, 0xc0, 0x53, 0x43, 0xbe, 0x84, 0x39, 0x8c, 0xd3, 0xc5, 0x33, 0x86, 0x01, - 0x5d, 0x00, 0xdb, 0x88, 0xd9, 0x66, 0x88, 0xa4, 0x03, 0xaa, 0x4c, 0x7f, 0x38, 0x1d, 0x0c, 0xf8, - 0xc8, 0x96, 0xe9, 0x8d, 0x3e, 0x59, 0x36, 0x83, 0xf1, 0x5a, 0xc4, 0x78, 0x6f, 0x3a, 0x18, 0x90, - 0xd7, 0x00, 0xfc, 0xa1, 0x7d, 0xe2, 0x05, 0x01, 0x7b, 0xcc, 0x09, 0xbd, 0x95, 0x23, 0xa8, 0x3c, + 0x5d, 0x00, 0xdb, 0x88, 0xd9, 0x66, 0x88, 0xa4, 0x03, 0xaa, 0x4c, 0x7f, 0x30, 0x1d, 0x0c, 0xf8, + 0xc8, 0x96, 0xe9, 0x8d, 0x3e, 0x59, 0x36, 0x83, 0xf1, 0x5a, 0xc4, 0x78, 0x77, 0x3a, 0x18, 0x90, + 0xd7, 0x00, 0xfc, 0xa1, 0x7d, 0xec, 0x05, 0x01, 0x7b, 0xcc, 0x09, 0xbd, 0x95, 0x23, 0xa8, 0x3c, 0x18, 0xfe, 0xb0, 0xc1, 0x80, 0xe4, 0x7f, 0x01, 0x46, 0x6b, 0xc0, 0x30, 0x26, 0xcc, 0x1a, 0x89, - 0x67, 0x6f, 0x11, 0xc0, 0xb8, 0x73, 0xf4, 0x91, 0x6b, 0x79, 0x5f, 0x17, 0xae, 0x1e, 0x9f, 0x83, - 0x0d, 0x6e, 0x3c, 0x7c, 0xcf, 0x9b, 0x1c, 0xf3, 0xab, 0xc4, 0xfb, 0xb9, 0x87, 0x48, 0x77, 0x89, - 0xbf, 0x29, 0x00, 0xe8, 0xf7, 0x2c, 0x11, 0x21, 0xec, 0x26, 0x2c, 0xd0, 0x0b, 0x92, 0x50, 0xb4, + 0x67, 0x6f, 0x11, 0xc0, 0xb8, 0x73, 0xf4, 0xa1, 0x6b, 0x79, 0x5f, 0x17, 0xae, 0x1e, 0x9f, 0x83, + 0x0d, 0x6e, 0x3c, 0x7c, 0xdf, 0x9b, 0x1c, 0xf1, 0xab, 0xc4, 0xfb, 0xb9, 0x87, 0x48, 0x77, 0x89, + 0xbf, 0x2e, 0x00, 0xe8, 0xf7, 0x2d, 0x11, 0x21, 0xec, 0x16, 0x2c, 0xd0, 0x0b, 0x92, 0x50, 0xb4, 0xa0, 0x9a, 0x1a, 0xf9, 0xca, 0x6a, 0x6a, 0xc4, 0xa0, 0xab, 0xd1, 0x44, 0xa3, 0x7a, 0xa1, 0x64, 0xc1, 0xd5, 0xc8, 0xec, 0xec, 0x63, 0x11, 0x9a, 0x39, 0x16, 0xa9, 0x03, 0x44, 0x31, 0xbb, 0xb8, 0xc8, 0xbf, 0x11, 0x05, 0xbf, 0xe1, 0x05, 0x3c, 0x4b, 0x44, 0x14, 0xf7, 0x4b, 0x9e, 0x3e, 0x11, - 0x1a, 0xb9, 0x0b, 0x85, 0x8e, 0x13, 0xfa, 0xe2, 0xce, 0x88, 0x64, 0xf6, 0x1c, 0x4f, 0xdd, 0x19, + 0x1a, 0xb9, 0x07, 0x85, 0x8e, 0x13, 0xfa, 0xe2, 0xce, 0x88, 0x64, 0xf6, 0x1c, 0x4f, 0xdd, 0x19, 0x45, 0x33, 0x5b, 0x9b, 0x38, 0xb1, 0x0c, 0xc7, 0xc8, 0x84, 0x18, 0xb0, 0xc8, 0xd3, 0xb2, 0xcf, - 0x88, 0x80, 0xc9, 0xb3, 0xb2, 0xf3, 0xb8, 0xd7, 0x08, 0x94, 0x65, 0x0a, 0x9e, 0x80, 0xfd, 0x36, + 0x88, 0x80, 0xc9, 0xb3, 0xb2, 0xf3, 0xb8, 0xd7, 0x08, 0x94, 0x65, 0x0a, 0x9e, 0x80, 0xfd, 0x0e, 0xe4, 0x2d, 0xab, 0xc1, 0xe3, 0x77, 0xac, 0x46, 0xd7, 0x2f, 0xcb, 0x6a, 0xb0, 0x77, 0xdf, 0x20, - 0x38, 0x91, 0xc8, 0x28, 0x32, 0xf9, 0x04, 0x94, 0x24, 0xa1, 0x98, 0x47, 0xbe, 0xc1, 0x3e, 0x90, - 0xbc, 0x9d, 0xe4, 0x4d, 0x43, 0xc2, 0x26, 0x75, 0x50, 0xef, 0x4e, 0x1f, 0xba, 0xfa, 0x68, 0x84, + 0x38, 0x96, 0xc8, 0x28, 0x32, 0xf9, 0x04, 0x94, 0x24, 0xa1, 0x98, 0x47, 0xbe, 0xc1, 0x3e, 0x90, + 0xbc, 0x9d, 0xe4, 0x4d, 0x43, 0xc2, 0x26, 0x75, 0x50, 0xef, 0x4d, 0x1f, 0xb9, 0xfa, 0x68, 0x84, 0x6e, 0x90, 0x6f, 0xbb, 0x63, 0x26, 0xb6, 0x15, 0xa3, 0x90, 0xd1, 0xe8, 0x23, 0xd1, 0x17, 0xa5, - 0xb2, 0xb2, 0x29, 0x49, 0x49, 0xda, 0xb0, 0x61, 0xb9, 0x93, 0xe9, 0x88, 0xd9, 0xd7, 0xec, 0xf9, - 0x63, 0x7a, 0xbf, 0x61, 0x71, 0x72, 0x30, 0xba, 0x6e, 0x40, 0x0b, 0x85, 0x51, 0xd3, 0xa1, 0x3f, + 0xb2, 0xb2, 0x29, 0x49, 0x49, 0xda, 0xb0, 0x61, 0xb9, 0x93, 0xe9, 0x88, 0xd9, 0xd7, 0xec, 0xfa, + 0x63, 0x7a, 0xbf, 0x61, 0x71, 0x72, 0x30, 0xba, 0x6e, 0x40, 0x0b, 0x85, 0x51, 0xd3, 0x81, 0x3f, 0x4e, 0xdc, 0x75, 0xd2, 0xc4, 0x9a, 0x2b, 0x0f, 0x39, 0x3d, 0x55, 0xe3, 0xb7, 0x26, 0x3c, 0x55, 0xc5, 0xad, 0x29, 0xba, 0x2b, 0x7d, 0x24, 0x23, 0x96, 0x1b, 0xbe, 0x0c, 0x4a, 0xb1, 0xdc, 0x62, - 0x11, 0xdc, 0xbe, 0x5f, 0x90, 0xc2, 0x89, 0xf2, 0xb1, 0x78, 0x13, 0xe0, 0x8e, 0xef, 0x0d, 0x1b, - 0xee, 0xe4, 0xd8, 0xef, 0x4b, 0x21, 0xe5, 0x4a, 0x5f, 0xf6, 0xbd, 0xa1, 0x7d, 0x82, 0xe0, 0x9f, - 0xbf, 0x7b, 0x45, 0x42, 0x32, 0xa5, 0xff, 0xc9, 0x87, 0x61, 0x99, 0xfe, 0xea, 0x44, 0x56, 0x42, + 0x11, 0xdc, 0xbe, 0x5f, 0x90, 0xc2, 0x89, 0xf2, 0xb1, 0x78, 0x13, 0xe0, 0xae, 0xef, 0x0d, 0x1b, + 0xee, 0xe4, 0xc8, 0xef, 0x4b, 0x21, 0xe5, 0x4a, 0x5f, 0xf6, 0xbd, 0xa1, 0x7d, 0x8c, 0xe0, 0x9f, + 0xbf, 0x7b, 0x55, 0x42, 0x32, 0xa5, 0xff, 0xc9, 0x87, 0x61, 0x99, 0xfe, 0xea, 0x44, 0x56, 0x42, 0x4c, 0x27, 0x8b, 0xd4, 0x2c, 0xe9, 0x46, 0x84, 0x40, 0xde, 0xc0, 0x34, 0x33, 0xde, 0x68, 0x22, 0x09, 0xaf, 0x22, 0xa7, 0x8c, 0x37, 0x9a, 0x24, 0x23, 0x44, 0x4b, 0xc8, 0xa4, 0x1a, 0x56, 0x5d, 0x64, 0x86, 0xe2, 0xd9, 0x6c, 0x50, 0xf1, 0xc8, 0xe7, 0x9a, 0x2d, 0x42, 0xd3, 0xca, 0x29, 0x7f, - 0x13, 0x64, 0x58, 0x09, 0xab, 0xba, 0xcb, 0x5e, 0x8a, 0xb8, 0x50, 0xcb, 0x2a, 0x11, 0x1c, 0xf7, - 0xed, 0x1e, 0x82, 0x63, 0x95, 0x08, 0x91, 0xc9, 0x0e, 0xac, 0x33, 0x19, 0x3f, 0xcc, 0x30, 0xc9, - 0x45, 0x5c, 0xdc, 0xdb, 0xa2, 0x14, 0x94, 0xf2, 0xe7, 0x13, 0x04, 0x64, 0x0f, 0x16, 0xf0, 0xae, - 0xc9, 0x5d, 0x03, 0x2e, 0xca, 0x6a, 0x82, 0xe4, 0x3a, 0xc2, 0x7d, 0x05, 0x15, 0x04, 0xf2, 0xbe, + 0x13, 0x64, 0x58, 0x09, 0xab, 0xba, 0xc3, 0x5e, 0x8a, 0xb8, 0x50, 0xcb, 0x2a, 0x11, 0x1c, 0xf5, + 0xed, 0x1e, 0x82, 0x63, 0x95, 0x08, 0x91, 0xc9, 0x36, 0xac, 0x33, 0x19, 0x3f, 0xcc, 0x30, 0xc9, + 0x45, 0x5c, 0xdc, 0xdb, 0xa2, 0x14, 0x94, 0xf2, 0xe7, 0x13, 0x04, 0x64, 0x17, 0x16, 0xf0, 0xae, + 0xc9, 0x5d, 0x03, 0x2e, 0xc9, 0x6a, 0x82, 0xe4, 0x3a, 0xc2, 0x7d, 0x05, 0x15, 0x04, 0xf2, 0xbe, 0x82, 0xa8, 0xe4, 0xb3, 0x00, 0xc6, 0x70, 0xec, 0x0f, 0x06, 0x18, 0x3c, 0xb9, 0x88, 0x57, 0xa9, 0x67, 0xe3, 0xeb, 0x11, 0xb9, 0x44, 0x48, 0x3c, 0xd0, 0x1f, 0xfe, 0xb6, 0x13, 0x21, 0x96, 0x25, 0x5e, 0x5a, 0x0d, 0x16, 0xd9, 0x62, 0xc4, 0x40, 0xe4, 0x3c, 0xb5, 0x8a, 0x14, 0xc6, 0x9a, 0x05, - 0x22, 0xe7, 0xf0, 0x74, 0x20, 0x72, 0x89, 0x40, 0xbb, 0x0b, 0xe7, 0xb2, 0x1a, 0x16, 0xbb, 0x1d, - 0x2b, 0xa7, 0xbd, 0x1d, 0x7f, 0x2f, 0x0f, 0x2b, 0xc8, 0x4d, 0xec, 0xc2, 0x3a, 0xac, 0x5a, 0xd3, - 0x87, 0x61, 0x94, 0x2e, 0xb1, 0x1b, 0x63, 0xfd, 0x02, 0xb9, 0x40, 0x7e, 0xc3, 0x8b, 0x51, 0x10, - 0x03, 0xd6, 0xc4, 0x49, 0xb0, 0x2f, 0x3c, 0x07, 0xc2, 0x18, 0xe0, 0x22, 0xd2, 0x64, 0x3a, 0xc3, - 0x6e, 0x82, 0x28, 0x3a, 0x0f, 0xf2, 0x4f, 0x73, 0x1e, 0x14, 0x4e, 0x75, 0x1e, 0x3c, 0x80, 0x15, - 0xf1, 0x35, 0xdc, 0xc9, 0x17, 0xde, 0xdf, 0x4e, 0x1e, 0x63, 0x46, 0xea, 0xe1, 0x8e, 0xbe, 0x38, - 0x77, 0x47, 0xc7, 0x87, 0x51, 0xb1, 0xca, 0x46, 0x08, 0x4b, 0x6f, 0xec, 0x98, 0x82, 0x72, 0xbf, - 0xd2, 0xfe, 0x05, 0x4e, 0xc9, 0x57, 0x61, 0xb9, 0xee, 0x8b, 0x37, 0x31, 0xe9, 0x31, 0x62, 0x20, - 0x80, 0xb2, 0xb8, 0x10, 0x62, 0x86, 0xa7, 0x5b, 0xfe, 0x83, 0x38, 0xdd, 0xde, 0x00, 0xe0, 0x2e, - 0x29, 0x51, 0xea, 0x38, 0x5c, 0x32, 0x22, 0x42, 0x49, 0xfc, 0x4d, 0x44, 0x42, 0xa6, 0xbb, 0x13, - 0x37, 0xb7, 0xd1, 0x7b, 0x3d, 0x7f, 0x3a, 0x9c, 0xc4, 0x72, 0x2d, 0x0b, 0x0f, 0x56, 0x87, 0x97, - 0xc9, 0xdb, 0x43, 0x82, 0xec, 0x83, 0x1d, 0x10, 0xf2, 0x99, 0xd0, 0xf8, 0x71, 0x69, 0x5e, 0x0f, - 0x69, 0xa9, 0x1e, 0x9a, 0x69, 0xf2, 0xa8, 0xfd, 0x58, 0x91, 0x13, 0x30, 0xfc, 0x02, 0x43, 0xfd, - 0x3a, 0x40, 0x68, 0x94, 0x20, 0xc6, 0x9a, 0xdd, 0x97, 0x42, 0xa8, 0xdc, 0xcb, 0x11, 0xae, 0xd4, - 0x9a, 0xfc, 0x07, 0xd5, 0x9a, 0x0e, 0x94, 0x5a, 0x5f, 0x99, 0x38, 0x91, 0x15, 0x0b, 0x58, 0xa1, - 0x24, 0x8b, 0x3b, 0x53, 0x7e, 0xe7, 0x05, 0x3c, 0x1b, 0x22, 0x39, 0x78, 0x86, 0x08, 0x2c, 0x11, - 0x6a, 0x7f, 0xa6, 0xc0, 0xba, 0xec, 0x76, 0xff, 0x78, 0xd8, 0x23, 0x9f, 0x62, 0xf1, 0x60, 0x95, - 0xd8, 0x95, 0x45, 0x42, 0xa2, 0x5b, 0xee, 0xe3, 0x61, 0x8f, 0x09, 0x40, 0xce, 0x23, 0xb9, 0xb2, - 0x94, 0x90, 0x3c, 0x84, 0x95, 0xb6, 0x3f, 0x18, 0x50, 0xb1, 0x66, 0xfc, 0x36, 0xbf, 0x00, 0x50, - 0x46, 0xc9, 0xa7, 0x11, 0x51, 0xa1, 0x9d, 0xe7, 0xf9, 0x3d, 0x77, 0x73, 0x44, 0xf7, 0x7b, 0x8f, - 0xd3, 0x45, 0x6c, 0xdf, 0x41, 0x3f, 0x39, 0x99, 0xa7, 0xf6, 0x53, 0x05, 0x48, 0xba, 0x4a, 0xf2, - 0x96, 0xa5, 0xfc, 0x37, 0x88, 0xb0, 0x09, 0xd1, 0xaf, 0xf0, 0x34, 0xa2, 0x5f, 0xf9, 0xb7, 0x15, - 0x58, 0xaf, 0xe9, 0x0d, 0x9e, 0x92, 0x81, 0xbd, 0xe0, 0x5c, 0x85, 0x67, 0x6b, 0x7a, 0xc3, 0x6e, - 0xb7, 0xea, 0xb5, 0xca, 0x7d, 0x3b, 0x33, 0xd2, 0xf2, 0xb3, 0xf0, 0x4c, 0x1a, 0x25, 0x7a, 0xe9, - 0xb9, 0x04, 0x5b, 0xe9, 0x62, 0x11, 0x8d, 0x39, 0x9b, 0x58, 0x04, 0x6e, 0xce, 0x97, 0xdf, 0x82, - 0x75, 0x11, 0x79, 0xb8, 0x53, 0xb7, 0x30, 0xb7, 0xc1, 0x3a, 0x94, 0x0e, 0x0c, 0xb3, 0xb6, 0x77, - 0xdf, 0xde, 0xeb, 0xd6, 0xeb, 0xea, 0x19, 0xb2, 0x0a, 0xcb, 0x1c, 0x50, 0xd1, 0x55, 0x85, 0xac, - 0x40, 0xb1, 0xd6, 0xb4, 0x8c, 0x4a, 0xd7, 0x34, 0xd4, 0x5c, 0xf9, 0x2d, 0x58, 0x6b, 0x8f, 0xbd, - 0xb7, 0x9d, 0x89, 0x7b, 0xd7, 0x7d, 0x8c, 0x0f, 0x35, 0x4b, 0x90, 0x37, 0xf5, 0x7b, 0xea, 0x19, - 0x02, 0xb0, 0xd8, 0xbe, 0x5b, 0xb1, 0x6e, 0xdd, 0x52, 0x15, 0x52, 0x82, 0xa5, 0xfd, 0x4a, 0xdb, - 0xbe, 0xdb, 0xb0, 0xd4, 0x1c, 0xfd, 0xa1, 0xdf, 0xb3, 0xf0, 0x47, 0xbe, 0xfc, 0x51, 0xd8, 0x40, - 0x81, 0xa4, 0xee, 0x05, 0x13, 0x77, 0xe8, 0x8e, 0xb1, 0x0e, 0x2b, 0x50, 0xb4, 0x5c, 0xba, 0x93, - 0x4c, 0x5c, 0x56, 0x81, 0xc6, 0x74, 0x30, 0xf1, 0x46, 0x03, 0xf7, 0x6b, 0xaa, 0x52, 0x7e, 0x03, - 0xd6, 0x4d, 0x7f, 0x3a, 0xf1, 0x86, 0x47, 0xd6, 0x84, 0x62, 0x1c, 0x3d, 0x26, 0xe7, 0x61, 0xa3, - 0xdb, 0xd4, 0x1b, 0x3b, 0xb5, 0xfd, 0x6e, 0xab, 0x6b, 0xd9, 0x0d, 0xbd, 0x53, 0xa9, 0xb2, 0x67, - 0xa2, 0x46, 0xcb, 0xea, 0xd8, 0xa6, 0x51, 0x31, 0x9a, 0x1d, 0x55, 0x29, 0x7f, 0x07, 0x75, 0x2b, - 0x3d, 0x7f, 0xd8, 0xdf, 0x73, 0x7a, 0x13, 0x7f, 0x8c, 0x15, 0xd6, 0xe0, 0xb2, 0x65, 0x54, 0x5a, - 0xcd, 0x5d, 0x7b, 0x4f, 0xaf, 0x74, 0x5a, 0x66, 0x56, 0xa8, 0xef, 0x6d, 0xb8, 0x90, 0x81, 0xd3, - 0xea, 0xb4, 0x55, 0x85, 0x5c, 0x81, 0x8b, 0x19, 0x65, 0xf7, 0x8c, 0x1d, 0xbd, 0xdb, 0xa9, 0x36, - 0xd5, 0xdc, 0x0c, 0x62, 0xcb, 0x6a, 0xa9, 0xf9, 0xf2, 0xff, 0x57, 0x60, 0xad, 0x1b, 0x70, 0x93, - 0xf3, 0x2e, 0x7a, 0x9b, 0x3e, 0x07, 0x97, 0xba, 0x96, 0x61, 0xda, 0x9d, 0xd6, 0x5d, 0xa3, 0x69, - 0x77, 0x2d, 0x7d, 0x3f, 0x59, 0x9b, 0x2b, 0x70, 0x51, 0xc2, 0x30, 0x8d, 0x4a, 0xeb, 0xc0, 0x30, - 0xed, 0xb6, 0x6e, 0x59, 0xf7, 0x5a, 0xe6, 0xae, 0xaa, 0xd0, 0x2f, 0x66, 0x20, 0x34, 0xf6, 0x74, - 0x56, 0x9b, 0x58, 0x59, 0xd3, 0xb8, 0xa7, 0xd7, 0xed, 0x9d, 0x56, 0x47, 0xcd, 0x97, 0x1b, 0xf4, - 0x7c, 0xc7, 0x80, 0xbb, 0xcc, 0xb2, 0xb0, 0x08, 0x85, 0x66, 0xab, 0x69, 0x24, 0x1f, 0x17, 0x57, - 0xa0, 0xa8, 0xb7, 0xdb, 0x66, 0xeb, 0x00, 0xa7, 0x18, 0xc0, 0xe2, 0xae, 0xd1, 0xa4, 0x35, 0xcb, - 0xd3, 0x92, 0xb6, 0xd9, 0x6a, 0xb4, 0x3a, 0xc6, 0xae, 0x5a, 0x28, 0x9b, 0x62, 0x09, 0x0b, 0xa6, - 0x3d, 0x9f, 0xbd, 0xe4, 0xed, 0x1a, 0x7b, 0x7a, 0xb7, 0xde, 0xe1, 0x43, 0x74, 0xdf, 0x36, 0x8d, - 0xcf, 0x74, 0x0d, 0xab, 0x63, 0xa9, 0x0a, 0x51, 0x61, 0xa5, 0x69, 0x18, 0xbb, 0x96, 0x6d, 0x1a, - 0x07, 0x35, 0xe3, 0x9e, 0x9a, 0xa3, 0x3c, 0xd9, 0xff, 0xf4, 0x0b, 0xe5, 0xef, 0x2b, 0x40, 0x58, - 0xb0, 0x62, 0x91, 0x01, 0x07, 0x67, 0xcc, 0x65, 0xd8, 0xae, 0xd2, 0xa1, 0xc6, 0xa6, 0x35, 0x5a, - 0xbb, 0xc9, 0x2e, 0xbb, 0x00, 0x24, 0x51, 0xde, 0xda, 0xdb, 0x53, 0x15, 0x72, 0x11, 0xce, 0x26, - 0xe0, 0xbb, 0x66, 0xab, 0xad, 0xe6, 0xb6, 0x73, 0x45, 0x85, 0x6c, 0xa6, 0x0a, 0xef, 0x1a, 0x46, - 0x5b, 0xcd, 0xd3, 0x21, 0x4a, 0x14, 0x88, 0x25, 0xc1, 0xc8, 0x0b, 0xe5, 0x6f, 0x29, 0x70, 0x81, - 0x55, 0x53, 0xac, 0xaf, 0xb0, 0xaa, 0x97, 0x60, 0x8b, 0x87, 0x60, 0xcf, 0xaa, 0xe8, 0x39, 0x50, - 0x63, 0xa5, 0xac, 0x9a, 0xe7, 0x61, 0x23, 0x06, 0xc5, 0x7a, 0xe4, 0xe8, 0xee, 0x11, 0x03, 0xef, - 0x18, 0x56, 0xc7, 0x36, 0xf6, 0xf6, 0x5a, 0x66, 0x87, 0x55, 0x24, 0x5f, 0xd6, 0x60, 0xa3, 0xe2, - 0x8e, 0x27, 0xf4, 0xea, 0x35, 0x0c, 0x3c, 0x7f, 0x88, 0x55, 0x58, 0x85, 0x65, 0xe3, 0xb3, 0x1d, - 0xa3, 0x69, 0xd5, 0x5a, 0x4d, 0xf5, 0x4c, 0xf9, 0x52, 0x02, 0x47, 0xac, 0x63, 0xcb, 0xaa, 0xaa, - 0x67, 0xca, 0x0e, 0xac, 0x0a, 0xc3, 0x6b, 0x36, 0x2b, 0x2e, 0xc3, 0xb6, 0x98, 0x6b, 0xb8, 0xa3, - 0x24, 0x9b, 0xb0, 0x05, 0xe7, 0xd2, 0xe5, 0x46, 0x47, 0x55, 0xe8, 0x28, 0x24, 0x4a, 0x28, 0x3c, - 0x57, 0xfe, 0xbf, 0x0a, 0xac, 0x86, 0x8f, 0x26, 0xa8, 0xa6, 0xbd, 0x02, 0x17, 0x1b, 0x7b, 0xba, - 0xbd, 0x6b, 0x1c, 0xd4, 0x2a, 0x86, 0x7d, 0xb7, 0xd6, 0xdc, 0x4d, 0x7c, 0xe4, 0x19, 0x38, 0x9f, - 0x81, 0x80, 0x5f, 0xd9, 0x82, 0x73, 0xc9, 0xa2, 0x0e, 0x5d, 0xaa, 0x39, 0xda, 0xf5, 0xc9, 0x92, - 0x70, 0x9d, 0xe6, 0xcb, 0x07, 0xb0, 0x66, 0xe9, 0x8d, 0xfa, 0x9e, 0x3f, 0xee, 0xb9, 0xfa, 0x74, - 0x72, 0x3c, 0x24, 0x17, 0x61, 0x73, 0xaf, 0x65, 0x56, 0x0c, 0x1b, 0x51, 0x12, 0x35, 0x38, 0x0b, - 0xeb, 0x72, 0xe1, 0x7d, 0x83, 0x4e, 0x5f, 0x02, 0x6b, 0x32, 0xb0, 0xd9, 0x52, 0x73, 0xe5, 0xcf, - 0xc3, 0x4a, 0x2c, 0x11, 0xde, 0x26, 0x9c, 0x95, 0x7f, 0xb7, 0xdd, 0x61, 0xdf, 0x1b, 0x1e, 0xa9, - 0x67, 0x92, 0x05, 0xe6, 0x74, 0x38, 0xa4, 0x05, 0xb8, 0x9e, 0xe5, 0x82, 0x8e, 0x3b, 0x3e, 0xf1, - 0x86, 0xce, 0xc4, 0xed, 0xab, 0xb9, 0xf2, 0xcb, 0xb0, 0x1a, 0x0b, 0xbf, 0x4d, 0x07, 0xae, 0xde, - 0xe2, 0x1b, 0x70, 0xc3, 0xd8, 0xad, 0x75, 0x1b, 0xea, 0x02, 0x5d, 0xc9, 0xd5, 0xda, 0x7e, 0x55, - 0x85, 0xf2, 0x77, 0x15, 0x7a, 0xcf, 0xc0, 0xa4, 0x3a, 0x8d, 0x3d, 0x5d, 0x0c, 0x35, 0x9d, 0x66, - 0x2c, 0xa8, 0xbf, 0x61, 0x59, 0xec, 0x4d, 0xfd, 0x12, 0x6c, 0xf1, 0x1f, 0xb6, 0xde, 0xdc, 0xb5, - 0xab, 0xba, 0xb9, 0x7b, 0x4f, 0x37, 0xe9, 0xdc, 0xbb, 0xaf, 0xe6, 0x70, 0x41, 0x49, 0x10, 0xbb, - 0xd3, 0xea, 0x56, 0xaa, 0x6a, 0x9e, 0xce, 0xdf, 0x18, 0xbc, 0x5d, 0x6b, 0xaa, 0x05, 0x5c, 0x9e, - 0x29, 0x6c, 0x64, 0x4b, 0xcb, 0x17, 0xca, 0xef, 0x29, 0xb0, 0x69, 0x79, 0x47, 0x43, 0x67, 0x32, - 0x1d, 0xbb, 0xfa, 0xe0, 0xc8, 0x1f, 0x7b, 0x93, 0xe3, 0x13, 0x6b, 0xea, 0x4d, 0x5c, 0x72, 0x13, - 0x5e, 0xb0, 0x6a, 0xfb, 0x4d, 0xbd, 0x43, 0x97, 0x97, 0x5e, 0xdf, 0x6f, 0x99, 0xb5, 0x4e, 0xb5, - 0x61, 0x5b, 0xdd, 0x5a, 0x6a, 0xe6, 0x5d, 0x83, 0xe7, 0x66, 0xa3, 0xd6, 0x8d, 0x7d, 0xbd, 0x72, - 0x5f, 0x55, 0xe6, 0x33, 0xdc, 0xd1, 0xeb, 0x7a, 0xb3, 0x62, 0xec, 0xda, 0x07, 0xb7, 0xd4, 0x1c, - 0x79, 0x01, 0xae, 0xce, 0x46, 0xdd, 0xab, 0xb5, 0x2d, 0x8a, 0x96, 0x9f, 0xff, 0xdd, 0xaa, 0xd5, - 0xa0, 0x58, 0x85, 0xf2, 0x1f, 0x2a, 0xb0, 0x35, 0x2b, 0x06, 0x13, 0xb9, 0x0e, 0x9a, 0xd1, 0xec, - 0x98, 0x7a, 0x6d, 0xd7, 0xae, 0x98, 0xc6, 0xae, 0xd1, 0xec, 0xd4, 0xf4, 0xba, 0x65, 0x5b, 0xad, - 0x2e, 0x9d, 0x4d, 0x91, 0xe9, 0xc3, 0xf3, 0x70, 0x65, 0x0e, 0x5e, 0xab, 0xb6, 0x5b, 0x51, 0x15, - 0x72, 0x0b, 0x5e, 0x9a, 0x83, 0x64, 0xdd, 0xb7, 0x3a, 0x46, 0x43, 0x2e, 0x51, 0x73, 0xe5, 0x0a, - 0x6c, 0xcf, 0x0e, 0xd2, 0x42, 0xb7, 0xe9, 0x78, 0x4f, 0x17, 0xa1, 0xb0, 0x4b, 0x4f, 0x86, 0x58, - 0xee, 0x87, 0xb2, 0x07, 0x6a, 0x32, 0xce, 0x42, 0xca, 0x46, 0xc5, 0xec, 0x36, 0x9b, 0xec, 0x18, - 0x59, 0x87, 0x52, 0xab, 0x53, 0x35, 0x4c, 0x9e, 0x3d, 0x03, 0xd3, 0x65, 0x74, 0x9b, 0x74, 0xe1, - 0xb4, 0xcc, 0xda, 0xe7, 0xf0, 0x3c, 0xd9, 0x82, 0x73, 0x56, 0x5d, 0xaf, 0xdc, 0xb5, 0x9b, 0xad, - 0x8e, 0x5d, 0x6b, 0xda, 0x95, 0xaa, 0xde, 0x6c, 0x1a, 0x75, 0x15, 0xb0, 0x33, 0x67, 0xf9, 0x56, - 0x92, 0x0f, 0xc3, 0x8d, 0xd6, 0xdd, 0x8e, 0x6e, 0xb7, 0xeb, 0xdd, 0xfd, 0x5a, 0xd3, 0xb6, 0xee, - 0x37, 0x2b, 0x42, 0xf6, 0xa9, 0xa4, 0xb7, 0xdc, 0x1b, 0x70, 0x6d, 0x2e, 0x76, 0x94, 0xe7, 0xe2, - 0x3a, 0x68, 0x73, 0x31, 0x79, 0x43, 0xca, 0x3f, 0x51, 0xe0, 0xe2, 0x9c, 0x37, 0x64, 0xf2, 0x12, - 0xdc, 0xac, 0x1a, 0xfa, 0x6e, 0xdd, 0xb0, 0x2c, 0xdc, 0x28, 0xe8, 0x30, 0x30, 0x5b, 0x96, 0xcc, - 0x0d, 0xf5, 0x26, 0xbc, 0x30, 0x1f, 0x3d, 0x3a, 0x9a, 0x6f, 0xc0, 0xb5, 0xf9, 0xa8, 0xfc, 0xa8, - 0xce, 0x91, 0x32, 0x5c, 0x9f, 0x8f, 0x19, 0x1e, 0xf1, 0xf9, 0xf2, 0x6f, 0x2a, 0x70, 0x21, 0x5b, - 0x91, 0x43, 0xeb, 0x56, 0x6b, 0x5a, 0x1d, 0xbd, 0x5e, 0xb7, 0xdb, 0xba, 0xa9, 0x37, 0x6c, 0xa3, - 0x69, 0xb6, 0xea, 0xf5, 0xac, 0xa3, 0xed, 0x1a, 0x3c, 0x37, 0x1b, 0xd5, 0xaa, 0x98, 0xb5, 0x36, - 0xdd, 0xbd, 0x35, 0xb8, 0x3c, 0x1b, 0xcb, 0xa8, 0x55, 0x0c, 0x35, 0xb7, 0xf3, 0xe6, 0x8f, 0xfe, - 0xfe, 0xf2, 0x99, 0x1f, 0xbd, 0x77, 0x59, 0xf9, 0xe9, 0x7b, 0x97, 0x95, 0xbf, 0x7b, 0xef, 0xb2, - 0xf2, 0xb9, 0x17, 0x4f, 0x97, 0x22, 0x0a, 0xe5, 0xfe, 0x87, 0x8b, 0x78, 0x43, 0x79, 0xe5, 0x3f, - 0x03, 0x00, 0x00, 0xff, 0xff, 0x09, 0xed, 0xed, 0x8d, 0x2e, 0xbf, 0x01, 0x00, + 0x22, 0xe7, 0xf0, 0x74, 0x20, 0x72, 0x89, 0x40, 0xbb, 0x07, 0xe7, 0xb2, 0x1a, 0x16, 0xbb, 0x1d, + 0x2b, 0x27, 0xbd, 0x1d, 0x7f, 0x2f, 0x0f, 0x2b, 0xc8, 0x4d, 0xec, 0xc2, 0x3a, 0xac, 0x5a, 0xd3, + 0x47, 0x61, 0x94, 0x2e, 0xb1, 0x1b, 0x63, 0xfd, 0x02, 0xb9, 0x40, 0x7e, 0xc3, 0x8b, 0x51, 0x10, + 0x03, 0xd6, 0xc4, 0x49, 0xb0, 0x27, 0x3c, 0x07, 0xc2, 0x18, 0xe0, 0x22, 0xd2, 0x64, 0x3a, 0xc3, + 0x6e, 0x82, 0x28, 0x3a, 0x0f, 0xf2, 0xa7, 0x39, 0x0f, 0x0a, 0x27, 0x3a, 0x0f, 0x1e, 0xc2, 0x8a, + 0xf8, 0x1a, 0xee, 0xe4, 0x0b, 0xef, 0x6f, 0x27, 0x8f, 0x31, 0x23, 0xf5, 0x70, 0x47, 0x5f, 0x9c, + 0xbb, 0xa3, 0xe3, 0xc3, 0xa8, 0x58, 0x65, 0x23, 0x84, 0xa5, 0x37, 0x76, 0x4c, 0x41, 0xb9, 0x57, + 0x69, 0xff, 0x02, 0xa7, 0xe4, 0xab, 0xb0, 0x5c, 0xf7, 0xc5, 0x9b, 0x98, 0xf4, 0x18, 0x31, 0x10, + 0x40, 0x59, 0x5c, 0x08, 0x31, 0xc3, 0xd3, 0x2d, 0xff, 0x41, 0x9c, 0x6e, 0x6f, 0x00, 0x70, 0x97, + 0x94, 0x28, 0x75, 0x1c, 0x2e, 0x19, 0x11, 0xa1, 0x24, 0xfe, 0x26, 0x22, 0x21, 0xd3, 0xdd, 0x89, + 0x9b, 0xdb, 0xe8, 0xbd, 0x9e, 0x3f, 0x1d, 0x4e, 0x62, 0xb9, 0x96, 0x85, 0x07, 0xab, 0xc3, 0xcb, + 0xe4, 0xed, 0x21, 0x41, 0xf6, 0xc1, 0x0e, 0x08, 0xf9, 0x4c, 0x68, 0xfc, 0xb8, 0x34, 0xaf, 0x87, + 0xb4, 0x54, 0x0f, 0xcd, 0x34, 0x79, 0xd4, 0x7e, 0xac, 0xc8, 0x09, 0x18, 0x7e, 0x81, 0xa1, 0x7e, + 0x1d, 0x20, 0x34, 0x4a, 0x10, 0x63, 0xcd, 0xee, 0x4b, 0x21, 0x54, 0xee, 0xe5, 0x08, 0x57, 0x6a, + 0x4d, 0xfe, 0x83, 0x6a, 0x4d, 0x07, 0x4a, 0xad, 0xaf, 0x4c, 0x9c, 0xc8, 0x8a, 0x05, 0xac, 0x50, + 0x92, 0xc5, 0x9d, 0x29, 0xbf, 0xfd, 0x02, 0x9e, 0x0d, 0x91, 0x1c, 0x3c, 0x43, 0x04, 0x96, 0x08, + 0xb5, 0xff, 0x50, 0x60, 0x5d, 0x76, 0xbb, 0x7f, 0x32, 0xec, 0x91, 0x4f, 0xb1, 0x78, 0xb0, 0x4a, + 0xec, 0xca, 0x22, 0x21, 0xd1, 0x2d, 0xf7, 0xc9, 0xb0, 0xc7, 0x04, 0x20, 0xe7, 0xb1, 0x5c, 0x59, + 0x4a, 0x48, 0x1e, 0xc1, 0x4a, 0xdb, 0x1f, 0x0c, 0xa8, 0x58, 0x33, 0x7e, 0x9b, 0x5f, 0x00, 0x28, + 0xa3, 0xe4, 0xd3, 0x88, 0xa8, 0xd0, 0xf6, 0xf3, 0xfc, 0x9e, 0xbb, 0x39, 0xa2, 0xfb, 0xbd, 0xc7, + 0xe9, 0x22, 0xb6, 0xef, 0xa0, 0x9f, 0x9c, 0xcc, 0x33, 0x3a, 0x9b, 0xe2, 0x89, 0x04, 0xe4, 0x5a, + 0xd2, 0x62, 0xac, 0xe7, 0x9c, 0xb3, 0x49, 0xfb, 0xa9, 0x02, 0x24, 0xdd, 0x34, 0x79, 0xeb, 0x53, + 0xfe, 0x1b, 0x44, 0xe1, 0x84, 0x08, 0x59, 0x38, 0x8d, 0x08, 0x49, 0xa7, 0xfd, 0xb9, 0xac, 0x7e, + 0x38, 0x7d, 0xa3, 0x0c, 0x58, 0x93, 0xcf, 0xa0, 0x50, 0x74, 0xc3, 0x23, 0x47, 0x3e, 0xb6, 0xe2, + 0xe7, 0x60, 0x82, 0x28, 0xd9, 0x9a, 0xfc, 0x69, 0x5a, 0x53, 0xfe, 0x6d, 0x05, 0xd6, 0x6b, 0x7a, + 0x83, 0x27, 0xaa, 0x60, 0xef, 0x5a, 0xd7, 0xe0, 0xd9, 0x9a, 0xde, 0xb0, 0xdb, 0xad, 0x7a, 0xad, + 0xf2, 0xc0, 0xce, 0x8c, 0x3f, 0xfd, 0x2c, 0x3c, 0x93, 0x46, 0x89, 0xde, 0xbf, 0x2e, 0xc3, 0x56, + 0xba, 0x58, 0xc4, 0xa8, 0xce, 0x26, 0x16, 0xe1, 0xac, 0xf3, 0xe5, 0xb7, 0x60, 0x5d, 0xc4, 0x63, + 0xee, 0xd4, 0x2d, 0xcc, 0xf8, 0xb0, 0x0e, 0xa5, 0x7d, 0xc3, 0xac, 0xed, 0x3e, 0xb0, 0x77, 0xbb, + 0xf5, 0xba, 0x7a, 0x86, 0xac, 0xc2, 0x32, 0x07, 0x54, 0x74, 0x55, 0x21, 0x2b, 0x50, 0xac, 0x35, + 0x2d, 0xa3, 0xd2, 0x35, 0x0d, 0x35, 0x57, 0x7e, 0x0b, 0xd6, 0xda, 0x63, 0xef, 0x6d, 0x67, 0xe2, + 0xde, 0x73, 0x9f, 0xe0, 0xf3, 0xd5, 0x12, 0xe4, 0x4d, 0xfd, 0xbe, 0x7a, 0x86, 0x00, 0x2c, 0xb6, + 0xef, 0x55, 0xac, 0xdb, 0xb7, 0x55, 0x85, 0x94, 0x60, 0x69, 0xaf, 0xd2, 0xb6, 0xef, 0x35, 0x2c, + 0x35, 0x47, 0x7f, 0xe8, 0xf7, 0x2d, 0xfc, 0x91, 0x2f, 0x7f, 0x14, 0x36, 0x50, 0x4c, 0xab, 0x7b, + 0xc1, 0xc4, 0x1d, 0xba, 0x63, 0xac, 0xc3, 0x0a, 0x14, 0x2d, 0x97, 0xee, 0xaf, 0x13, 0x97, 0x55, + 0xa0, 0x31, 0x1d, 0x4c, 0xbc, 0xd1, 0xc0, 0xfd, 0x9a, 0xaa, 0x94, 0xdf, 0x80, 0x75, 0xd3, 0x9f, + 0x4e, 0xbc, 0xe1, 0xa1, 0x35, 0xa1, 0x18, 0x87, 0x4f, 0xc8, 0x79, 0xd8, 0xe8, 0x36, 0xf5, 0xc6, + 0x76, 0x6d, 0xaf, 0xdb, 0xea, 0x5a, 0x76, 0x43, 0xef, 0x54, 0xaa, 0xec, 0xf1, 0xac, 0xd1, 0xb2, + 0x3a, 0xb6, 0x69, 0x54, 0x8c, 0x66, 0x47, 0x55, 0xca, 0xdf, 0x41, 0x8d, 0x53, 0xcf, 0x1f, 0xf6, + 0x77, 0x9d, 0xde, 0xc4, 0x1f, 0x63, 0x85, 0x35, 0xb8, 0x62, 0x19, 0x95, 0x56, 0x73, 0xc7, 0xde, + 0xd5, 0x2b, 0x9d, 0x96, 0x99, 0x15, 0x00, 0xfd, 0x22, 0x5c, 0xc8, 0xc0, 0x69, 0x75, 0xda, 0xaa, + 0x42, 0xae, 0xc2, 0xa5, 0x8c, 0xb2, 0xfb, 0xc6, 0xb6, 0xde, 0xed, 0x54, 0x9b, 0x6a, 0x6e, 0x06, + 0xb1, 0x65, 0xb5, 0xd4, 0x7c, 0xf9, 0xff, 0x2b, 0xb0, 0xd6, 0x0d, 0xb8, 0x21, 0x7e, 0x17, 0x7d, + 0x70, 0x9f, 0x83, 0xcb, 0x5d, 0xcb, 0x30, 0xed, 0x4e, 0xeb, 0x9e, 0xd1, 0xb4, 0xbb, 0x96, 0xbe, + 0x97, 0xac, 0xcd, 0x55, 0xb8, 0x24, 0x61, 0x98, 0x46, 0xa5, 0xb5, 0x6f, 0x98, 0x76, 0x5b, 0xb7, + 0xac, 0xfb, 0x2d, 0x73, 0x47, 0x55, 0xe8, 0x17, 0x33, 0x10, 0x1a, 0xbb, 0x3a, 0xab, 0x4d, 0xac, + 0xac, 0x69, 0xdc, 0xd7, 0xeb, 0xf6, 0x76, 0xab, 0xa3, 0xe6, 0xcb, 0x0d, 0x2a, 0xf5, 0x60, 0x18, + 0x62, 0x66, 0x6f, 0x59, 0x84, 0x42, 0xb3, 0xd5, 0x34, 0x92, 0x4f, 0xae, 0x2b, 0x50, 0xd4, 0xdb, + 0x6d, 0xb3, 0xb5, 0x8f, 0x53, 0x0c, 0x60, 0x71, 0xc7, 0x68, 0xd2, 0x9a, 0xe5, 0x69, 0x49, 0xdb, + 0x6c, 0x35, 0x5a, 0x1d, 0x63, 0x47, 0x2d, 0x94, 0x4d, 0xb1, 0x21, 0x09, 0xa6, 0x3d, 0x9f, 0xbd, + 0x6f, 0xee, 0x18, 0xbb, 0x7a, 0xb7, 0xde, 0xe1, 0x43, 0xf4, 0xc0, 0x36, 0x8d, 0xcf, 0x74, 0x0d, + 0xab, 0x63, 0xa9, 0x0a, 0x51, 0x61, 0xa5, 0x69, 0x18, 0x3b, 0x96, 0x6d, 0x1a, 0xfb, 0x35, 0xe3, + 0xbe, 0x9a, 0xa3, 0x3c, 0xd9, 0xff, 0xf4, 0x0b, 0xe5, 0xef, 0x2b, 0x40, 0x58, 0x08, 0x67, 0x91, + 0x17, 0x08, 0x67, 0xcc, 0x15, 0xb8, 0x58, 0xa5, 0x43, 0x8d, 0x4d, 0x6b, 0xb4, 0x76, 0x92, 0x5d, + 0x76, 0x01, 0x48, 0xa2, 0xbc, 0xb5, 0xbb, 0xab, 0x2a, 0xe4, 0x12, 0x9c, 0x4d, 0xc0, 0x77, 0xcc, + 0x56, 0x5b, 0xcd, 0x5d, 0xcc, 0x15, 0x15, 0xb2, 0x99, 0x2a, 0xbc, 0x67, 0x18, 0x6d, 0x35, 0x4f, + 0x87, 0x28, 0x51, 0x20, 0x96, 0x04, 0x23, 0x2f, 0x94, 0xbf, 0xa5, 0xc0, 0x05, 0x56, 0x4d, 0xb1, + 0xbe, 0xc2, 0xaa, 0x5e, 0x86, 0x2d, 0x1e, 0x98, 0x3e, 0xab, 0xa2, 0xe7, 0x40, 0x8d, 0x95, 0xb2, + 0x6a, 0x9e, 0x87, 0x8d, 0x18, 0x14, 0xeb, 0x91, 0xa3, 0xbb, 0x47, 0x0c, 0xbc, 0x6d, 0x58, 0x1d, + 0xdb, 0xd8, 0xdd, 0x6d, 0x99, 0x1d, 0x56, 0x91, 0x7c, 0x59, 0x83, 0x8d, 0x8a, 0x3b, 0x9e, 0xd0, + 0x0b, 0xe9, 0x30, 0xf0, 0xfc, 0x21, 0x56, 0x61, 0x15, 0x96, 0x8d, 0xcf, 0x76, 0x8c, 0xa6, 0x55, + 0x6b, 0x35, 0xd5, 0x33, 0xe5, 0xcb, 0x09, 0x1c, 0xb1, 0x8e, 0x2d, 0xab, 0xaa, 0x9e, 0x29, 0x3b, + 0xb0, 0x2a, 0xcc, 0xd1, 0xd9, 0xac, 0xb8, 0x02, 0x17, 0xc5, 0x5c, 0xc3, 0x1d, 0x25, 0xd9, 0x84, + 0x2d, 0x38, 0x97, 0x2e, 0x37, 0x3a, 0xaa, 0x42, 0x47, 0x21, 0x51, 0x42, 0xe1, 0xb9, 0xf2, 0xff, + 0x55, 0x60, 0x35, 0x7c, 0x4a, 0x42, 0xe5, 0xf5, 0x55, 0xb8, 0xd4, 0xd8, 0xd5, 0xed, 0x1d, 0x63, + 0xbf, 0x56, 0x31, 0xec, 0x7b, 0xb5, 0xe6, 0x4e, 0xe2, 0x23, 0xcf, 0xc0, 0xf9, 0x0c, 0x04, 0xfc, + 0xca, 0x16, 0x9c, 0x4b, 0x16, 0x75, 0xe8, 0x52, 0xcd, 0xd1, 0xae, 0x4f, 0x96, 0x84, 0xeb, 0x34, + 0x5f, 0xde, 0x87, 0x35, 0x4b, 0x6f, 0xd4, 0x77, 0xfd, 0x71, 0xcf, 0xd5, 0xa7, 0x93, 0xa3, 0x21, + 0xb9, 0x04, 0x9b, 0xbb, 0x2d, 0xb3, 0x62, 0xd8, 0x88, 0x92, 0xa8, 0xc1, 0x59, 0x58, 0x97, 0x0b, + 0x1f, 0x18, 0x74, 0xfa, 0x12, 0x58, 0x93, 0x81, 0xcd, 0x96, 0x9a, 0x2b, 0x7f, 0x1e, 0x56, 0x62, + 0xe9, 0x01, 0x37, 0xe1, 0xac, 0xfc, 0xbb, 0xed, 0x0e, 0xfb, 0xde, 0xf0, 0x50, 0x3d, 0x93, 0x2c, + 0x30, 0xa7, 0xc3, 0x21, 0x2d, 0xc0, 0xf5, 0x2c, 0x17, 0x74, 0xdc, 0xf1, 0xb1, 0x37, 0x74, 0x26, + 0x6e, 0x5f, 0xcd, 0x95, 0x5f, 0x86, 0xd5, 0x58, 0x50, 0x72, 0x3a, 0x70, 0xf5, 0x16, 0xdf, 0x80, + 0x1b, 0xc6, 0x4e, 0xad, 0xdb, 0x50, 0x17, 0xe8, 0x4a, 0xae, 0xd6, 0xf6, 0xaa, 0x2a, 0x94, 0xbf, + 0xab, 0xd0, 0xdb, 0x17, 0xa6, 0x1a, 0x6a, 0xec, 0xea, 0x62, 0xa8, 0xe9, 0x34, 0x63, 0xa9, 0x0e, + 0x0c, 0xcb, 0x62, 0x96, 0x06, 0x97, 0x61, 0x8b, 0xff, 0xb0, 0xf5, 0xe6, 0x8e, 0x5d, 0xd5, 0xcd, + 0x9d, 0xfb, 0xba, 0x49, 0xe7, 0xde, 0x03, 0x35, 0x87, 0x0b, 0x4a, 0x82, 0xd8, 0x9d, 0x56, 0xb7, + 0x52, 0x55, 0xf3, 0x74, 0xfe, 0xc6, 0xe0, 0xed, 0x5a, 0x53, 0x2d, 0xe0, 0xf2, 0x4c, 0x61, 0x23, + 0x5b, 0x5a, 0xbe, 0x50, 0x7e, 0x4f, 0x81, 0x4d, 0xcb, 0x3b, 0x1c, 0x3a, 0x93, 0xe9, 0xd8, 0xd5, + 0x07, 0x87, 0xfe, 0xd8, 0x9b, 0x1c, 0x1d, 0x5b, 0x53, 0x6f, 0xe2, 0x92, 0x5b, 0xf0, 0x82, 0x55, + 0xdb, 0x6b, 0xea, 0x1d, 0xba, 0xbc, 0xf4, 0xfa, 0x5e, 0xcb, 0xac, 0x75, 0xaa, 0x0d, 0xdb, 0xea, + 0xd6, 0x52, 0x33, 0xef, 0x3a, 0x3c, 0x37, 0x1b, 0xb5, 0x6e, 0xec, 0xe9, 0x95, 0x07, 0xaa, 0x32, + 0x9f, 0xe1, 0xb6, 0x5e, 0xd7, 0x9b, 0x15, 0x63, 0xc7, 0xde, 0xbf, 0xad, 0xe6, 0xc8, 0x0b, 0x70, + 0x6d, 0x36, 0xea, 0x6e, 0xad, 0x6d, 0x51, 0xb4, 0xfc, 0xfc, 0xef, 0x56, 0xad, 0x06, 0xc5, 0x2a, + 0x94, 0xff, 0x50, 0x81, 0xad, 0x59, 0x91, 0xa9, 0xc8, 0x0d, 0xd0, 0x8c, 0x66, 0xc7, 0xd4, 0x6b, + 0x3b, 0x76, 0xc5, 0x34, 0x76, 0x8c, 0x66, 0xa7, 0xa6, 0xd7, 0x2d, 0xdb, 0x6a, 0x75, 0xe9, 0x6c, + 0x8a, 0x0c, 0x42, 0x9e, 0x87, 0xab, 0x73, 0xf0, 0x5a, 0xb5, 0x9d, 0x8a, 0xaa, 0x90, 0xdb, 0xf0, + 0xd2, 0x1c, 0x24, 0xeb, 0x81, 0xd5, 0x31, 0x1a, 0x72, 0x89, 0x9a, 0x2b, 0x57, 0xe0, 0xe2, 0xec, + 0xd0, 0x35, 0x74, 0x9b, 0x8e, 0xf7, 0x74, 0x11, 0x0a, 0x3b, 0xf4, 0x64, 0x88, 0x65, 0xc4, 0x28, + 0x7b, 0xa0, 0x26, 0xa3, 0x4f, 0xa4, 0x2c, 0x77, 0xcc, 0x6e, 0xb3, 0xc9, 0x8e, 0x91, 0x75, 0x28, + 0xb5, 0x3a, 0x55, 0xc3, 0xe4, 0x39, 0x45, 0x30, 0x89, 0x48, 0xb7, 0x49, 0x17, 0x4e, 0xcb, 0xac, + 0x7d, 0x0e, 0xcf, 0x93, 0x2d, 0x38, 0x67, 0xd5, 0xf5, 0xca, 0x3d, 0xbb, 0xd9, 0xea, 0xd8, 0xb5, + 0xa6, 0x5d, 0xa9, 0xea, 0xcd, 0xa6, 0x51, 0x57, 0x01, 0x3b, 0x73, 0x96, 0xc7, 0x29, 0xf9, 0x30, + 0xdc, 0x6c, 0xdd, 0xeb, 0xe8, 0x76, 0xbb, 0xde, 0xdd, 0xab, 0x35, 0x6d, 0xeb, 0x41, 0xb3, 0x22, + 0x64, 0x9f, 0x4a, 0x7a, 0xcb, 0xbd, 0x09, 0xd7, 0xe7, 0x62, 0x47, 0xd9, 0x3f, 0x6e, 0x80, 0x36, + 0x17, 0x93, 0x37, 0xa4, 0xfc, 0x13, 0x05, 0x2e, 0xcd, 0x79, 0x59, 0x27, 0x2f, 0xc1, 0xad, 0xaa, + 0xa1, 0xef, 0xd4, 0x0d, 0xcb, 0xc2, 0x8d, 0x82, 0x0e, 0x03, 0xb3, 0xf0, 0xc9, 0xdc, 0x50, 0x6f, + 0xc1, 0x0b, 0xf3, 0xd1, 0xa3, 0xa3, 0xf9, 0x26, 0x5c, 0x9f, 0x8f, 0xca, 0x8f, 0xea, 0x1c, 0x29, + 0xc3, 0x8d, 0xf9, 0x98, 0xe1, 0x11, 0x9f, 0x2f, 0xff, 0xa6, 0x02, 0x17, 0xb2, 0xd5, 0x5b, 0xb4, + 0x6e, 0xb5, 0xa6, 0xd5, 0xd1, 0xeb, 0x75, 0xbb, 0xad, 0x9b, 0x7a, 0xc3, 0x36, 0x9a, 0x66, 0xab, + 0x5e, 0xcf, 0x3a, 0xda, 0xae, 0xc3, 0x73, 0xb3, 0x51, 0xad, 0x8a, 0x59, 0x6b, 0xd3, 0xdd, 0x5b, + 0x83, 0x2b, 0xb3, 0xb1, 0x8c, 0x5a, 0xc5, 0x50, 0x73, 0xdb, 0x6f, 0xfe, 0xe8, 0xef, 0xae, 0x9c, + 0xf9, 0xd1, 0x7b, 0x57, 0x94, 0x9f, 0xbe, 0x77, 0x45, 0xf9, 0xdb, 0xf7, 0xae, 0x28, 0x9f, 0x7b, + 0xf1, 0x64, 0x89, 0xb3, 0xf0, 0x16, 0xf3, 0x68, 0x11, 0xef, 0x6d, 0xaf, 0xfc, 0x67, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x2f, 0x37, 0xd2, 0xf7, 0x44, 0xc0, 0x01, 0x00, } func (this *PluginSpecV1) Equal(that interface{}) bool { @@ -50362,6 +50411,20 @@ func (m *AccessGraphSync) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Azure) > 0 { + for iNdEx := len(m.Azure) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Azure[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } n432, err432 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.PollInterval, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.PollInterval):]) if err432 != nil { return 0, err432 @@ -50442,6 +50505,56 @@ func (m *AccessGraphAWSSync) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *AccessGraphAzureSync) 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 *AccessGraphAzureSync) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AccessGraphAzureSync) 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.Integration) > 0 { + i -= len(m.Integration) + copy(dAtA[i:], m.Integration) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Integration))) + i-- + dAtA[i] = 0x1a + } + if len(m.SubscriptionID) > 0 { + i -= len(m.SubscriptionID) + copy(dAtA[i:], m.SubscriptionID) + i = encodeVarintTypes(dAtA, i, uint64(len(m.SubscriptionID))) + i-- + dAtA[i] = 0x12 + } + if len(m.Regions) > 0 { + for iNdEx := len(m.Regions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Regions[iNdEx]) + copy(dAtA[i:], m.Regions[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Regions[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -61807,6 +61920,12 @@ func (m *AccessGraphSync) Size() (n int) { } l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.PollInterval) n += 1 + l + sovTypes(uint64(l)) + if len(m.Azure) > 0 { + for _, e := range m.Azure { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -61839,6 +61958,32 @@ func (m *AccessGraphAWSSync) Size() (n int) { return n } +func (m *AccessGraphAzureSync) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Regions) > 0 { + for _, s := range m.Regions { + l = len(s) + n += 1 + l + sovTypes(uint64(l)) + } + } + l = len(m.SubscriptionID) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Integration) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -134180,6 +134325,40 @@ func (m *AccessGraphSync) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Azure", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Azure = append(m.Azure, &AccessGraphAzureSync{}) + if err := m.Azure[len(m.Azure)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -134353,6 +134532,153 @@ func (m *AccessGraphAWSSync) Unmarshal(dAtA []byte) error { } return nil } +func (m *AccessGraphAzureSync) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AccessGraphAzureSync: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccessGraphAzureSync: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Regions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Regions = append(m.Regions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubscriptionID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Integration", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Integration = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/gen/proto/go/accessgraph/v1alpha/azure.pb.go b/gen/proto/go/accessgraph/v1alpha/azure.pb.go index 308847e258ba9..c839731eb60bb 100644 --- a/gen/proto/go/accessgraph/v1alpha/azure.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/azure.pb.go @@ -683,7 +683,7 @@ func (x *AzureRoleDefinition) GetType() string { return "" } -// AzurePermission defines the actions and not (disallowed) actions for a role definition +// AzureRBACPermission defines the actions and not (disallowed) actions for a role definition type AzureRBACPermission struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/go.mod b/go.mod index fa63fe6eb8695..4b7a13bd836e9 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( connectrpc.com/connect v1.17.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0 diff --git a/go.sum b/go.sum index 6a73eda617b84..eacf5605716a1 100644 --- a/go.sum +++ b/go.sum @@ -668,6 +668,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLC github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 h1:Hp+EScFOu9HeCbeW8WU2yQPJd4gGwhMgKxWe+G6jNzw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0/go.mod h1:/pz8dyNQe+Ey3yBp/XuYz7oqX8YDNWVpPB0hH3XWfbc= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0 h1:zDeQI/PaWztI2tcrGO/9RIMey9NvqYbnyttf/0P3QWM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0/go.mod h1:zflC9v4VfViJrSvcvplqws/yGXVbUEMZi/iHpZdSPWA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q= diff --git a/integrations/event-handler/go.mod b/integrations/event-handler/go.mod index a1faea1d48034..f716fd71ffb2a 100644 --- a/integrations/event-handler/go.mod +++ b/integrations/event-handler/go.mod @@ -37,6 +37,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0 // indirect diff --git a/integrations/event-handler/go.sum b/integrations/event-handler/go.sum index 2d1840725368c..e8410ac24cbc3 100644 --- a/integrations/event-handler/go.sum +++ b/integrations/event-handler/go.sum @@ -631,6 +631,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvUL github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0/go.mod h1:/pz8dyNQe+Ey3yBp/XuYz7oqX8YDNWVpPB0hH3XWfbc= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0 h1:zDeQI/PaWztI2tcrGO/9RIMey9NvqYbnyttf/0P3QWM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0/go.mod h1:zflC9v4VfViJrSvcvplqws/yGXVbUEMZi/iHpZdSPWA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q= diff --git a/integrations/terraform/go.mod b/integrations/terraform/go.mod index 7240aa79dc715..786539368f8da 100644 --- a/integrations/terraform/go.mod +++ b/integrations/terraform/go.mod @@ -44,6 +44,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0 // indirect diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index 1b7cf7ecfecec..1bc2349420356 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -644,6 +644,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvUL github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0/go.mod h1:/pz8dyNQe+Ey3yBp/XuYz7oqX8YDNWVpPB0hH3XWfbc= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0 h1:zDeQI/PaWztI2tcrGO/9RIMey9NvqYbnyttf/0P3QWM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.1.0/go.mod h1:zflC9v4VfViJrSvcvplqws/yGXVbUEMZi/iHpZdSPWA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q= diff --git a/lib/cloud/azure/roleassignments.go b/lib/cloud/azure/roleassignments.go new file mode 100644 index 0000000000000..114bceef88b96 --- /dev/null +++ b/lib/cloud/azure/roleassignments.go @@ -0,0 +1,57 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2" + "github.com/gravitational/trace" +) + +// RoleAssignmentsClient wraps the Azure API to provide a high level subset of functionality +type RoleAssignmentsClient struct { + cli *armauthorization.RoleAssignmentsClient +} + +// NewRoleAssignmentsClient creates a new client for a given subscription and credentials +func NewRoleAssignmentsClient(subscription string, cred azcore.TokenCredential, options *arm.ClientOptions) (*RoleAssignmentsClient, error) { + clientFactory, err := armauthorization.NewClientFactory(subscription, cred, options) + if err != nil { + return nil, trace.Wrap(err) + } + roleDefCli := clientFactory.NewRoleAssignmentsClient() + return &RoleAssignmentsClient{cli: roleDefCli}, nil +} + +// ListRoleAssignments returns role assignments for a given scope +func (c *RoleAssignmentsClient) ListRoleAssignments(ctx context.Context, scope string) ([]*armauthorization.RoleAssignment, error) { + pager := c.cli.NewListForScopePager(scope, nil) + var roleDefs []*armauthorization.RoleAssignment + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + roleDefs = append(roleDefs, page.Value...) + } + return roleDefs, nil +} diff --git a/lib/cloud/azure/roledefinitions.go b/lib/cloud/azure/roledefinitions.go new file mode 100644 index 0000000000000..cdc46196aa530 --- /dev/null +++ b/lib/cloud/azure/roledefinitions.go @@ -0,0 +1,57 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2" + "github.com/gravitational/trace" +) + +// RoleDefinitionsClient wraps the Azure API to provide a high level subset of functionality +type RoleDefinitionsClient struct { + cli *armauthorization.RoleDefinitionsClient +} + +// NewRoleDefinitionsClient creates a new client for a given subscription and credentials +func NewRoleDefinitionsClient(subscription string, cred azcore.TokenCredential, options *arm.ClientOptions) (*RoleDefinitionsClient, error) { + clientFactory, err := armauthorization.NewClientFactory(subscription, cred, options) + if err != nil { + return nil, trace.Wrap(err) + } + roleDefCli := clientFactory.NewRoleDefinitionsClient() + return &RoleDefinitionsClient{cli: roleDefCli}, nil +} + +// ListRoleDefinitions returns role definitions for a given scope +func (c *RoleDefinitionsClient) ListRoleDefinitions(ctx context.Context, scope string) ([]*armauthorization.RoleDefinition, error) { + pager := c.cli.NewListPager(scope, nil) + var roleDefs []*armauthorization.RoleDefinition + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + roleDefs = append(roleDefs, page.Value...) + } + return roleDefs, nil +} diff --git a/lib/cloud/clients.go b/lib/cloud/clients.go index 93bb27f90b246..66a2fe9a5c71b 100644 --- a/lib/cloud/clients.go +++ b/lib/cloud/clients.go @@ -356,6 +356,10 @@ type azureClients struct { azurePostgresFlexServersClients azure.ClientMap[azure.PostgresFlexServersClient] // azureRunCommandClients contains the cached Azure Run Command clients. azureRunCommandClients azure.ClientMap[azure.RunCommandClient] + // azureRoleDefinitionsClients contains the cached Azure Role Definitions clients. + azureRoleDefinitionsClients azure.ClientMap[azure.RoleDefinitionsClient] + // azureRoleAssignmentsClients contains the cached Azure Role Assignments clients. + azureRoleAssignmentsClients azure.ClientMap[azure.RoleAssignmentsClient] } // credentialsSource defines where the credentials must come from. @@ -756,6 +760,16 @@ func (c *cloudClients) GetAzureRunCommandClient(subscription string) (azure.RunC return c.azureRunCommandClients.Get(subscription, c.GetAzureCredential) } +// GetAzureRoleDefinitionsClient returns an Azure Role Definitions client +func (c *cloudClients) GetAzureRoleDefinitionsClient(subscription string) (azure.RoleDefinitionsClient, error) { + return c.azureRoleDefinitionsClients.Get(subscription, c.GetAzureCredential) +} + +// GetAzureRoleAssignmentsClient returns an Azure Role Assignments client +func (c *cloudClients) GetAzureRoleAssignmentsClient(subscription string) (azure.RoleAssignmentsClient, error) { + return c.azureRoleAssignmentsClients.Get(subscription, c.GetAzureCredential) +} + // Close closes all initialized clients. func (c *cloudClients) Close() (err error) { c.mtx.Lock() @@ -1066,6 +1080,8 @@ type TestCloudClients struct { AzureMySQLFlex azure.MySQLFlexServersClient AzurePostgresFlex azure.PostgresFlexServersClient AzureRunCommand azure.RunCommandClient + AzureRoleDefinitions azure.RoleDefinitionsClient + AzureRoleAssignments azure.RoleAssignmentsClient } // GetAWSSession returns AWS session for the specified region, optionally @@ -1319,11 +1335,21 @@ func (c *TestCloudClients) GetAzurePostgresFlexServersClient(subscription string return c.AzurePostgresFlex, nil } -// GetAzureRunCommand returns an Azure Run Command client for the given subscription. +// GetAzureRunCommandClient returns an Azure Run Command client for the given subscription. func (c *TestCloudClients) GetAzureRunCommandClient(subscription string) (azure.RunCommandClient, error) { return c.AzureRunCommand, nil } +// GetAzureRoleDefinitionsClient returns an Azure Role Definitions client for the given subscription. +func (c *TestCloudClients) GetAzureRoleDefinitionsClient(subscription string) (azure.RoleDefinitionsClient, error) { + return c.AzureRoleDefinitions, nil +} + +// GetAzureRoleAssignmentsClient returns an Azure Role Assignments client for the given subscription. +func (c *TestCloudClients) GetAzureRoleAssignmentsClient(subscription string) (azure.RoleAssignmentsClient, error) { + return c.AzureRoleAssignments, nil +} + // Close closes all initialized clients. func (c *TestCloudClients) Close() error { return nil diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 54dc6a2c82820..33b3b406b46b5 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -1754,6 +1754,19 @@ kubernetes matchers are present`) AssumeRole: assumeRole, }) } + for _, azureMatcher := range fc.Discovery.AccessGraph.Azure { + regions := azureMatcher.Regions + if len(regions) == 0 { + return trace.BadParameter("missing regions in access_graph.azure") + } + subscriptionID := azureMatcher.SubscriptionID + integration := azureMatcher.Integration + tMatcher.Azure = append(tMatcher.Azure, &types.AccessGraphAzureSync{ + Regions: regions, + SubscriptionID: subscriptionID, + Integration: integration, + }) + } if fc.Discovery.AccessGraph.PollInterval > 0 { tMatcher.PollInterval = fc.Discovery.AccessGraph.PollInterval } diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index a4bc53787ce9f..89b668a0221b5 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -1523,6 +1523,8 @@ type GCPMatcher struct { type AccessGraphSync struct { // AWS is the AWS configuration for the AccessGraph Sync service. AWS []AccessGraphAWSSync `yaml:"aws,omitempty"` + // Azure is the Azure configuration for the AccessGraph Sync service. + Azure []AccessGraphAzureSync `yaml:"azure,omitempty"` // PollInterval is the frequency at which to poll for AWS resources PollInterval time.Duration `yaml:"poll_interval,omitempty"` } @@ -1538,6 +1540,12 @@ type AccessGraphAWSSync struct { ExternalID string `yaml:"external_id,omitempty"` } +type AccessGraphAzureSync struct { + Regions []string `yaml:"regions,omitempty"` + SubscriptionID string `yaml:"subscription_id,omitempty"` + Integration string `yaml:"integration,omitempty"` +} + // CommandLabel is `command` section of `ssh_service` in the config file type CommandLabel struct { Name string `yaml:"name"` diff --git a/lib/srv/discovery/access_graph.go b/lib/srv/discovery/access_graph_aws.go similarity index 98% rename from lib/srv/discovery/access_graph.go rename to lib/srv/discovery/access_graph_aws.go index 4bc207b21df01..b05e85d72bc32 100644 --- a/lib/srv/discovery/access_graph.go +++ b/lib/srv/discovery/access_graph_aws.go @@ -145,15 +145,15 @@ func (s *Server) reconcileAccessGraph(ctx context.Context, currentTAGResources * // getAllAWSSyncFetchers returns all AWS sync fetchers. func (s *Server) getAllAWSSyncFetchers() []aws_sync.AWSSync { - allFetchers := make([]aws_sync.AWSSync, 0, len(s.dynamicTAGSyncFetchers)) + allFetchers := make([]aws_sync.AWSSync, 0, len(s.dynamicTAGAWSFetchers)) - s.muDynamicTAGSyncFetchers.RLock() - for _, fetcherSet := range s.dynamicTAGSyncFetchers { + s.muDynamicTAGAWSFetchers.RLock() + for _, fetcherSet := range s.dynamicTAGAWSFetchers { allFetchers = append(allFetchers, fetcherSet...) } - s.muDynamicTAGSyncFetchers.RUnlock() + s.muDynamicTAGAWSFetchers.RUnlock() - allFetchers = append(allFetchers, s.staticTAGSyncFetchers...) + allFetchers = append(allFetchers, s.staticTAGAWSFetchers...) // TODO(tigrato): submit fetchers event return allFetchers } @@ -443,7 +443,7 @@ func (s *Server) initAccessGraphWatchers(ctx context.Context, cfg *Config) error if err != nil { s.Log.ErrorContext(ctx, "Error initializing access graph fetchers", "error", err) } - s.staticTAGSyncFetchers = fetchers + s.staticTAGAWSFetchers = fetchers if cfg.AccessGraphConfig.Enabled { go func() { diff --git a/lib/srv/discovery/access_graph_azure.go b/lib/srv/discovery/access_graph_azure.go new file mode 100644 index 0000000000000..73baadc1e0345 --- /dev/null +++ b/lib/srv/discovery/access_graph_azure.go @@ -0,0 +1,410 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package discovery + +import ( + "context" + "errors" + "io" + "sync" + "time" + + "github.com/gravitational/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/retryutils" + "github.com/gravitational/teleport/entitlements" + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" + "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/services" + azure_sync "github.com/gravitational/teleport/lib/srv/discovery/fetchers/azure-sync" +) + +// reconcileAccessGraphAzure fetches Azure resources, creates a set of resources to delete and upsert based on +// the previous fetch, and then sends the delete and upsert results to the Access Graph stream +func (s *Server) reconcileAccessGraphAzure( + ctx context.Context, + currentTAGResources *azure_sync.Resources, + stream accessgraphv1alpha.AccessGraphService_AzureEventsStreamClient, + features azure_sync.Features, +) error { + type fetcherResult struct { + result *azure_sync.Resources + err error + } + + // Get all the fetchers + allFetchers := s.getAllTAGSyncAzureFetchers() + if len(allFetchers) == 0 { + // If there are no fetchers, we don't need to continue. + // We will send a delete request for all resources and return. + upsert, toDel := azure_sync.ReconcileResults(currentTAGResources, &azure_sync.Resources{}) + + if err := azurePush(stream, upsert, toDel); err != nil { + s.Log.ErrorContext(ctx, "Error pushing empty resources to TAGs", "error", err) + } + return trace.Wrap(errNoAccessGraphFetchers) + } + + // Fetch results concurrently + resultsC := make(chan fetcherResult, len(allFetchers)) + tokens := make(chan struct{}, 3) + accountIds := map[string]struct{}{} + for _, fetcher := range allFetchers { + fetcher := fetcher + accountIds[fetcher.GetSubscriptionID()] = struct{}{} + tokens <- struct{}{} + go func() { + defer func() { + <-tokens + }() + result, err := fetcher.Poll(ctx, features) + resultsC <- fetcherResult{result, trace.Wrap(err)} + }() + } + + // Collect the results from all fetchers. + results := make([]*azure_sync.Resources, 0, len(allFetchers)) + errs := make([]error, 0, len(allFetchers)) + for i := 0; i < len(allFetchers); i++ { + // Each fetcher can return an error and a result. + fetcherResult := <-resultsC + if fetcherResult.err != nil { + errs = append(errs, fetcherResult.err) + } + if fetcherResult.result != nil { + results = append(results, fetcherResult.result) + } + } + + // Aggregate all errors into a single error. + err := trace.NewAggregate(errs...) + if err != nil { + s.Log.ErrorContext(ctx, "Error polling TAGs", "error", err) + } + result := azure_sync.MergeResources(results...) + + // Merge all results into a single result + upsert, toDel := azure_sync.ReconcileResults(currentTAGResources, result) + pushErr := azurePush(stream, upsert, toDel) + + if pushErr != nil { + s.Log.ErrorContext(ctx, "Error pushing TAGs", "error", pushErr) + return nil + } + + // Update the currentTAGResources with the result of the reconciliation. + *currentTAGResources = *result + return nil +} + +// azurePushUpsertInBatches upserts resources to the Access Graph in batches +func azurePushUpsertInBatches( + client accessgraphv1alpha.AccessGraphService_AzureEventsStreamClient, + upsert *accessgraphv1alpha.AzureResourceList, +) error { + for i := 0; i < len(upsert.Resources); i += batchSize { + end := i + batchSize + if end > len(upsert.Resources) { + end = len(upsert.Resources) + } + err := client.Send( + &accessgraphv1alpha.AzureEventsStreamRequest{ + Operation: &accessgraphv1alpha.AzureEventsStreamRequest_Upsert{ + Upsert: &accessgraphv1alpha.AzureResourceList{ + Resources: upsert.Resources[i:end], + }, + }, + }, + ) + if err != nil { + return trace.Wrap(err) + } + } + return nil +} + +// azurePushDeleteInBatches deletes resources from the Access Graph in batches +func azurePushDeleteInBatches( + client accessgraphv1alpha.AccessGraphService_AzureEventsStreamClient, + toDel *accessgraphv1alpha.AzureResourceList, +) error { + for i := 0; i < len(toDel.Resources); i += batchSize { + end := i + batchSize + if end > len(toDel.Resources) { + end = len(toDel.Resources) + } + err := client.Send( + &accessgraphv1alpha.AzureEventsStreamRequest{ + Operation: &accessgraphv1alpha.AzureEventsStreamRequest_Delete{ + Delete: &accessgraphv1alpha.AzureResourceList{ + Resources: toDel.Resources[i:end], + }, + }, + }, + ) + if err != nil { + return trace.Wrap(err) + } + } + return nil +} + +// azurePush upserts and deletes Azure resources to/from the Access Graph +func azurePush( + client accessgraphv1alpha.AccessGraphService_AzureEventsStreamClient, + upsert *accessgraphv1alpha.AzureResourceList, + toDel *accessgraphv1alpha.AzureResourceList, +) error { + err := azurePushUpsertInBatches(client, upsert) + if err != nil { + return trace.Wrap(err) + } + err = azurePushDeleteInBatches(client, toDel) + if err != nil { + return trace.Wrap(err) + } + err = client.Send( + &accessgraphv1alpha.AzureEventsStreamRequest{ + Operation: &accessgraphv1alpha.AzureEventsStreamRequest_Sync{}, + }, + ) + return trace.Wrap(err) +} + +// getAllTAGSyncAzureFetchers returns both static and dynamic TAG Azure fetchers +func (s *Server) getAllTAGSyncAzureFetchers() []*azure_sync.Fetcher { + allFetchers := make([]*azure_sync.Fetcher, 0, len(s.dynamicTAGAWSFetchers)) + + s.muDynamicTAGAzureFetchers.RLock() + for _, fetcherSet := range s.dynamicTAGAzureFetchers { + allFetchers = append(allFetchers, fetcherSet...) + } + s.muDynamicTAGAzureFetchers.RUnlock() + + allFetchers = append(allFetchers, s.staticTAGAzureFetchers...) + return allFetchers +} + +// initializeAndWatchAzureAccessGraph initializes and watches the TAG Azure stream +func (s *Server) initializeAndWatchAzureAccessGraph(ctx context.Context, reloadCh chan struct{}) error { + // Check if the access graph is enabled + clusterFeatures := s.Config.ClusterFeatures() + policy := modules.GetProtoEntitlement(&clusterFeatures, entitlements.Policy) + if !clusterFeatures.AccessGraph && !policy.Enabled { + return trace.Wrap(errTAGFeatureNotEnabled) + } + + // Configure the access graph semaphore for constraining multiple discovery servers + const ( + semaphoreExpiration = time.Minute + semaphoreName = "access_graph_azure_sync" + serviceConfig = `{ + "loadBalancingPolicy": "round_robin", + "healthCheckConfig": { + "serviceName": "" + } + }` + ) + lease, err := services.AcquireSemaphoreLockWithRetry( + ctx, + services.SemaphoreLockConfigWithRetry{ + SemaphoreLockConfig: services.SemaphoreLockConfig{ + Service: s.AccessPoint, + Params: types.AcquireSemaphoreRequest{ + SemaphoreKind: types.KindAccessGraph, + SemaphoreName: semaphoreName, + MaxLeases: 1, + Holder: s.Config.ServerID, + }, + Expiry: semaphoreExpiration, + Clock: s.clock, + }, + Retry: retryutils.LinearConfig{ + Clock: s.clock, + First: time.Second, + Step: semaphoreExpiration / 2, + Max: semaphoreExpiration, + Jitter: retryutils.DefaultJitter, + }, + }, + ) + if err != nil { + return trace.Wrap(err) + } + ctx, cancel := context.WithCancel(lease) + defer cancel() + defer func() { + lease.Stop() + if err := lease.Wait(); err != nil { + s.Log.WarnContext(ctx, "error cleaning up semaphore", "error", err) + } + }() + + // Create the access graph client + accessGraphConn, err := newAccessGraphClient( + ctx, + s.GetClientCert, + s.Config.AccessGraphConfig, + grpc.WithDefaultServiceConfig(serviceConfig), + ) + if err != nil { + return trace.Wrap(err) + } + // Close the connection when the function returns. + defer accessGraphConn.Close() + client := accessgraphv1alpha.NewAccessGraphServiceClient(accessGraphConn) + + // Create the event stream + stream, err := client.AzureEventsStream(ctx) + if err != nil { + s.Log.ErrorContext(ctx, "Failed to get TAG Azure service stream", "error", err) + return trace.Wrap(err) + } + header, err := stream.Header() + if err != nil { + s.Log.ErrorContext(ctx, "Failed to get TAG Azure service stream header", "error", err) + return trace.Wrap(err) + } + const ( + supportedResourcesKey = "supported-kinds" + ) + supportedKinds := header.Get(supportedResourcesKey) + if len(supportedKinds) == 0 { + return trace.BadParameter("TAG Azure service did not return supported kinds") + } + features := azure_sync.BuildFeatures(supportedKinds...) + + // Cancels the context to stop the event watcher if the access graph connection fails + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(1) + go func() { + defer wg.Done() + defer cancel() + if !accessGraphConn.WaitForStateChange(ctx, connectivity.Ready) { + s.Log.InfoContext(ctx, "access graph service connection was closed") + } + }() + + // Configure the poll interval + tickerInterval := defaultPollInterval + if s.Config.Matchers.AccessGraph != nil { + if s.Config.Matchers.AccessGraph.PollInterval > defaultPollInterval { + tickerInterval = s.Config.Matchers.AccessGraph.PollInterval + } else { + s.Log.WarnContext(ctx, + "Access graph Azure service poll interval cannot be less than the default", + "default_poll_interval", + defaultPollInterval) + } + } + s.Log.InfoContext(ctx, "Access graph Azure service poll interval", "poll_interval", tickerInterval) + + // Reconciles the resources as they're imported from Azure + azureResources := &azure_sync.Resources{} + ticker := time.NewTicker(15 * time.Minute) + defer ticker.Stop() + for { + err := s.reconcileAccessGraphAzure(ctx, azureResources, stream, features) + if errors.Is(err, errNoAccessGraphFetchers) { + err := stream.CloseSend() + if errors.Is(err, io.EOF) { + err = nil + } + return trace.Wrap(err) + } + select { + case <-ctx.Done(): + return trace.Wrap(ctx.Err()) + case <-ticker.C: + case <-reloadCh: + } + } +} + +// initTAGAzureWatchers initializes the TAG Azure watchers +func (s *Server) initTAGAzureWatchers(ctx context.Context, cfg *Config) error { + staticFetchers, err := s.accessGraphAzureFetchersFromMatchers(cfg.Matchers, "" /* discoveryConfigName */) + if err != nil { + s.Log.ErrorContext(ctx, "Error initializing access graph fetchers", "error", err) + } + s.staticTAGAzureFetchers = staticFetchers + if !cfg.AccessGraphConfig.Enabled { + return nil + } + go func() { + reloadCh := s.newDiscoveryConfigChangedSub() + for { + fetchers := s.getAllTAGSyncAzureFetchers() + // Wait for the config to change and re-evaluate the fetchers before starting the sync. + if len(fetchers) == 0 { + s.Log.DebugContext(ctx, "No Azure sync fetchers configured. Access graph sync will not be enabled.") + select { + case <-ctx.Done(): + return + case <-reloadCh: + // if the config changes, we need to get the updated list of fetchers + } + continue + } + // Reset the Azure resources to force a full sync + if err := s.initializeAndWatchAzureAccessGraph(ctx, reloadCh); errors.Is(err, errTAGFeatureNotEnabled) { + s.Log.WarnContext(ctx, "Access Graph specified in config, but the license does not include Teleport Policy. Access graph sync will not be enabled.") + break + } else if err != nil { + s.Log.WarnContext(ctx, "Error initializing and watching access graph", "error", err) + } + + select { + case <-ctx.Done(): + return + case <-time.After(time.Minute): + } + } + }() + return nil +} + +// accessGraphAzureFetchersFromMatchers converts matcher configuration to fetchers for Azure resource synchronization +func (s *Server) accessGraphAzureFetchersFromMatchers( + matchers Matchers, discoveryConfigName string) ([]*azure_sync.Fetcher, error) { + var fetchers []*azure_sync.Fetcher + var errs []error + if matchers.AccessGraph == nil { + return fetchers, nil + } + for _, matcher := range matchers.AccessGraph.Azure { + fetcherCfg := azure_sync.Config{ + CloudClients: s.CloudClients, + SubscriptionID: matcher.SubscriptionID, + Integration: matcher.Integration, + DiscoveryConfigName: discoveryConfigName, + } + fetcher, err := azure_sync.NewFetcher(fetcherCfg, s.ctx) + if err != nil { + errs = append(errs, err) + continue + } + fetchers = append(fetchers, fetcher) + } + return fetchers, trace.NewAggregate(errs...) +} diff --git a/lib/srv/discovery/common/reconcile.go b/lib/srv/discovery/common/reconcile.go new file mode 100644 index 0000000000000..9161ae69b95b1 --- /dev/null +++ b/lib/srv/discovery/common/reconcile.go @@ -0,0 +1,32 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package common + +func DeduplicateSlice[T any](s []T, key func(T) string) []T { + out := make([]T, 0, len(s)) + seen := make(map[string]struct{}) + for _, v := range s { + if _, ok := seen[key(v)]; ok { + continue + } + seen[key(v)] = struct{}{} + out = append(out, v) + } + return out +} diff --git a/lib/srv/discovery/discovery.go b/lib/srv/discovery/discovery.go index 141e2cd6d613a..db5fda2cadafd 100644 --- a/lib/srv/discovery/discovery.go +++ b/lib/srv/discovery/discovery.go @@ -62,6 +62,7 @@ import ( "github.com/gravitational/teleport/lib/srv/discovery/common" "github.com/gravitational/teleport/lib/srv/discovery/fetchers" aws_sync "github.com/gravitational/teleport/lib/srv/discovery/fetchers/aws-sync" + azure_sync "github.com/gravitational/teleport/lib/srv/discovery/fetchers/azure-sync" "github.com/gravitational/teleport/lib/srv/discovery/fetchers/db" "github.com/gravitational/teleport/lib/srv/server" logutils "github.com/gravitational/teleport/lib/utils/log" @@ -376,11 +377,17 @@ type Server struct { muDynamicServerGCPFetchers sync.RWMutex staticServerGCPFetchers []server.Fetcher - // dynamicTAGSyncFetchers holds the current TAG Fetchers for the Dynamic Matchers (those coming from DiscoveryConfig resource). + // dynamicTAGAWSFetchers holds the current TAG Fetchers for the Dynamic Matchers (those coming from DiscoveryConfig resource). // The key is the DiscoveryConfig name. - dynamicTAGSyncFetchers map[string][]aws_sync.AWSSync - muDynamicTAGSyncFetchers sync.RWMutex - staticTAGSyncFetchers []aws_sync.AWSSync + dynamicTAGAWSFetchers map[string][]aws_sync.AWSSync + muDynamicTAGAWSFetchers sync.RWMutex + staticTAGAWSFetchers []aws_sync.AWSSync + + // dynamicTAGAzureFetchers holds the current TAG Fetchers for the Dynamic Matchers (those coming from DiscoveryConfig resource). + // The key is the DiscoveryConfig name. + dynamicTAGAzureFetchers map[string][]*azure_sync.Fetcher + muDynamicTAGAzureFetchers sync.RWMutex + staticTAGAzureFetchers []*azure_sync.Fetcher // dynamicKubeFetchers holds the current kube fetchers that use integration as a source of credentials, // for the Dynamic Matchers (those coming from DiscoveryConfig resource). @@ -426,7 +433,8 @@ func New(ctx context.Context, cfg *Config) (*Server, error) { dynamicServerAWSFetchers: make(map[string][]server.Fetcher), dynamicServerAzureFetchers: make(map[string][]server.Fetcher), dynamicServerGCPFetchers: make(map[string][]server.Fetcher), - dynamicTAGSyncFetchers: make(map[string][]aws_sync.AWSSync), + dynamicTAGAWSFetchers: make(map[string][]aws_sync.AWSSync), + dynamicTAGAzureFetchers: make(map[string][]*azure_sync.Fetcher), dynamicDiscoveryConfig: make(map[string]*discoveryconfig.DiscoveryConfig), awsSyncStatus: awsSyncStatus{}, awsEC2ResourcesStatus: newAWSResourceStatusCollector(types.AWSMatcherEC2), @@ -471,6 +479,10 @@ func New(ctx context.Context, cfg *Config) (*Server, error) { return nil, trace.Wrap(err) } + if err := s.initTAGAzureWatchers(s.ctx, cfg); err != nil { + return nil, trace.Wrap(err) + } + return s, nil } @@ -1669,9 +1681,13 @@ func (s *Server) deleteDynamicFetchers(name string) { delete(s.dynamicServerGCPFetchers, name) s.muDynamicServerGCPFetchers.Unlock() - s.muDynamicTAGSyncFetchers.Lock() - delete(s.dynamicTAGSyncFetchers, name) - s.muDynamicTAGSyncFetchers.Unlock() + s.muDynamicTAGAWSFetchers.Lock() + delete(s.dynamicTAGAWSFetchers, name) + s.muDynamicTAGAWSFetchers.Unlock() + + s.muDynamicTAGAzureFetchers.Lock() + delete(s.dynamicTAGAzureFetchers, name) + s.muDynamicTAGAzureFetchers.Unlock() s.muDynamicKubeFetchers.Lock() delete(s.dynamicKubeFetchers, name) @@ -1725,9 +1741,17 @@ func (s *Server) upsertDynamicMatchers(ctx context.Context, dc *discoveryconfig. if err != nil { return trace.Wrap(err) } - s.muDynamicTAGSyncFetchers.Lock() - s.dynamicTAGSyncFetchers[dc.GetName()] = awsSyncMatchers - s.muDynamicTAGSyncFetchers.Unlock() + s.muDynamicTAGAWSFetchers.Lock() + s.dynamicTAGAWSFetchers[dc.GetName()] = awsSyncMatchers + s.muDynamicTAGAWSFetchers.Unlock() + + azureSyncMatchers, err := s.accessGraphAzureFetchersFromMatchers(matchers, dc.GetName()) + if err != nil { + return trace.Wrap(err) + } + s.muDynamicTAGAzureFetchers.Lock() + s.dynamicTAGAzureFetchers[dc.GetName()] = azureSyncMatchers + s.muDynamicTAGAzureFetchers.Unlock() kubeFetchers, err := s.kubeFetchersFromMatchers(matchers, dc.GetName()) if err != nil { diff --git a/lib/srv/discovery/fetchers/aws-sync/merge.go b/lib/srv/discovery/fetchers/aws-sync/merge.go index 6847243e13782..1249cf00120e3 100644 --- a/lib/srv/discovery/fetchers/aws-sync/merge.go +++ b/lib/srv/discovery/fetchers/aws-sync/merge.go @@ -18,6 +18,8 @@ package aws_sync +import "github.com/gravitational/teleport/lib/srv/discovery/common" + // MergeResources merges multiple resources into a single Resources object. // This is used to merge resources from multiple accounts and regions // into a single object. @@ -58,24 +60,24 @@ func MergeResources(results ...*Resources) *Resources { } func deduplicateResources(result *Resources) { - result.Users = deduplicateSlice(result.Users, usersKey) - result.UserInlinePolicies = deduplicateSlice(result.UserInlinePolicies, userInlinePolKey) - result.UserAttachedPolicies = deduplicateSlice(result.UserAttachedPolicies, userAttchPolKey) - result.UserGroups = deduplicateSlice(result.UserGroups, userGroupKey) - result.Groups = deduplicateSlice(result.Groups, groupKey) - result.GroupInlinePolicies = deduplicateSlice(result.GroupInlinePolicies, grpInlinePolKey) - result.GroupAttachedPolicies = deduplicateSlice(result.GroupAttachedPolicies, grpAttchPolKey) - result.Instances = deduplicateSlice(result.Instances, instanceKey) - result.Policies = deduplicateSlice(result.Policies, policyKey) - result.S3Buckets = deduplicateSlice(result.S3Buckets, s3bucketKey) - result.Roles = deduplicateSlice(result.Roles, roleKey) - result.RoleInlinePolicies = deduplicateSlice(result.RoleInlinePolicies, roleInlinePolKey) - result.RoleAttachedPolicies = deduplicateSlice(result.RoleAttachedPolicies, roleAttchPolKey) - result.InstanceProfiles = deduplicateSlice(result.InstanceProfiles, instanceProfKey) - result.AssociatedAccessPolicies = deduplicateSlice(result.AssociatedAccessPolicies, assocAccPolKey) - result.EKSClusters = deduplicateSlice(result.EKSClusters, eksClusterKey) - result.AccessEntries = deduplicateSlice(result.AccessEntries, accessEntryKey) - result.RDSDatabases = deduplicateSlice(result.RDSDatabases, rdsDbKey) - result.SAMLProviders = deduplicateSlice(result.SAMLProviders, samlProvKey) - result.OIDCProviders = deduplicateSlice(result.OIDCProviders, oidcProvKey) + result.Users = common.DeduplicateSlice(result.Users, usersKey) + result.UserInlinePolicies = common.DeduplicateSlice(result.UserInlinePolicies, userInlinePolKey) + result.UserAttachedPolicies = common.DeduplicateSlice(result.UserAttachedPolicies, userAttchPolKey) + result.UserGroups = common.DeduplicateSlice(result.UserGroups, userGroupKey) + result.Groups = common.DeduplicateSlice(result.Groups, groupKey) + result.GroupInlinePolicies = common.DeduplicateSlice(result.GroupInlinePolicies, grpInlinePolKey) + result.GroupAttachedPolicies = common.DeduplicateSlice(result.GroupAttachedPolicies, grpAttchPolKey) + result.Instances = common.DeduplicateSlice(result.Instances, instanceKey) + result.Policies = common.DeduplicateSlice(result.Policies, policyKey) + result.S3Buckets = common.DeduplicateSlice(result.S3Buckets, s3bucketKey) + result.Roles = common.DeduplicateSlice(result.Roles, roleKey) + result.RoleInlinePolicies = common.DeduplicateSlice(result.RoleInlinePolicies, roleInlinePolKey) + result.RoleAttachedPolicies = common.DeduplicateSlice(result.RoleAttachedPolicies, roleAttchPolKey) + result.InstanceProfiles = common.DeduplicateSlice(result.InstanceProfiles, instanceProfKey) + result.AssociatedAccessPolicies = common.DeduplicateSlice(result.AssociatedAccessPolicies, assocAccPolKey) + result.EKSClusters = common.DeduplicateSlice(result.EKSClusters, eksClusterKey) + result.AccessEntries = common.DeduplicateSlice(result.AccessEntries, accessEntryKey) + result.RDSDatabases = common.DeduplicateSlice(result.RDSDatabases, rdsDbKey) + result.SAMLProviders = common.DeduplicateSlice(result.SAMLProviders, samlProvKey) + result.OIDCProviders = common.DeduplicateSlice(result.OIDCProviders, oidcProvKey) } diff --git a/lib/srv/discovery/fetchers/aws-sync/merge_test.go b/lib/srv/discovery/fetchers/aws-sync/merge_test.go index b109782ed1333..614828aae61b6 100644 --- a/lib/srv/discovery/fetchers/aws-sync/merge_test.go +++ b/lib/srv/discovery/fetchers/aws-sync/merge_test.go @@ -25,6 +25,8 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" + + "github.com/gravitational/teleport/lib/srv/discovery/common" ) func TestMergeResources(t *testing.T) { @@ -45,9 +47,9 @@ func TestMergeResources(t *testing.T) { result := MergeResources(&oldResults, &newResults) expected := Resources{ - Users: deduplicateSlice(append(oldUsers, newUsers...), usersKey), - Roles: deduplicateSlice(append(oldRoles, newRoles...), roleKey), - Instances: deduplicateSlice(append(oldEC2, newEC2...), instanceKey), + Users: common.DeduplicateSlice(append(oldUsers, newUsers...), usersKey), + Roles: common.DeduplicateSlice(append(oldRoles, newRoles...), roleKey), + Instances: common.DeduplicateSlice(append(oldEC2, newEC2...), instanceKey), } require.Empty(t, cmp.Diff(&expected, result, protocmp.Transform(), cmpopts.EquateEmpty())) } diff --git a/lib/srv/discovery/fetchers/aws-sync/reconcile.go b/lib/srv/discovery/fetchers/aws-sync/reconcile.go index d9bfc3330ce69..553a1edcd129a 100644 --- a/lib/srv/discovery/fetchers/aws-sync/reconcile.go +++ b/lib/srv/discovery/fetchers/aws-sync/reconcile.go @@ -24,6 +24,7 @@ import ( "google.golang.org/protobuf/proto" accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" + "github.com/gravitational/teleport/lib/srv/discovery/common" ) func newResourceList() *accessgraphv1alpha.AWSResourceList { @@ -71,19 +72,6 @@ type reconcilePair struct { upsert, delete *accessgraphv1alpha.AWSResourceList } -func deduplicateSlice[T any](s []T, key func(T) string) []T { - out := make([]T, 0, len(s)) - seen := make(map[string]struct{}) - for _, v := range s { - if _, ok := seen[key(v)]; ok { - continue - } - seen[key(v)] = struct{}{} - out = append(out, v) - } - return out -} - func reconcile[T proto.Message]( oldItems []T, newItems []T, @@ -91,7 +79,7 @@ func reconcile[T proto.Message]( wrapFn func(T) *accessgraphv1alpha.AWSResource, ) *reconcilePair { // Remove duplicates from the new items - newItems = deduplicateSlice(newItems, keyFn) + newItems = common.DeduplicateSlice(newItems, keyFn) upsertRes := newResourceList() deleteRes := newResourceList() diff --git a/lib/srv/discovery/fetchers/azure-sync/azure-sync.go b/lib/srv/discovery/fetchers/azure-sync/azure-sync.go new file mode 100644 index 0000000000000..0002c8e7a4282 --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/azure-sync.go @@ -0,0 +1,257 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "context" + "sync" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6" + "github.com/gravitational/trace" + "golang.org/x/sync/errgroup" + + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" + "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/cloud/azure" + "github.com/gravitational/teleport/lib/srv/discovery/common" +) + +const ( + featNamePrincipals = "azure/principals" + featNameRoleDefinitions = "azure/roledefinitions" + featNameRoleAssignments = "azure/roleassignments" + featNameVms = "azure/virtualmachines" +) + +// FetcherConcurrency is an arbitrary per-resource type concurrency to ensure significant throughput. As we increase +// the number of resource types, we may increase this value or use some other approach to fetching concurrency. +const FetcherConcurrency = 4 + +// Config defines parameters required for fetching resources from Azure +type Config struct { + CloudClients cloud.Clients + SubscriptionID string + Integration string + DiscoveryConfigName string +} + +// Resources represents the set of resources fetched from Azure +type Resources struct { + Principals []*accessgraphv1alpha.AzurePrincipal + RoleDefinitions []*accessgraphv1alpha.AzureRoleDefinition + RoleAssignments []*accessgraphv1alpha.AzureRoleAssignment + VirtualMachines []*accessgraphv1alpha.AzureVirtualMachine +} + +// RoleDefinitionsClient specifies the methods used to fetch roles from Azure +type RoleDefinitionsClient interface { + ListRoleDefinitions(ctx context.Context, scope string) ([]*armauthorization.RoleDefinition, error) +} + +// RoleAssignmentsClient specifies the methods used to fetch role assignments from Azure +type RoleAssignmentsClient interface { + ListRoleAssignments(ctx context.Context, scope string) ([]*armauthorization.RoleAssignment, error) +} + +// VirtualMachinesClient specifies the methods used to fetch virtual machines from Azure +type VirtualMachinesClient interface { + ListVirtualMachines(ctx context.Context, resourceGroup string) ([]*armcompute.VirtualMachine, error) +} + +// Fetcher provides the functionality for fetching resources from Azure +type Fetcher struct { + Config + lastError error + lastDiscoveredResources uint64 + lastResult *Resources + + roleAssignClient RoleAssignmentsClient + roleDefClient RoleDefinitionsClient + vmClient VirtualMachinesClient +} + +// NewFetcher returns a new fetcher based on configuration parameters +func NewFetcher(cfg Config, ctx context.Context) (*Fetcher, error) { + // Establish the credential from the managed identity + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return nil, trace.Wrap(err) + } + + // Create the clients + roleAssignClient, err := azure.NewRoleAssignmentsClient(cfg.SubscriptionID, cred, nil) + if err != nil { + return nil, trace.Wrap(err) + } + roleDefClient, err := azure.NewRoleDefinitionsClient(cfg.SubscriptionID, cred, nil) + if err != nil { + return nil, trace.Wrap(err) + } + vmClient, err := azure.NewVirtualMachinesClient(cfg.SubscriptionID, cred, nil) + if err != nil { + return nil, trace.Wrap(err) + } + + // Create and return the fetcher + return &Fetcher{ + Config: cfg, + lastResult: &Resources{}, + roleAssignClient: roleAssignClient, + roleDefClient: roleDefClient, + vmClient: vmClient, + }, nil +} + +// Features is a set of booleans that are received from the Access Graph to indicate which resources it can receive +type Features struct { + Principals bool + RoleDefinitions bool + RoleAssignments bool + VirtualMachines bool +} + +// BuildFeatures builds the feature flags based on supported types returned by Access Graph Azure endpoints. +func BuildFeatures(values ...string) Features { + features := Features{} + for _, value := range values { + switch value { + case featNamePrincipals: + features.Principals = true + case featNameRoleAssignments: + features.RoleAssignments = true + case featNameRoleDefinitions: + features.RoleDefinitions = true + case featNameVms: + features.VirtualMachines = true + } + } + return features +} + +// Poll fetches and deduplicates Azure resources specified by the Access Graph +func (a *Fetcher) Poll(ctx context.Context, feats Features) (*Resources, error) { + res, err := a.fetch(ctx, feats) + if res == nil { + return nil, err + } + res.Principals = common.DeduplicateSlice(res.Principals, azurePrincipalsKey) + res.RoleAssignments = common.DeduplicateSlice(res.RoleAssignments, azureRoleAssignKey) + res.RoleDefinitions = common.DeduplicateSlice(res.RoleDefinitions, azureRoleDefKey) + res.VirtualMachines = common.DeduplicateSlice(res.VirtualMachines, azureVmKey) + return res, trace.Wrap(err) +} + +// fetch returns the resources specified by the Access Graph +func (a *Fetcher) fetch(ctx context.Context, feats Features) (*Resources, error) { + // Accumulate Azure resources + eg, ctx := errgroup.WithContext(ctx) + eg.SetLimit(FetcherConcurrency) + var result = &Resources{} + var errs []error + errsCh := make(chan error) + if feats.Principals { + eg.Go(func() error { + principals, err := a.fetchPrincipals(ctx) + if err != nil { + errsCh <- err + return err + } + result.Principals = principals + return nil + }) + } + if feats.RoleAssignments { + eg.Go(func() error { + roleAssigns, err := a.fetchRoleAssignments(ctx) + if err != nil { + errsCh <- err + return err + } + result.RoleAssignments = roleAssigns + return nil + }) + } + if feats.RoleDefinitions { + eg.Go(func() error { + roleDefs, err := a.fetchRoleDefinitions(ctx) + if err != nil { + errsCh <- err + return err + } + result.RoleDefinitions = roleDefs + return nil + }) + } + if feats.VirtualMachines { + eg.Go(func() error { + vms, err := a.fetchVirtualMachines(ctx) + if err != nil { + errsCh <- err + return err + } + result.VirtualMachines = vms + return nil + }) + } + + // Collect the error messages from the error channel + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for { + err, ok := <-errsCh + if !ok { + return + } + errs = append(errs, err) + } + }() + _ = eg.Wait() + close(errsCh) + wg.Wait() + if len(errs) > 0 { + return result, trace.NewAggregate(errs...) + } + + // Return the resources + return result, nil +} + +// Status returns the number of resources last fetched and/or the last fetching/reconciling error +func (a *Fetcher) Status() (uint64, error) { + return a.lastDiscoveredResources, a.lastError +} + +// DiscoveryConfigName returns the name of the configured discovery +func (a *Fetcher) DiscoveryConfigName() string { + return a.Config.DiscoveryConfigName +} + +// IsFromDiscoveryConfig returns whether the discovery is from configuration or dynamic +func (a *Fetcher) IsFromDiscoveryConfig() bool { + return a.Config.DiscoveryConfigName != "" +} + +// GetSubscriptionID returns the ID of the Azure subscription +func (a *Fetcher) GetSubscriptionID() string { + return a.Config.SubscriptionID +} diff --git a/lib/srv/discovery/fetchers/azure-sync/azure-sync_test.go b/lib/srv/discovery/fetchers/azure-sync/azure-sync_test.go new file mode 100644 index 0000000000000..d5c57d06edf1e --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/azure-sync_test.go @@ -0,0 +1,227 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "context" + "fmt" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6" + "github.com/stretchr/testify/require" +) + +type testRoleDefCli struct { + returnErr bool + roleDefs []*armauthorization.RoleDefinition +} + +func (t testRoleDefCli) ListRoleDefinitions(ctx context.Context, scope string) ([]*armauthorization.RoleDefinition, error) { + if t.returnErr { + return nil, fmt.Errorf("error") + } + return t.roleDefs, nil +} + +type testRoleAssignCli struct { + returnErr bool + roleAssigns []*armauthorization.RoleAssignment +} + +func (t testRoleAssignCli) ListRoleAssignments(ctx context.Context, scope string) ([]*armauthorization.RoleAssignment, error) { + if t.returnErr { + return nil, fmt.Errorf("error") + } + return t.roleAssigns, nil +} + +type testVmCli struct { + returnErr bool + vms []*armcompute.VirtualMachine +} + +func (t testVmCli) ListVirtualMachines(ctx context.Context, resourceGroup string) ([]*armcompute.VirtualMachine, error) { + if t.returnErr { + return nil, fmt.Errorf("error") + } + return t.vms, nil +} + +func newRoleDef(id string, name string) *armauthorization.RoleDefinition { + role_name := "test_role_name" + action1 := "Microsoft.Compute/virtualMachines/read" + action2 := "Microsoft.Compute/virtualMachines/*" + action3 := "Microsoft.Compute/*" + return &armauthorization.RoleDefinition{ + ID: &id, + Name: &name, + Properties: &armauthorization.RoleDefinitionProperties{ + Permissions: []*armauthorization.Permission{ + { + Actions: []*string{&action1, &action2}, + }, + { + Actions: []*string{&action3}, + }, + }, + RoleName: &role_name, + }, + } +} + +func newRoleAssign(id string, name string) *armauthorization.RoleAssignment { + scope := "test_scope" + principalId := "test_principal_id" + roleDefId := "test_role_def_id" + return &armauthorization.RoleAssignment{ + ID: &id, + Name: &name, + Properties: &armauthorization.RoleAssignmentProperties{ + PrincipalID: &principalId, + RoleDefinitionID: &roleDefId, + Scope: &scope, + }, + } +} + +func newVm(id string, name string) *armcompute.VirtualMachine { + return &armcompute.VirtualMachine{ + ID: &id, + Name: &name, + } +} + +func TestPoll(t *testing.T) { + roleDefs := []*armauthorization.RoleDefinition{ + newRoleDef("id1", "name1"), + } + roleAssigns := []*armauthorization.RoleAssignment{ + newRoleAssign("id1", "name1"), + } + vms := []*armcompute.VirtualMachine{ + newVm("id1", "name2"), + } + roleDefClient := testRoleDefCli{} + roleAssignClient := testRoleAssignCli{} + vmClient := testVmCli{} + fetcher := Fetcher{ + Config: Config{SubscriptionID: "1234567890"}, + lastResult: &Resources{}, + roleDefClient: &roleDefClient, + roleAssignClient: &roleAssignClient, + vmClient: &vmClient, + } + ctx := context.Background() + allFeats := Features{ + RoleDefinitions: true, + RoleAssignments: true, + VirtualMachines: true, + } + noVmsFeats := allFeats + noVmsFeats.VirtualMachines = false + + tests := []struct { + returnErr bool + roleDefs []*armauthorization.RoleDefinition + roleAssigns []*armauthorization.RoleAssignment + vms []*armcompute.VirtualMachine + feats Features + }{ + // Process no results from clients + { + returnErr: false, + roleDefs: []*armauthorization.RoleDefinition{}, + roleAssigns: []*armauthorization.RoleAssignment{}, + vms: []*armcompute.VirtualMachine{}, + feats: allFeats, + }, + // Process test results from clients + { + returnErr: false, + roleDefs: roleDefs, + roleAssigns: roleAssigns, + vms: vms, + feats: allFeats, + }, + // Handle errors from clients + { + returnErr: true, + roleDefs: roleDefs, + roleAssigns: roleAssigns, + vms: vms, + feats: allFeats, + }, + // Handle VM features being disabled + { + returnErr: false, + roleDefs: roleDefs, + roleAssigns: roleAssigns, + vms: vms, + feats: noVmsFeats, + }, + } + + for _, tt := range tests { + // Set the test data + roleDefClient.returnErr = tt.returnErr + roleDefClient.roleDefs = tt.roleDefs + roleAssignClient.returnErr = tt.returnErr + roleAssignClient.roleAssigns = tt.roleAssigns + vmClient.returnErr = tt.returnErr + vmClient.vms = tt.vms + + // Poll for resources + resources, err := fetcher.Poll(ctx, tt.feats) + + // Require no error unless otherwise specified + if tt.returnErr { + require.Error(t, err) + continue + } + require.NoError(t, err) + + // Verify the results, based on the features set + require.NotNil(t, resources) + require.Equal(t, tt.feats.RoleDefinitions == false || len(tt.roleDefs) == 0, len(resources.RoleDefinitions) == 0) + for idx, resource := range resources.RoleDefinitions { + roleDef := tt.roleDefs[idx] + require.Equal(t, *roleDef.ID, resource.Id) + require.Equal(t, fetcher.SubscriptionID, resource.SubscriptionId) + require.Equal(t, *roleDef.Properties.RoleName, resource.Name) + require.Len(t, roleDef.Properties.Permissions, len(resource.Permissions)) + } + require.Equal(t, tt.feats.RoleAssignments == false || len(tt.roleAssigns) == 0, len(resources.RoleAssignments) == 0) + for idx, resource := range resources.RoleAssignments { + roleAssign := tt.roleAssigns[idx] + require.Equal(t, *roleAssign.ID, resource.Id) + require.Equal(t, fetcher.SubscriptionID, resource.SubscriptionId) + require.Equal(t, *roleAssign.Properties.PrincipalID, resource.PrincipalId) + require.Equal(t, *roleAssign.Properties.RoleDefinitionID, resource.RoleDefinitionId) + require.Equal(t, *roleAssign.Properties.Scope, resource.Scope) + } + require.Equal(t, tt.feats.VirtualMachines == false || len(tt.vms) == 0, len(resources.VirtualMachines) == 0) + for idx, resource := range resources.VirtualMachines { + vm := tt.vms[idx] + require.Equal(t, *vm.ID, resource.Id) + require.Equal(t, fetcher.SubscriptionID, resource.SubscriptionId) + require.Equal(t, *vm.Name, resource.Name) + } + } +} diff --git a/lib/srv/discovery/fetchers/azure-sync/msggraphclient.go b/lib/srv/discovery/fetchers/azure-sync/msggraphclient.go new file mode 100644 index 0000000000000..75d2960d7fa55 --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/msggraphclient.go @@ -0,0 +1,240 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" +) + +// GraphClient represents generic MS API client +type GraphClient struct { + token azcore.AccessToken +} + +const ( + usersSuffix = "users" + groupsSuffix = "groups" + servicePrincipalsSuffix = "servicePrincipals" + graphBaseURL = "https://graph.microsoft.com/v1.0" + httpTimeout = time.Second * 30 +) + +// graphError represents MS Graph error +type graphError struct { + E struct { + Code string `json:"code"` + Message string `json:"message"` + } `json:"error"` +} + +// genericGraphResponse represents the utility struct for parsing MS Graph API response +type genericGraphResponse struct { + Context string `json:"@odata.context"` + Count int `json:"@odata.count"` + NextLink string `json:"@odata.nextLink"` + Value json.RawMessage `json:"value"` +} + +// User represents user resource +type User struct { + ID string `json:"id"` + Name string `json:"displayName"` + MemberOf []Membership `json:"memberOf"` +} + +type Membership struct { + Type string `json:"@odata.type"` + ID string `json:"id"` +} + +// request represents generic request structure +type request struct { + // Method HTTP method + Method string + // URL which overrides URL construction + URL *string + // Path to a resource + Path string + // Expand $expand value + Expand []string + // Filter $filter value + Filter string + // Body request body + Body string + // Response represents template structure for a response + Response interface{} + // Err represents template structure for an error + Err error + // SuccessCode http code representing success + SuccessCode int +} + +// GetURL builds the request URL +func (r *request) GetURL() (string, error) { + if r.URL != nil { + return *r.URL, nil + } + u, err := url.Parse(graphBaseURL) + if err != nil { + return "", err + } + + data := url.Values{} + if len(r.Expand) > 0 { + data.Set("$expand", strings.Join(r.Expand, ",")) + } + if r.Filter != "" { + data.Set("$filter", r.Filter) + } + + u.Path = u.Path + "/" + r.Path + u.RawQuery = data.Encode() + + return u.String(), nil +} + +// NewGraphClient creates MS Graph API client +func NewGraphClient(token azcore.AccessToken) *GraphClient { + return &GraphClient{ + token: token, + } +} + +// Error returns error string +func (e graphError) Error() string { + return e.E.Code + " " + e.E.Message +} + +func (c *GraphClient) ListUsers(ctx context.Context) ([]User, error) { + return c.listIdentities(ctx, usersSuffix, []string{"memberOf"}) +} + +func (c *GraphClient) ListGroups(ctx context.Context) ([]User, error) { + return c.listIdentities(ctx, groupsSuffix, []string{"memberOf"}) +} + +func (c *GraphClient) ListServicePrincipals(ctx context.Context) ([]User, error) { + return c.listIdentities(ctx, servicePrincipalsSuffix, []string{"memberOf"}) +} + +func (c *GraphClient) listIdentities(ctx context.Context, idType string, expand []string) ([]User, error) { + var users []User + var nextLink *string + for { + g := &genericGraphResponse{} + req := request{ + Method: http.MethodGet, + Path: idType, + Expand: expand, + Response: &g, + Err: &graphError{}, + URL: nextLink, + } + err := c.request(ctx, req) + if err != nil { + return nil, err + } + var newUsers []User + err = json.NewDecoder(bytes.NewReader(g.Value)).Decode(&newUsers) + if err != nil { + return nil, err + } + users = append(users, newUsers...) + if g.NextLink == "" { + break + } + nextLink = &g.NextLink + } + + return users, nil +} + +// request sends the request to the graph/bot service and returns response body as bytes slice +func (c *GraphClient) request(ctx context.Context, req request) error { + reqUrl, err := req.GetURL() + if err != nil { + return err + } + + r, err := http.NewRequestWithContext(ctx, req.Method, reqUrl, strings.NewReader(req.Body)) + if err != nil { + return err + } + + r.Header.Set("Authorization", "Bearer "+c.token.Token) + r.Header.Set("Content-Type", "application/json") + + client := http.Client{Timeout: httpTimeout} + resp, err := client.Do(r) + if err != nil { + return err + } + + defer func(r *http.Response) { + _ = r.Body.Close() + }(resp) + + b, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + expectedCode := req.SuccessCode + if expectedCode == 0 { + expectedCode = http.StatusOK + } + + if expectedCode == resp.StatusCode { + if req.Response == nil { + return nil + } + + err := json.NewDecoder(bytes.NewReader(b)).Decode(req.Response) + if err != nil { + return err + } + } else { + if req.Err == nil { + return fmt.Errorf("Error requesting MS Graph API: %v", string(b)) + } + + err := json.NewDecoder(bytes.NewReader(b)).Decode(req.Err) + if err != nil { + return err + } + + if req.Err.Error() == "" { + return fmt.Errorf("Error requesting MS Graph API. Expected response code was %v, but is %v", expectedCode, resp.StatusCode) + } + + return req.Err + } + + return nil +} diff --git a/lib/srv/discovery/fetchers/azure-sync/principals.go b/lib/srv/discovery/fetchers/azure-sync/principals.go new file mode 100644 index 0000000000000..1584989d81cd4 --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/principals.go @@ -0,0 +1,84 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "context" + "slices" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/timestamppb" + + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" +) + +const groupType = "#microsoft.graph.group" +const defaultGraphScope = "https://graph.microsoft.com/.default" + +func (a *Fetcher) fetchPrincipals(ctx context.Context) ([]*accessgraphv1alpha.AzurePrincipal, error) { + // Get the VM client + cred, err := a.CloudClients.GetAzureCredential() + if err != nil { + return nil, trace.Wrap(err) + } + scopes := []string{defaultGraphScope} + token, err := cred.GetToken(ctx, policy.TokenRequestOptions{Scopes: scopes}) + if err != nil { + return nil, trace.Wrap(err) + } + cli := NewGraphClient(token) + + // Fetch the users, groups, and managed identities + users, err := cli.ListUsers(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + groups, err := cli.ListGroups(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + svcPrincipals, err := cli.ListServicePrincipals(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + principals := slices.Concat(users, groups, svcPrincipals) + + // Return the users as protobuf messages + pbPrincipals := make([]*accessgraphv1alpha.AzurePrincipal, 0, len(principals)) + for _, principal := range principals { + // Extract group membership + memberOf := make([]string, 0) + for _, member := range principal.MemberOf { + if member.Type == groupType { + memberOf = append(memberOf, member.ID) + } + } + // Create the protobuf principal and append it to the list + pbPrincipal := &accessgraphv1alpha.AzurePrincipal{ + Id: principal.ID, + SubscriptionId: a.GetSubscriptionID(), + LastSyncTime: timestamppb.Now(), + DisplayName: principal.Name, + MemberOf: memberOf, + } + pbPrincipals = append(pbPrincipals, pbPrincipal) + } + return pbPrincipals, nil +} diff --git a/lib/srv/discovery/fetchers/azure-sync/reconcile.go b/lib/srv/discovery/fetchers/azure-sync/reconcile.go new file mode 100644 index 0000000000000..2b54c8cfac911 --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/reconcile.go @@ -0,0 +1,165 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "fmt" + + "google.golang.org/protobuf/proto" + + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" + "github.com/gravitational/teleport/lib/srv/discovery/common" +) + +// MergeResources merges Azure resources fetched from multiple configured Azure fetchers +func MergeResources(results ...*Resources) *Resources { + if len(results) == 0 { + return &Resources{} + } + if len(results) == 1 { + return results[0] + } + result := &Resources{} + for _, r := range results { + result.Principals = append(result.Principals, r.Principals...) + result.RoleAssignments = append(result.RoleAssignments, r.RoleAssignments...) + result.RoleDefinitions = append(result.RoleDefinitions, r.RoleDefinitions...) + result.VirtualMachines = append(result.VirtualMachines, r.VirtualMachines...) + } + result.Principals = common.DeduplicateSlice(result.Principals, azurePrincipalsKey) + result.RoleAssignments = common.DeduplicateSlice(result.RoleAssignments, azureRoleAssignKey) + result.RoleDefinitions = common.DeduplicateSlice(result.RoleDefinitions, azureRoleDefKey) + result.VirtualMachines = common.DeduplicateSlice(result.VirtualMachines, azureVmKey) + return result +} + +// newResourceList creates a new resource list message +func newResourceList() *accessgraphv1alpha.AzureResourceList { + return &accessgraphv1alpha.AzureResourceList{ + Resources: make([]*accessgraphv1alpha.AzureResource, 0), + } +} + +// ReconcileResults compares previously and currently fetched results and determines which resources to upsert and +// which to delete. +func ReconcileResults(old *Resources, new *Resources) (upsert, delete *accessgraphv1alpha.AzureResourceList) { + upsert, delete = newResourceList(), newResourceList() + reconciledResources := []*reconcilePair{ + reconcile(old.Principals, new.Principals, azurePrincipalsKey, azurePrincipalsWrap), + reconcile(old.RoleAssignments, new.RoleAssignments, azureRoleAssignKey, azureRoleAssignWrap), + reconcile(old.RoleDefinitions, new.RoleDefinitions, azureRoleDefKey, azureRoleDefWrap), + reconcile(old.VirtualMachines, new.VirtualMachines, azureVmKey, azureVmWrap), + } + for _, res := range reconciledResources { + upsert.Resources = append(upsert.Resources, res.upsert.Resources...) + delete.Resources = append(delete.Resources, res.delete.Resources...) + } + return upsert, delete +} + +// reconcilePair contains the Azure resources to upsert and delete +type reconcilePair struct { + upsert, delete *accessgraphv1alpha.AzureResourceList +} + +// reconcile compares old and new items to build a list of resources to upsert and delete in the Access Graph +func reconcile[T proto.Message]( + oldItems []T, + newItems []T, + keyFn func(T) string, + wrapFn func(T) *accessgraphv1alpha.AzureResource, +) *reconcilePair { + // Remove duplicates from the new items + newItems = common.DeduplicateSlice(newItems, keyFn) + upsertRes := newResourceList() + deleteRes := newResourceList() + + // Delete all old items if there are no new items + if len(newItems) == 0 { + for _, item := range oldItems { + deleteRes.Resources = append(deleteRes.Resources, wrapFn(item)) + } + return &reconcilePair{upsertRes, deleteRes} + } + + // Create all new items if there are no old items + if len(oldItems) == 0 { + for _, item := range newItems { + upsertRes.Resources = append(upsertRes.Resources, wrapFn(item)) + } + return &reconcilePair{upsertRes, deleteRes} + } + + // Map old and new items by their key + oldMap := make(map[string]T, len(oldItems)) + for _, item := range oldItems { + oldMap[keyFn(item)] = item + } + newMap := make(map[string]T, len(newItems)) + for _, item := range newItems { + newMap[keyFn(item)] = item + } + + // Append new or modified items to the upsert list + for _, item := range newItems { + if oldItem, ok := oldMap[keyFn(item)]; !ok || !proto.Equal(oldItem, item) { + upsertRes.Resources = append(upsertRes.Resources, wrapFn(item)) + } + } + + // Append removed items to the delete list + for _, item := range oldItems { + if _, ok := newMap[keyFn(item)]; !ok { + deleteRes.Resources = append(deleteRes.Resources, wrapFn(item)) + } + } + return &reconcilePair{upsertRes, deleteRes} +} + +func azurePrincipalsKey(user *accessgraphv1alpha.AzurePrincipal) string { + return fmt.Sprintf("%s:%s", user.SubscriptionId, user.Id) +} + +func azurePrincipalsWrap(principal *accessgraphv1alpha.AzurePrincipal) *accessgraphv1alpha.AzureResource { + return &accessgraphv1alpha.AzureResource{Resource: &accessgraphv1alpha.AzureResource_Principal{Principal: principal}} +} + +func azureRoleAssignKey(roleAssign *accessgraphv1alpha.AzureRoleAssignment) string { + return fmt.Sprintf("%s:%s", roleAssign.SubscriptionId, roleAssign.Id) +} + +func azureRoleAssignWrap(roleAssign *accessgraphv1alpha.AzureRoleAssignment) *accessgraphv1alpha.AzureResource { + return &accessgraphv1alpha.AzureResource{Resource: &accessgraphv1alpha.AzureResource_RoleAssignment{RoleAssignment: roleAssign}} +} + +func azureRoleDefKey(roleDef *accessgraphv1alpha.AzureRoleDefinition) string { + return fmt.Sprintf("%s:%s", roleDef.SubscriptionId, roleDef.Id) +} + +func azureRoleDefWrap(roleDef *accessgraphv1alpha.AzureRoleDefinition) *accessgraphv1alpha.AzureResource { + return &accessgraphv1alpha.AzureResource{Resource: &accessgraphv1alpha.AzureResource_RoleDefinition{RoleDefinition: roleDef}} +} + +func azureVmKey(vm *accessgraphv1alpha.AzureVirtualMachine) string { + return fmt.Sprintf("%s:%s", vm.SubscriptionId, vm.Id) +} + +func azureVmWrap(vm *accessgraphv1alpha.AzureVirtualMachine) *accessgraphv1alpha.AzureResource { + return &accessgraphv1alpha.AzureResource{Resource: &accessgraphv1alpha.AzureResource_VirtualMachine{VirtualMachine: vm}} +} diff --git a/lib/srv/discovery/fetchers/azure-sync/reconcile_test.go b/lib/srv/discovery/fetchers/azure-sync/reconcile_test.go new file mode 100644 index 0000000000000..28b293bcf1f8d --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/reconcile_test.go @@ -0,0 +1,191 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package azure_sync + +import ( + "testing" + + "github.com/stretchr/testify/require" + + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" +) + +func TestReconcileResults(t *testing.T) { + principals := generatePrincipals() + roleDefs := generateRoleDefs() + roleAssigns := generateRoleAssigns() + vms := generateVms() + + tests := []struct { + oldResults *Resources + newResults *Resources + expectedUpserts *accessgraphv1alpha.AzureResourceList + expectedDeletes *accessgraphv1alpha.AzureResourceList + }{ + // Overlapping old and new results + { + oldResults: &Resources{ + Principals: principals[0:2], + RoleDefinitions: roleDefs[0:2], + RoleAssignments: roleAssigns[0:2], + VirtualMachines: vms[0:2], + }, + newResults: &Resources{ + Principals: principals[1:3], + RoleDefinitions: roleDefs[1:3], + RoleAssignments: roleAssigns[1:3], + VirtualMachines: vms[1:3], + }, + expectedUpserts: generateExpected(principals[2:3], roleDefs[2:3], roleAssigns[2:3], vms[2:3]), + expectedDeletes: generateExpected(principals[0:1], roleDefs[0:1], roleAssigns[0:1], vms[0:1]), + }, + // Completely new results + { + oldResults: &Resources{ + Principals: nil, + RoleDefinitions: nil, + RoleAssignments: nil, + VirtualMachines: nil, + }, + newResults: &Resources{ + Principals: principals[1:3], + RoleDefinitions: roleDefs[1:3], + RoleAssignments: roleAssigns[1:3], + VirtualMachines: vms[1:3], + }, + expectedUpserts: generateExpected(principals[1:3], roleDefs[1:3], roleAssigns[1:3], vms[1:3]), + expectedDeletes: generateExpected(nil, nil, nil, nil), + }, + // No new results + { + oldResults: &Resources{ + Principals: principals[1:3], + RoleDefinitions: roleDefs[1:3], + RoleAssignments: roleAssigns[1:3], + VirtualMachines: vms[1:3], + }, + newResults: &Resources{ + Principals: nil, + RoleDefinitions: nil, + RoleAssignments: nil, + VirtualMachines: nil, + }, + expectedUpserts: generateExpected(nil, nil, nil, nil), + expectedDeletes: generateExpected(principals[1:3], roleDefs[1:3], roleAssigns[1:3], vms[1:3]), + }, + } + + for _, tt := range tests { + upserts, deletes := ReconcileResults(tt.oldResults, tt.newResults) + require.ElementsMatch(t, upserts.Resources, tt.expectedUpserts.Resources) + require.ElementsMatch(t, deletes.Resources, tt.expectedDeletes.Resources) + } + +} + +func generateExpected( + principals []*accessgraphv1alpha.AzurePrincipal, + roleDefs []*accessgraphv1alpha.AzureRoleDefinition, + roleAssigns []*accessgraphv1alpha.AzureRoleAssignment, + vms []*accessgraphv1alpha.AzureVirtualMachine, +) *accessgraphv1alpha.AzureResourceList { + resList := &accessgraphv1alpha.AzureResourceList{ + Resources: make([]*accessgraphv1alpha.AzureResource, 0), + } + for _, principal := range principals { + resList.Resources = append(resList.Resources, azurePrincipalsWrap(principal)) + } + for _, roleDef := range roleDefs { + resList.Resources = append(resList.Resources, azureRoleDefWrap(roleDef)) + } + for _, roleAssign := range roleAssigns { + resList.Resources = append(resList.Resources, azureRoleAssignWrap(roleAssign)) + } + for _, vm := range vms { + resList.Resources = append(resList.Resources, azureVmWrap(vm)) + } + return resList +} + +func generatePrincipals() []*accessgraphv1alpha.AzurePrincipal { + return []*accessgraphv1alpha.AzurePrincipal{ + { + Id: "/principals/foo", + DisplayName: "userFoo", + }, + { + Id: "/principals/bar", + DisplayName: "userBar", + }, + { + Id: "/principals/charles", + DisplayName: "userCharles", + }, + } +} + +func generateRoleDefs() []*accessgraphv1alpha.AzureRoleDefinition { + return []*accessgraphv1alpha.AzureRoleDefinition{ + { + Id: "/roledefinitions/foo", + Name: "roleFoo", + }, + { + Id: "/roledefinitions/bar", + Name: "roleBar", + }, + { + Id: "/roledefinitions/charles", + Name: "roleCharles", + }, + } +} + +func generateRoleAssigns() []*accessgraphv1alpha.AzureRoleAssignment { + return []*accessgraphv1alpha.AzureRoleAssignment{ + { + Id: "/roleassignments/foo", + PrincipalId: "userFoo", + }, + { + Id: "/roleassignments/bar", + PrincipalId: "userBar", + }, + { + Id: "/roleassignments/charles", + PrincipalId: "userCharles", + }, + } +} + +func generateVms() []*accessgraphv1alpha.AzureVirtualMachine { + return []*accessgraphv1alpha.AzureVirtualMachine{ + { + Id: "/vms/foo", + Name: "userFoo", + }, + { + Id: "/vms/bar", + Name: "userBar", + }, + { + Id: "/vms/charles", + Name: "userCharles", + }, + } +} diff --git a/lib/srv/discovery/fetchers/azure-sync/roleassignments.go b/lib/srv/discovery/fetchers/azure-sync/roleassignments.go new file mode 100644 index 0000000000000..f45275368debe --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/roleassignments.go @@ -0,0 +1,52 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "context" + "fmt" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/timestamppb" + + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" +) + +func (a *Fetcher) fetchRoleAssignments(ctx context.Context) ([]*accessgraphv1alpha.AzureRoleAssignment, error) { + // List the role definitions + roleAssigns, err := a.roleAssignClient.ListRoleAssignments(ctx, fmt.Sprintf("/subscriptions/%s", a.SubscriptionID)) + if err != nil { + return nil, trace.Wrap(err) + } + + // Convert to protobuf format + pbRoleAssigns := make([]*accessgraphv1alpha.AzureRoleAssignment, 0, len(roleAssigns)) + for _, roleAssign := range roleAssigns { + pbRoleAssign := &accessgraphv1alpha.AzureRoleAssignment{ + Id: *roleAssign.ID, + SubscriptionId: a.SubscriptionID, + LastSyncTime: timestamppb.Now(), + PrincipalId: *roleAssign.Properties.PrincipalID, + RoleDefinitionId: *roleAssign.Properties.RoleDefinitionID, + Scope: *roleAssign.Properties.Scope, + } + pbRoleAssigns = append(pbRoleAssigns, pbRoleAssign) + } + return pbRoleAssigns, nil +} diff --git a/lib/srv/discovery/fetchers/azure-sync/roledefinitions.go b/lib/srv/discovery/fetchers/azure-sync/roledefinitions.go new file mode 100644 index 0000000000000..050cb0cadc816 --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/roledefinitions.go @@ -0,0 +1,67 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "context" + "fmt" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/timestamppb" + + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" +) + +func (a *Fetcher) fetchRoleDefinitions(ctx context.Context) ([]*accessgraphv1alpha.AzureRoleDefinition, error) { + // List the role definitions + roleDefs, err := a.roleDefClient.ListRoleDefinitions(ctx, fmt.Sprintf("/subscriptions/%s", a.SubscriptionID)) + if err != nil { + return nil, trace.Wrap(err) + } + + // Convert to protobuf format + pbRoleDefs := make([]*accessgraphv1alpha.AzureRoleDefinition, 0, len(roleDefs)) + for _, roleDef := range roleDefs { + pbPerms := make([]*accessgraphv1alpha.AzureRBACPermission, 0, len(roleDef.Properties.Permissions)) + for _, perm := range roleDef.Properties.Permissions { + pbPerm := accessgraphv1alpha.AzureRBACPermission{ + Actions: ptrsToList(perm.Actions), + NotActions: ptrsToList(perm.NotActions), + } + pbPerms = append(pbPerms, &pbPerm) + } + pbRoleDef := &accessgraphv1alpha.AzureRoleDefinition{ + Id: *roleDef.ID, + Name: *roleDef.Properties.RoleName, + SubscriptionId: a.SubscriptionID, + LastSyncTime: timestamppb.Now(), + Permissions: pbPerms, + } + pbRoleDefs = append(pbRoleDefs, pbRoleDef) + } + return pbRoleDefs, nil +} + +func ptrsToList(ptrs []*string) []string { + strList := make([]string, 0, len(ptrs)) + for _, ptr := range ptrs { + strList = append(strList, *ptr) + } + return strList +} diff --git a/lib/srv/discovery/fetchers/azure-sync/virtualmachines.go b/lib/srv/discovery/fetchers/azure-sync/virtualmachines.go new file mode 100644 index 0000000000000..1dbb254fc976c --- /dev/null +++ b/lib/srv/discovery/fetchers/azure-sync/virtualmachines.go @@ -0,0 +1,50 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package azure_sync + +import ( + "context" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/timestamppb" + + accessgraphv1alpha "github.com/gravitational/teleport/gen/proto/go/accessgraph/v1alpha" +) + +const allResourceGroups = "*" + +func (a *Fetcher) fetchVirtualMachines(ctx context.Context) ([]*accessgraphv1alpha.AzureVirtualMachine, error) { + vms, err := a.vmClient.ListVirtualMachines(ctx, allResourceGroups) + if err != nil { + return nil, trace.Wrap(err) + } + + // Return the VMs as protobuf messages + pbVms := make([]*accessgraphv1alpha.AzureVirtualMachine, 0, len(vms)) + for _, vm := range vms { + pbVm := accessgraphv1alpha.AzureVirtualMachine{ + Id: *vm.ID, + SubscriptionId: a.GetSubscriptionID(), + LastSyncTime: timestamppb.Now(), + Name: *vm.Name, + } + pbVms = append(pbVms, &pbVm) + } + return pbVms, nil +} diff --git a/proto/accessgraph/v1alpha/azure.proto b/proto/accessgraph/v1alpha/azure.proto index 1050c3c98f75e..58bef9b36e97b 100644 --- a/proto/accessgraph/v1alpha/azure.proto +++ b/proto/accessgraph/v1alpha/azure.proto @@ -127,7 +127,7 @@ message AzureRoleDefinition { string type = 9; } -// AzurePermission defines the actions and not (disallowed) actions for a role definition +// AzureRBACPermission defines the actions and not (disallowed) actions for a role definition message AzureRBACPermission { // actions define the resources and verbs allowed on the resources repeated string actions = 1;