Skip to content

Commit 00768a1

Browse files
committed
fix(azure-connection): Restrict list and get to federated identity credentials
`List(..)` now filters out any connections not configured for federated identity credentials `Get(...)` now returns an error for requests that are not for federated identity credentials
1 parent 3b10bee commit 00768a1

File tree

2 files changed

+181
-3
lines changed

2 files changed

+181
-3
lines changed

dynatrace/api/builtin/hyperscalerauthentication/connections/azure/authentication/service.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,35 @@ type service struct {
4141
}
4242

4343
func (me *service) List(ctx context.Context) (api.Stubs, error) {
44-
return me.connService.List(ctx)
44+
stubs, err := me.connService.List(ctx)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
var filteredStubs api.Stubs
50+
for _, stub := range stubs {
51+
connValue := azureconnection_settings.Settings{}
52+
if err := me.connService.Get(ctx, stub.ID, &connValue); err != nil {
53+
return nil, err
54+
}
55+
if connValue.Type == azureconnection_settings.Types.Federatedidentitycredential {
56+
filteredStubs = append(filteredStubs, stub)
57+
}
58+
}
59+
60+
return filteredStubs, nil
4561
}
4662

4763
func (me *service) Get(ctx context.Context, id string, v *connectionauthentication_settings.Settings) error {
4864
connValue := azureconnection_settings.Settings{}
4965
if err := me.connService.Get(ctx, id, &connValue); err != nil {
5066
return err
5167
}
68+
69+
if connValue.Type != azureconnection_settings.Types.Federatedidentitycredential {
70+
return errors.New("the associated Azure connection is not configured for federated identity credential authentication")
71+
}
72+
5273
if connValue.FederatedIdentityCredential != nil {
5374
v.ApplicationID = connValue.FederatedIdentityCredential.ApplicationID
5475
v.DirectoryID = connValue.FederatedIdentityCredential.DirectoryID

dynatrace/api/builtin/hyperscalerauthentication/connections/azure/authentication/service_test.go

Lines changed: 159 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,172 @@ func (m *mockCRUDService) Get(ctx context.Context, id string, v *azure.Settings)
4444
func (m *mockCRUDService) Update(ctx context.Context, id string, v *azure.Settings) error {
4545
return m.updateFunc(ctx, id, v)
4646
}
47-
func (m *mockCRUDService) List(_ context.Context) (api.Stubs, error) {
48-
return api.Stubs{}, nil
47+
func (m *mockCRUDService) List(ctx context.Context) (api.Stubs, error) {
48+
return m.listFunc(ctx)
4949
}
5050
func (m *mockCRUDService) Create(_ context.Context, _ *azure.Settings) (*api.Stub, error) {
5151
return nil, nil
5252
}
5353
func (m *mockCRUDService) Delete(_ context.Context, _ string) error { return nil }
5454
func (m *mockCRUDService) SchemaID() string { return "" }
5555

56+
func TestService_List(t *testing.T) {
57+
t.Run("returns stubs only for federated identity credentials", func(t *testing.T) {
58+
59+
mockConn1 := &azure.Settings{
60+
Type: azure.Types.Federatedidentitycredential,
61+
FederatedIdentityCredential: &azure.FederatedIdentityCredential{Consumers: []azure.ConsumersOfFederatedIdentityCredential{"DA"}},
62+
Name: "Federated Identity Credential Connection 1",
63+
}
64+
65+
mockConn2 := &azure.Settings{
66+
Type: azure.Types.Clientsecret,
67+
ClientSecret: &azure.ClientSecretConfig{},
68+
Name: "Client Secret Connection",
69+
}
70+
71+
mockConn3 := &azure.Settings{
72+
Type: azure.Types.Federatedidentitycredential,
73+
FederatedIdentityCredential: &azure.FederatedIdentityCredential{Consumers: []azure.ConsumersOfFederatedIdentityCredential{"DA"}},
74+
Name: "Federated Identity Credential Connection 2",
75+
}
76+
77+
mock := &mockCRUDService{
78+
listFunc: func(ctx context.Context) (api.Stubs, error) {
79+
return api.Stubs{
80+
{ID: "1", Name: mockConn1.Name},
81+
{ID: "2", Name: mockConn2.Name},
82+
{ID: "3", Name: mockConn3.Name},
83+
}, nil
84+
},
85+
getFunc: func(ctx context.Context, id string, v *azure.Settings) error {
86+
switch id {
87+
case "1":
88+
*v = *mockConn1
89+
case "2":
90+
*v = *mockConn2
91+
case "3":
92+
*v = *mockConn3
93+
default:
94+
return errors.New("not found")
95+
}
96+
return nil
97+
},
98+
}
99+
100+
svc := &service{connService: mock}
101+
stubs, err := svc.List(context.Background())
102+
103+
assert.NoError(t, err)
104+
assert.Len(t, stubs, 2)
105+
assert.Equal(t, "1", stubs[0].ID)
106+
assert.Equal(t, mockConn1.Name, stubs[0].Name)
107+
assert.Equal(t, "3", stubs[1].ID)
108+
assert.Equal(t, mockConn3.Name, stubs[1].Name)
109+
})
110+
111+
t.Run("returns error if connService.List errors", func(t *testing.T) {
112+
mock := &mockCRUDService{
113+
listFunc: func(ctx context.Context) (api.Stubs, error) {
114+
return nil, errors.New("list error")
115+
},
116+
}
117+
118+
svc := &service{connService: mock}
119+
_, err := svc.List(context.Background())
120+
121+
assert.ErrorContains(t, err, "list error")
122+
})
123+
124+
t.Run("returns error if connService.Get errors", func(t *testing.T) {
125+
mock := &mockCRUDService{
126+
listFunc: func(ctx context.Context) (api.Stubs, error) {
127+
return api.Stubs{
128+
{ID: "1", Name: "connection1"},
129+
{ID: "2", Name: "connection2"},
130+
{ID: "3", Name: "connection3"},
131+
}, nil
132+
},
133+
getFunc: func(ctx context.Context, id string, v *azure.Settings) error {
134+
return errors.New("get error")
135+
},
136+
}
137+
138+
svc := &service{connService: mock}
139+
_, err := svc.List(context.Background())
140+
141+
assert.ErrorContains(t, err, "get error")
142+
})
143+
}
144+
145+
func TestService_Get(t *testing.T) {
146+
connectionID := "id1"
147+
148+
t.Run("success if connection uses federated identity credentials", func(t *testing.T) {
149+
150+
mockConn := &azure.Settings{
151+
Type: azure.Types.Federatedidentitycredential,
152+
FederatedIdentityCredential: &azure.FederatedIdentityCredential{Consumers: []azure.ConsumersOfFederatedIdentityCredential{"DA"}},
153+
Name: "Federated Identity Credential Connection 2",
154+
}
155+
156+
mock := &mockCRUDService{
157+
getFunc: func(ctx context.Context, id string, v *azure.Settings) error {
158+
assert.Equal(t, connectionID, id)
159+
*v = *mockConn
160+
return nil
161+
},
162+
}
163+
164+
svc := &service{connService: mock}
165+
v := connectionauthentication_settings.Settings{}
166+
167+
err := svc.Get(context.Background(), connectionID, &v)
168+
169+
assert.NoError(t, err)
170+
assert.Equal(t, mockConn.Name, v.Name)
171+
assert.Equal(t, connectionID, v.AzureConnectionID)
172+
assert.Equal(t, mockConn.FederatedIdentityCredential.ApplicationID, v.ApplicationID)
173+
assert.Equal(t, mockConn.FederatedIdentityCredential.DirectoryID, v.DirectoryID)
174+
})
175+
176+
t.Run("returns error if connection uses client secret", func(t *testing.T) {
177+
mockConn := &azure.Settings{
178+
Type: azure.Types.Clientsecret,
179+
ClientSecret: &azure.ClientSecretConfig{},
180+
Name: "client secret connection",
181+
}
182+
183+
mock := &mockCRUDService{
184+
getFunc: func(_ context.Context, id string, v *azure.Settings) error {
185+
assert.Equal(t, connectionID, id)
186+
*v = *mockConn
187+
return nil
188+
},
189+
}
190+
191+
svc := &service{connService: mock}
192+
v := connectionauthentication_settings.Settings{}
193+
err := svc.Get(context.Background(), connectionID, &v)
194+
195+
assert.ErrorContains(t, err, "not configured for federated identity credential")
196+
})
197+
198+
t.Run("returns error if connService.Get errors", func(t *testing.T) {
199+
mock := &mockCRUDService{
200+
getFunc: func(ctx context.Context, id string, v *azure.Settings) error {
201+
return errors.New("get error")
202+
},
203+
}
204+
205+
svc := &service{connService: mock}
206+
v := connectionauthentication_settings.Settings{}
207+
err := svc.Get(context.Background(), connectionID, &v)
208+
209+
assert.ErrorContains(t, err, "get error")
210+
})
211+
}
212+
56213
// TestService_Create tests the Create method of the dynatrace_azure_connection_authentication resource service
57214
func TestService_Create(t *testing.T) {
58215
const connID = "conn-123"

0 commit comments

Comments
 (0)