mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
GitHub Proxy part 3.5: caching PluginStaticCredentials (#49472)
* GitHub Proxy part 3.5: caching PluginStaticCredentials * fix lint
This commit is contained in:
@@ -343,6 +343,10 @@ func EventToGRPC(in types.Event) (*proto.Event, error) {
|
||||
out.Resource = &proto.Event_AccessListReview{
|
||||
AccessListReview: accesslistv1conv.ToReviewProto(r),
|
||||
}
|
||||
case *types.PluginStaticCredentialsV1:
|
||||
out.Resource = &proto.Event_PluginStaticCredentials{
|
||||
PluginStaticCredentials: r,
|
||||
}
|
||||
default:
|
||||
return nil, trace.BadParameter("resource type %T is not supported", in.Resource)
|
||||
}
|
||||
@@ -609,6 +613,9 @@ func EventFromGRPC(in *proto.Event) (*types.Event, error) {
|
||||
} else if r := in.GetIdentityCenterAccountAssignment(); r != nil {
|
||||
out.Resource = types.Resource153ToLegacy(r)
|
||||
return &out, nil
|
||||
} else if r := in.GetPluginStaticCredentials(); r != nil {
|
||||
out.Resource = r
|
||||
return &out, nil
|
||||
} else {
|
||||
return nil, trace.BadParameter("received unsupported resource %T", in.Resource)
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ type Event struct {
|
||||
// *Event_IdentityCenterAccount
|
||||
// *Event_IdentityCenterPrincipalAssignment
|
||||
// *Event_IdentityCenterAccountAssignment
|
||||
// *Event_PluginStaticCredentials
|
||||
Resource isEvent_Resource `protobuf_oneof:"Resource"`
|
||||
}
|
||||
|
||||
@@ -716,6 +717,13 @@ func (x *Event) GetIdentityCenterAccountAssignment() *v114.AccountAssignment {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Event) GetPluginStaticCredentials() *types.PluginStaticCredentialsV1 {
|
||||
if x, ok := x.GetResource().(*Event_PluginStaticCredentials); ok {
|
||||
return x.PluginStaticCredentials
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isEvent_Resource interface {
|
||||
isEvent_Resource()
|
||||
}
|
||||
@@ -1071,6 +1079,11 @@ type Event_IdentityCenterAccountAssignment struct {
|
||||
IdentityCenterAccountAssignment *v114.AccountAssignment `protobuf:"bytes,74,opt,name=IdentityCenterAccountAssignment,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Event_PluginStaticCredentials struct {
|
||||
// PluginStaticCredentials is filled in PluginStaticCredentials related events
|
||||
PluginStaticCredentials *types.PluginStaticCredentialsV1 `protobuf:"bytes,75,opt,name=PluginStaticCredentials,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Event_ResourceHeader) isEvent_Resource() {}
|
||||
|
||||
func (*Event_CertAuthority) isEvent_Resource() {}
|
||||
@@ -1209,6 +1222,8 @@ func (*Event_IdentityCenterPrincipalAssignment) isEvent_Resource() {}
|
||||
|
||||
func (*Event_IdentityCenterAccountAssignment) isEvent_Resource() {}
|
||||
|
||||
func (*Event_PluginStaticCredentials) isEvent_Resource() {}
|
||||
|
||||
var File_teleport_legacy_client_proto_event_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_teleport_legacy_client_proto_event_proto_rawDesc = []byte{
|
||||
@@ -1267,7 +1282,7 @@ var file_teleport_legacy_client_proto_event_proto_rawDesc = []byte{
|
||||
0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x2f, 0x76, 0x31, 0x2f,
|
||||
0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x22, 0xcf, 0x28, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x54, 0x79,
|
||||
0x22, 0xad, 0x29, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x54, 0x79,
|
||||
0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65,
|
||||
0x12, 0x3f, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x65, 0x61, 0x64,
|
||||
@@ -1586,19 +1601,25 @@ var file_teleport_legacy_client_proto_event_proto_rawDesc = []byte{
|
||||
0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x48, 0x00, 0x52, 0x1f, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x65,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x73, 0x73, 0x69, 0x67,
|
||||
0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x31, 0x10, 0x32, 0x4a, 0x04, 0x08,
|
||||
0x3f, 0x10, 0x40, 0x4a, 0x04, 0x08, 0x44, 0x10, 0x45, 0x52, 0x12, 0x45, 0x78, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x75, 0x64, 0x69, 0x74, 0x52, 0x0e, 0x53,
|
||||
0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x13, 0x41,
|
||||
0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x50, 0x6c,
|
||||
0x61, 0x6e, 0x2a, 0x2a, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||
0x08, 0x0a, 0x04, 0x49, 0x4e, 0x49, 0x54, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x55, 0x54,
|
||||
0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x02, 0x42, 0x34,
|
||||
0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61,
|
||||
0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x5c, 0x0a, 0x17, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53,
|
||||
0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,
|
||||
0x18, 0x4b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x50,
|
||||
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x72, 0x65, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x56, 0x31, 0x48, 0x00, 0x52, 0x17, 0x50, 0x6c, 0x75, 0x67,
|
||||
0x69, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4a,
|
||||
0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x31, 0x10, 0x32, 0x4a, 0x04, 0x08, 0x3f, 0x10,
|
||||
0x40, 0x4a, 0x04, 0x08, 0x44, 0x10, 0x45, 0x52, 0x12, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6c, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x41, 0x75, 0x64, 0x69, 0x74, 0x52, 0x0e, 0x53, 0x74, 0x61,
|
||||
0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x13, 0x41, 0x75, 0x74,
|
||||
0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x61, 0x6e,
|
||||
0x2a, 0x2a, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a,
|
||||
0x04, 0x49, 0x4e, 0x49, 0x54, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x55, 0x54, 0x10, 0x01,
|
||||
0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x02, 0x42, 0x34, 0x5a, 0x32,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1684,6 +1705,7 @@ var file_teleport_legacy_client_proto_event_proto_goTypes = []any{
|
||||
(*v114.Account)(nil), // 65: teleport.identitycenter.v1.Account
|
||||
(*v114.PrincipalAssignment)(nil), // 66: teleport.identitycenter.v1.PrincipalAssignment
|
||||
(*v114.AccountAssignment)(nil), // 67: teleport.identitycenter.v1.AccountAssignment
|
||||
(*types.PluginStaticCredentialsV1)(nil), // 68: types.PluginStaticCredentialsV1
|
||||
}
|
||||
var file_teleport_legacy_client_proto_event_proto_depIdxs = []int32{
|
||||
0, // 0: proto.Event.Type:type_name -> proto.Operation
|
||||
@@ -1756,11 +1778,12 @@ var file_teleport_legacy_client_proto_event_proto_depIdxs = []int32{
|
||||
65, // 67: proto.Event.IdentityCenterAccount:type_name -> teleport.identitycenter.v1.Account
|
||||
66, // 68: proto.Event.IdentityCenterPrincipalAssignment:type_name -> teleport.identitycenter.v1.PrincipalAssignment
|
||||
67, // 69: proto.Event.IdentityCenterAccountAssignment:type_name -> teleport.identitycenter.v1.AccountAssignment
|
||||
70, // [70:70] is the sub-list for method output_type
|
||||
70, // [70:70] is the sub-list for method input_type
|
||||
70, // [70:70] is the sub-list for extension type_name
|
||||
70, // [70:70] is the sub-list for extension extendee
|
||||
0, // [0:70] is the sub-list for field type_name
|
||||
68, // 70: proto.Event.PluginStaticCredentials:type_name -> types.PluginStaticCredentialsV1
|
||||
71, // [71:71] is the sub-list for method output_type
|
||||
71, // [71:71] is the sub-list for method input_type
|
||||
71, // [71:71] is the sub-list for extension type_name
|
||||
71, // [71:71] is the sub-list for extension extendee
|
||||
0, // [0:71] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_teleport_legacy_client_proto_event_proto_init() }
|
||||
@@ -1838,6 +1861,7 @@ func file_teleport_legacy_client_proto_event_proto_init() {
|
||||
(*Event_IdentityCenterAccount)(nil),
|
||||
(*Event_IdentityCenterPrincipalAssignment)(nil),
|
||||
(*Event_IdentityCenterAccountAssignment)(nil),
|
||||
(*Event_PluginStaticCredentials)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
|
||||
@@ -206,5 +206,7 @@ message Event {
|
||||
// IdentityCenterAccountlAssignment is a resource representing a potential
|
||||
// Permission Set grant on a specific AWS account.
|
||||
teleport.identitycenter.v1.AccountAssignment IdentityCenterAccountAssignment = 74;
|
||||
// PluginStaticCredentials is filled in PluginStaticCredentials related events
|
||||
types.PluginStaticCredentialsV1 PluginStaticCredentials = 75;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ type Config struct {
|
||||
AutoUpdateService services.AutoUpdateServiceGetter
|
||||
ProvisioningStates services.ProvisioningStates
|
||||
IdentityCenter services.IdentityCenter
|
||||
PluginStaticCredentials services.PluginStaticCredentials
|
||||
}
|
||||
|
||||
func (c *Config) CheckAndSetDefaults() error {
|
||||
@@ -205,6 +206,7 @@ func NewCache(cfg Config) (*cache.Cache, error) {
|
||||
DynamicWindowsDesktops: cfg.DynamicWindowsDesktops,
|
||||
ProvisioningStates: cfg.ProvisioningStates,
|
||||
IdentityCenter: cfg.IdentityCenter,
|
||||
PluginStaticCredentials: cfg.PluginStaticCredentials,
|
||||
}
|
||||
|
||||
return cache.New(cfg.Setup(cacheCfg))
|
||||
|
||||
@@ -370,6 +370,7 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) {
|
||||
WebToken: svces.WebTokens(),
|
||||
WindowsDesktops: svces.WindowsDesktops,
|
||||
DynamicWindowsDesktops: svces.DynamicWindowsDesktops,
|
||||
PluginStaticCredentials: svces.PluginStaticCredentials,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
|
||||
Vendored
+11
@@ -198,6 +198,7 @@ func ForAuth(cfg Config) Config {
|
||||
{Kind: types.KindIdentityCenterAccount},
|
||||
{Kind: types.KindIdentityCenterPrincipalAssignment},
|
||||
{Kind: types.KindIdentityCenterAccountAssignment},
|
||||
{Kind: types.KindPluginStaticCredentials},
|
||||
}
|
||||
cfg.QueueSize = defaults.AuthQueueSize
|
||||
// We don't want to enable partial health for auth cache because auth uses an event stream
|
||||
@@ -550,6 +551,7 @@ type Cache struct {
|
||||
staticHostUsersCache *local.StaticHostUserService
|
||||
provisioningStatesCache *local.ProvisioningStateService
|
||||
identityCenterCache *local.IdentityCenterService
|
||||
pluginStaticCredentialsCache *local.PluginStaticCredentialsService
|
||||
|
||||
// closed indicates that the cache has been closed
|
||||
closed atomic.Bool
|
||||
@@ -784,6 +786,8 @@ type Config struct {
|
||||
|
||||
// IdentityCenter is the upstream Identity Center service that we're caching
|
||||
IdentityCenter services.IdentityCenter
|
||||
// PluginStaticCredentials is the plugin static credentials services
|
||||
PluginStaticCredentials services.PluginStaticCredentials
|
||||
}
|
||||
|
||||
// CheckAndSetDefaults checks parameters and sets default values
|
||||
@@ -1023,6 +1027,12 @@ func New(config Config) (*Cache, error) {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
pluginStaticCredentialsCache, err := local.NewPluginStaticCredentialsService(config.Backend)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
cs := &Cache{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
@@ -1070,6 +1080,7 @@ func New(config Config) (*Cache, error) {
|
||||
staticHostUsersCache: staticHostUserCache,
|
||||
provisioningStatesCache: provisioningStatesCache,
|
||||
identityCenterCache: identityCenterCache,
|
||||
pluginStaticCredentialsCache: pluginStaticCredentialsCache,
|
||||
Logger: log.WithFields(log.Fields{
|
||||
teleport.ComponentKey: config.Component,
|
||||
}),
|
||||
|
||||
Vendored
+32
-9
@@ -141,17 +141,19 @@ type testPack struct {
|
||||
autoUpdateService services.AutoUpdateService
|
||||
provisioningStates services.ProvisioningStates
|
||||
identityCenter services.IdentityCenter
|
||||
pluginStaticCredentials *local.PluginStaticCredentialsService
|
||||
}
|
||||
|
||||
// testFuncs are functions to support testing an object in a cache.
|
||||
type testFuncs[T types.Resource] struct {
|
||||
newResource func(string) (T, error)
|
||||
create func(context.Context, T) error
|
||||
list func(context.Context) ([]T, error)
|
||||
cacheGet func(context.Context, string) (T, error)
|
||||
cacheList func(context.Context) ([]T, error)
|
||||
update func(context.Context, T) error
|
||||
deleteAll func(context.Context) error
|
||||
newResource func(string) (T, error)
|
||||
create func(context.Context, T) error
|
||||
list func(context.Context) ([]T, error)
|
||||
cacheGet func(context.Context, string) (T, error)
|
||||
cacheList func(context.Context) ([]T, error)
|
||||
update func(context.Context, T) error
|
||||
deleteAll func(context.Context) error
|
||||
changeResource func(T)
|
||||
}
|
||||
|
||||
// testFuncs153 are functions to support testing an RFD153-style resource in a cache.
|
||||
@@ -402,6 +404,11 @@ func newPackWithoutCache(dir string, opts ...packOption) (*testPack, error) {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
p.pluginStaticCredentials, err = local.NewPluginStaticCredentialsService(p.backend)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@@ -455,6 +462,7 @@ func newPack(dir string, setupConfig func(c Config) Config, opts ...packOption)
|
||||
AutoUpdateService: p.autoUpdateService,
|
||||
ProvisioningStates: p.provisioningStates,
|
||||
IdentityCenter: p.identityCenter,
|
||||
PluginStaticCredentials: p.pluginStaticCredentials,
|
||||
MaxRetryPeriod: 200 * time.Millisecond,
|
||||
EventsC: p.eventsC,
|
||||
}))
|
||||
@@ -868,6 +876,7 @@ func TestCompletenessInit(t *testing.T) {
|
||||
ProvisioningStates: p.provisioningStates,
|
||||
MaxRetryPeriod: 200 * time.Millisecond,
|
||||
IdentityCenter: p.identityCenter,
|
||||
PluginStaticCredentials: p.pluginStaticCredentials,
|
||||
EventsC: p.eventsC,
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
@@ -951,6 +960,7 @@ func TestCompletenessReset(t *testing.T) {
|
||||
AutoUpdateService: p.autoUpdateService,
|
||||
ProvisioningStates: p.provisioningStates,
|
||||
IdentityCenter: p.identityCenter,
|
||||
PluginStaticCredentials: p.pluginStaticCredentials,
|
||||
MaxRetryPeriod: 200 * time.Millisecond,
|
||||
EventsC: p.eventsC,
|
||||
}))
|
||||
@@ -1161,6 +1171,7 @@ func TestListResources_NodesTTLVariant(t *testing.T) {
|
||||
AutoUpdateService: p.autoUpdateService,
|
||||
ProvisioningStates: p.provisioningStates,
|
||||
IdentityCenter: p.identityCenter,
|
||||
PluginStaticCredentials: p.pluginStaticCredentials,
|
||||
MaxRetryPeriod: 200 * time.Millisecond,
|
||||
EventsC: p.eventsC,
|
||||
neverOK: true, // ensure reads are never healthy
|
||||
@@ -1256,6 +1267,7 @@ func initStrategy(t *testing.T) {
|
||||
AutoUpdateService: p.autoUpdateService,
|
||||
ProvisioningStates: p.provisioningStates,
|
||||
IdentityCenter: p.identityCenter,
|
||||
PluginStaticCredentials: p.pluginStaticCredentials,
|
||||
MaxRetryPeriod: 200 * time.Millisecond,
|
||||
EventsC: p.eventsC,
|
||||
}))
|
||||
@@ -2926,10 +2938,20 @@ func TestStaticHostUsers(t *testing.T) {
|
||||
func testResources[T types.Resource](t *testing.T, p *testPack, funcs testFuncs[T]) {
|
||||
ctx := context.Background()
|
||||
|
||||
if funcs.changeResource == nil {
|
||||
funcs.changeResource = func(t T) {
|
||||
if t.Expiry().IsZero() {
|
||||
t.SetExpiry(time.Now().Add(30 * time.Minute))
|
||||
} else {
|
||||
t.SetExpiry(t.Expiry().Add(30 * time.Minute))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a resource.
|
||||
r, err := funcs.newResource("test-sp")
|
||||
require.NoError(t, err)
|
||||
r.SetExpiry(time.Now().Add(30 * time.Minute))
|
||||
funcs.changeResource(r)
|
||||
|
||||
err = funcs.create(ctx, r)
|
||||
require.NoError(t, err)
|
||||
@@ -2963,7 +2985,7 @@ func testResources[T types.Resource](t *testing.T, p *testPack, funcs testFuncs[
|
||||
// update is optional as not every resource implements it
|
||||
if funcs.update != nil {
|
||||
// Update the resource and upsert it into the backend again.
|
||||
r.SetExpiry(r.Expiry().Add(30 * time.Minute))
|
||||
funcs.changeResource(r)
|
||||
err = funcs.update(ctx, r)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -3521,6 +3543,7 @@ func TestCacheWatchKindExistsInEvents(t *testing.T) {
|
||||
types.KindIdentityCenterAccount: types.Resource153ToLegacy(newIdentityCenterAccount("some_account")),
|
||||
types.KindIdentityCenterAccountAssignment: types.Resource153ToLegacy(newIdentityCenterAccountAssignment("some_account_assignment")),
|
||||
types.KindIdentityCenterPrincipalAssignment: types.Resource153ToLegacy(newIdentityCenterPrincipalAssignment("some_principal_assignment")),
|
||||
types.KindPluginStaticCredentials: &types.PluginStaticCredentialsV1{},
|
||||
}
|
||||
|
||||
for name, cfg := range cases {
|
||||
|
||||
Vendored
+15
@@ -176,6 +176,7 @@ type cacheCollections struct {
|
||||
identityCenterAccounts collectionReader[identityCenterAccountGetter]
|
||||
identityCenterPrincipalAssignments collectionReader[identityCenterPrincipalAssignmentGetter]
|
||||
identityCenterAccountAssignments collectionReader[identityCenterAccountAssignmentGetter]
|
||||
pluginStaticCredentials collectionReader[pluginStaticCredentialsGetter]
|
||||
}
|
||||
|
||||
// setupCollections returns a registry of collections.
|
||||
@@ -784,6 +785,20 @@ func setupCollections(c *Cache, watches []types.WatchKind) (*cacheCollections, e
|
||||
}
|
||||
collections.byKind[resourceKind] = collections.identityCenterAccountAssignments
|
||||
|
||||
case types.KindPluginStaticCredentials:
|
||||
if c.PluginStaticCredentials == nil {
|
||||
return nil, trace.BadParameter("missing parameter PluginStaticCredentials")
|
||||
}
|
||||
collections.pluginStaticCredentials = &genericCollection[
|
||||
types.PluginStaticCredentials,
|
||||
pluginStaticCredentialsGetter,
|
||||
pluginStaticCredentialsExecutor,
|
||||
]{
|
||||
cache: c,
|
||||
watch: watch,
|
||||
}
|
||||
collections.byKind[resourceKind] = collections.pluginStaticCredentials
|
||||
|
||||
default:
|
||||
return nil, trace.BadParameter("resource %q is not supported", watch.Kind)
|
||||
}
|
||||
|
||||
+84
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
)
|
||||
|
||||
func (c *Cache) GetPluginStaticCredentials(ctx context.Context, name string) (types.PluginStaticCredentials, error) {
|
||||
ctx, span := c.Tracer.Start(ctx, "cache/GetPluginStaticCredentials")
|
||||
defer span.End()
|
||||
|
||||
rg, err := readCollectionCache(c, c.collections.pluginStaticCredentials)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
defer rg.Release()
|
||||
return rg.reader.GetPluginStaticCredentials(ctx, name)
|
||||
}
|
||||
|
||||
func (c *Cache) GetPluginStaticCredentialsByLabels(ctx context.Context, labels map[string]string) ([]types.PluginStaticCredentials, error) {
|
||||
ctx, span := c.Tracer.Start(ctx, "cache/GetPluginStaticCredentialsByLabels")
|
||||
defer span.End()
|
||||
|
||||
rg, err := readCollectionCache(c, c.collections.pluginStaticCredentials)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
defer rg.Release()
|
||||
return rg.reader.GetPluginStaticCredentialsByLabels(ctx, labels)
|
||||
}
|
||||
|
||||
type pluginStaticCredentialsGetter interface {
|
||||
GetPluginStaticCredentials(ctx context.Context, name string) (types.PluginStaticCredentials, error)
|
||||
GetPluginStaticCredentialsByLabels(ctx context.Context, labels map[string]string) ([]types.PluginStaticCredentials, error)
|
||||
}
|
||||
|
||||
var _ executor[types.PluginStaticCredentials, pluginStaticCredentialsGetter] = pluginStaticCredentialsExecutor{}
|
||||
|
||||
type pluginStaticCredentialsExecutor struct{}
|
||||
|
||||
func (pluginStaticCredentialsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.PluginStaticCredentials, error) {
|
||||
return cache.pluginStaticCredentialsCache.GetAllPluginStaticCredentials(ctx)
|
||||
}
|
||||
|
||||
func (pluginStaticCredentialsExecutor) upsert(ctx context.Context, cache *Cache, resource types.PluginStaticCredentials) error {
|
||||
_, err := cache.pluginStaticCredentialsCache.UpsertPluginStaticCredentials(ctx, resource)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
func (pluginStaticCredentialsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
|
||||
return cache.pluginStaticCredentialsCache.DeleteAllPluginStaticCredentials(ctx)
|
||||
}
|
||||
|
||||
func (pluginStaticCredentialsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
|
||||
return cache.pluginStaticCredentialsCache.DeletePluginStaticCredentials(ctx, resource.GetName())
|
||||
}
|
||||
|
||||
func (pluginStaticCredentialsExecutor) isSingleton() bool { return false }
|
||||
|
||||
func (pluginStaticCredentialsExecutor) getReader(cache *Cache, cacheOK bool) pluginStaticCredentialsGetter {
|
||||
if cacheOK {
|
||||
return cache.pluginStaticCredentialsCache
|
||||
}
|
||||
return cache.Config.PluginStaticCredentials
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
)
|
||||
|
||||
func TestPluginStaticCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p, err := newPack(t.TempDir(), ForAuth)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(p.Close)
|
||||
|
||||
makeLabels := func(name string) map[string]string {
|
||||
return map[string]string{
|
||||
"resource-label": "label-for-" + name,
|
||||
}
|
||||
}
|
||||
|
||||
cacheGets := []struct {
|
||||
name string
|
||||
fn func(context.Context, string) (types.PluginStaticCredentials, error)
|
||||
}{
|
||||
{
|
||||
name: "GetPluginStaticCredentials",
|
||||
fn: p.cache.GetPluginStaticCredentials,
|
||||
},
|
||||
{
|
||||
name: "GetPluginStaticCredentialsByLabels",
|
||||
fn: func(ctx context.Context, name string) (types.PluginStaticCredentials, error) {
|
||||
creds, err := p.cache.GetPluginStaticCredentialsByLabels(ctx, makeLabels(name))
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if len(creds) != 1 {
|
||||
return nil, trace.CompareFailed("expecting one creds for this test but got %v", len(creds))
|
||||
}
|
||||
return creds[0], nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, cacheGet := range cacheGets {
|
||||
t.Run(cacheGet.name, func(t *testing.T) {
|
||||
// Empty backend before the test.
|
||||
err := p.pluginStaticCredentials.DeleteAllPluginStaticCredentials(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
testResources(t, p, testFuncs[types.PluginStaticCredentials]{
|
||||
newResource: func(name string) (types.PluginStaticCredentials, error) {
|
||||
return types.NewPluginStaticCredentials(
|
||||
types.Metadata{
|
||||
Name: name,
|
||||
Labels: makeLabels(name),
|
||||
},
|
||||
types.PluginStaticCredentialsSpecV1{
|
||||
Credentials: &types.PluginStaticCredentialsSpecV1_APIToken{
|
||||
APIToken: "some-token",
|
||||
},
|
||||
})
|
||||
},
|
||||
create: p.pluginStaticCredentials.CreatePluginStaticCredentials,
|
||||
list: p.pluginStaticCredentials.GetAllPluginStaticCredentials,
|
||||
update: func(ctx context.Context, cred types.PluginStaticCredentials) error {
|
||||
_, err := p.pluginStaticCredentials.UpdatePluginStaticCredentials(ctx, cred)
|
||||
return err
|
||||
},
|
||||
deleteAll: p.pluginStaticCredentials.DeleteAllPluginStaticCredentials,
|
||||
cacheList: p.cache.pluginStaticCredentialsCache.GetAllPluginStaticCredentials,
|
||||
cacheGet: cacheGet.fn,
|
||||
changeResource: func(cred types.PluginStaticCredentials) {
|
||||
// types.PluginStaticCredentials does not support Expires. Let's
|
||||
// use labels.
|
||||
labels := cred.GetStaticLabels()
|
||||
labels["now"] = time.Now().String()
|
||||
cred.SetStaticLabels(labels)
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2552,6 +2552,7 @@ func (process *TeleportProcess) newAccessCacheForServices(cfg accesspoint.Config
|
||||
cfg.AutoUpdateService = services.AutoUpdateService
|
||||
cfg.ProvisioningStates = services.ProvisioningStates
|
||||
cfg.IdentityCenter = services.IdentityCenter
|
||||
cfg.PluginStaticCredentials = services.PluginStaticCredentials
|
||||
|
||||
return accesspoint.NewCache(cfg)
|
||||
}
|
||||
|
||||
@@ -248,6 +248,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type
|
||||
parser = newIdentityCenterPrincipalAssignmentParser()
|
||||
case types.KindIdentityCenterAccountAssignment:
|
||||
parser = newIdentityCenterAccountAssignmentParser()
|
||||
case types.KindPluginStaticCredentials:
|
||||
parser = newPluginStaticCredentialsParser()
|
||||
default:
|
||||
if watch.AllowPartialSuccess {
|
||||
continue
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
@@ -94,4 +95,62 @@ func (p *PluginStaticCredentialsService) DeletePluginStaticCredentials(ctx conte
|
||||
return trace.Wrap(p.svc.DeleteResource(ctx, name))
|
||||
}
|
||||
|
||||
// GetAllPluginStaticCredentials will get all plugin static credentials. Cache
|
||||
// use only.
|
||||
func (p *PluginStaticCredentialsService) GetAllPluginStaticCredentials(ctx context.Context) ([]types.PluginStaticCredentials, error) {
|
||||
creds, err := p.svc.GetResources(ctx)
|
||||
return creds, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// DeleteAllPluginStaticCredentials will remove all plugin static credentials.
|
||||
// Cache use only.
|
||||
func (p *PluginStaticCredentialsService) DeleteAllPluginStaticCredentials(ctx context.Context) error {
|
||||
return trace.Wrap(p.svc.DeleteAllResources(ctx))
|
||||
}
|
||||
|
||||
// UpsertPluginStaticCredentials will upsert a plugin static credentials. Cache
|
||||
// use only.
|
||||
func (p *PluginStaticCredentialsService) UpsertPluginStaticCredentials(ctx context.Context, item types.PluginStaticCredentials) (types.PluginStaticCredentials, error) {
|
||||
cred, err := p.svc.UpsertResource(ctx, item)
|
||||
return cred, trace.Wrap(err)
|
||||
}
|
||||
|
||||
var _ services.PluginStaticCredentials = (*PluginStaticCredentialsService)(nil)
|
||||
|
||||
type pluginStaticCredentialsParser struct {
|
||||
baseParser
|
||||
}
|
||||
|
||||
func newPluginStaticCredentialsParser() *pluginStaticCredentialsParser {
|
||||
return &pluginStaticCredentialsParser{
|
||||
baseParser: newBaseParser(backend.NewKey(pluginStaticCredentialsPrefix)),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pluginStaticCredentialsParser) parse(event backend.Event) (types.Resource, error) {
|
||||
switch event.Type {
|
||||
case types.OpDelete:
|
||||
parts := event.Item.Key.Components()
|
||||
if len(parts) != 2 {
|
||||
return nil, trace.BadParameter("malformed key for %s event: %s", types.KindPluginStaticCredentials, event.Item.Key)
|
||||
}
|
||||
|
||||
return &types.ResourceHeader{
|
||||
Kind: types.KindPluginStaticCredentials,
|
||||
Version: types.V1,
|
||||
Metadata: types.Metadata{
|
||||
Name: parts[1],
|
||||
Namespace: apidefaults.Namespace,
|
||||
Description: parts[0],
|
||||
},
|
||||
}, nil
|
||||
|
||||
case types.OpPut:
|
||||
return services.UnmarshalPluginStaticCredentials(
|
||||
event.Item.Value,
|
||||
services.WithExpires(event.Item.Expires),
|
||||
services.WithRevision(event.Item.Revision))
|
||||
default:
|
||||
return nil, trace.BadParameter("event %v is not supported", event.Type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,11 @@ func TestPluginStaticCredentialsCRUD(t *testing.T) {
|
||||
cmpopts.IgnoreFields(types.Metadata{}, "Revision"),
|
||||
))
|
||||
|
||||
// Fetch all.
|
||||
all, err := service.GetAllPluginStaticCredentials(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, all, 2)
|
||||
|
||||
// Try to fetch a static credential that doesn't exist.
|
||||
_, err = service.GetPluginStaticCredentials(ctx, "doesnotexist")
|
||||
require.True(t, trace.IsNotFound(err))
|
||||
@@ -132,4 +137,17 @@ func TestPluginStaticCredentialsCRUD(t *testing.T) {
|
||||
// Try to delete a static credential that doesn't exist.
|
||||
err = service.DeletePluginStaticCredentials(ctx, "doesnotexist")
|
||||
require.True(t, trace.IsNotFound(err))
|
||||
|
||||
// Upsert.
|
||||
_, err = service.UpsertPluginStaticCredentials(ctx, cred1)
|
||||
require.NoError(t, err)
|
||||
_, err = service.GetPluginStaticCredentials(ctx, cred1.GetName())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete all.
|
||||
err = service.DeleteAllPluginStaticCredentials(ctx)
|
||||
require.NoError(t, err)
|
||||
all, err = service.GetAllPluginStaticCredentials(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, all)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package services
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -45,4 +46,24 @@ func TestMarshalPluginStaticCredentialsRoundTrip(t *testing.T) {
|
||||
unmarshaled, err := UnmarshalPluginStaticCredentials(payload)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, cmp.Diff(creds, unmarshaled))
|
||||
|
||||
// TODO(greedy52) PluginStaticCredentials uses protojson for marshaling
|
||||
// and unmarshaling. Unfortunately Expires (*time.Time) is not supported
|
||||
// and becomes zero after the marshaling. Unlikely Expires will ever be
|
||||
// needed for PluginStaticCredentials in production but this can cause
|
||||
// unexpected issues in other places (like resource.SetExpiry in cache
|
||||
// tests).
|
||||
t.Run("with expires", func(t *testing.T) {
|
||||
t.Skip("PluginStaticCredentials does not marshal Expires")
|
||||
now := time.Now()
|
||||
|
||||
creds.SetExpiry(now)
|
||||
payload, err := MarshalPluginStaticCredentials(creds)
|
||||
require.NoError(t, err)
|
||||
|
||||
unmarshaled, err := UnmarshalPluginStaticCredentials(payload)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, cmp.Diff(creds, unmarshaled))
|
||||
require.Equal(t, now, unmarshaled.Expiry())
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user