1 package clustersecrets
2
3 import (
4 "context"
5 "database/sql/driver"
6 "testing"
7 "time"
8
9 "github.com/DATA-DOG/go-sqlmock"
10 "github.com/stretchr/testify/require"
11
12 "edge-infra.dev/pkg/edge/api/bsl/types"
13 "edge-infra.dev/pkg/edge/api/graph/model"
14 "edge-infra.dev/pkg/edge/api/middleware"
15 configType "edge-infra.dev/pkg/edge/api/types"
16 "edge-infra.dev/pkg/sds/clustersecrets/common"
17 )
18
19 var (
20 mockClusterSecretEdgeID = "4c235de1-6984-4c96-bae9-0fc35f94a020"
21 mockTerminalEdgeID = "78de6c9a-5d30-4860-bd01-be751c849d7e"
22 clusterSecretColumns = []string{"cluster_secret_edge_id", "cluster_secret_lease_edge_id", "secert_name", "secret_version", "secret_expire_at"}
23 fetchSecretVersionQueryType = []string{"expire_at", "secret_version"}
24 config = &configType.Config{
25 EdgeOptInSecurityCompliance: true,
26 EdgeMaxLeaseValidityPeriod: "48h",
27 }
28 )
29
30 func TestAddClusterSecretValidityInDays(t *testing.T) {
31 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
32 require.NoError(t, err)
33 defer db.Close()
34
35 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
36 Username: testuser,
37 })
38 config.EdgeMaxSecretValidityPeriod = "60d"
39 expirationTime := time.Now().UTC().AddDate(0, 0, 60).Format(time.RFC3339)
40
41 clusterSecret := common.ClusterSecret{
42 SecretEdgeID: mockClusterSecretEdgeID,
43 LeaseEdgeID: mockLeaseID,
44 Name: "breakglass",
45 Version: "1",
46 Type: model.ClusterSecretTypeBreakglass,
47 }
48 mockAddClusterSecretQuery(mock, expirationTime, sqlmock.NewResult(1, 1))
49 clusterSecretService := NewClusterSecretService(db, nil, config)
50 require.NoError(t, clusterSecretService.AddClusterSecret(ctx, clusterSecret))
51 require.NoError(t, mock.ExpectationsWereMet())
52 }
53
54 func TestAddClusterSecretValidityInHours(t *testing.T) {
55 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
56 require.NoError(t, err)
57 defer db.Close()
58
59 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
60 Username: testuser,
61 })
62 config.EdgeMaxSecretValidityPeriod = "60h"
63 expirationTime := time.Now().UTC().Add(time.Hour * 60).Format(time.RFC3339)
64
65 clusterSecret := common.ClusterSecret{
66 SecretEdgeID: mockClusterSecretEdgeID,
67 LeaseEdgeID: mockLeaseID,
68 Name: "breakglass",
69 Version: "1",
70 Type: model.ClusterSecretTypeBreakglass,
71 }
72 mockAddClusterSecretQuery(mock, expirationTime, sqlmock.NewResult(1, 1))
73
74 clusterSecretService := NewClusterSecretService(db, nil, config)
75 require.NoError(t, clusterSecretService.AddClusterSecret(ctx, clusterSecret))
76 require.NoError(t, mock.ExpectationsWereMet())
77 }
78
79 func TestAddClusterSecretValidityInMins(t *testing.T) {
80 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
81 require.NoError(t, err)
82 defer db.Close()
83
84 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
85 Username: testuser,
86 })
87 config.EdgeMaxSecretValidityPeriod = "60m"
88 expirationTime := time.Now().UTC().Add(time.Minute * 60).Format(time.RFC3339)
89
90 clusterSecret := common.ClusterSecret{
91 SecretEdgeID: mockClusterSecretEdgeID,
92 LeaseEdgeID: mockLeaseID,
93 Name: "breakglass",
94 Version: "1",
95 Type: model.ClusterSecretTypeBreakglass,
96 }
97 mockAddClusterSecretQuery(mock, expirationTime, sqlmock.NewResult(1, 1))
98
99 clusterSecretService := NewClusterSecretService(db, nil, config)
100 require.NoError(t, clusterSecretService.AddClusterSecret(ctx, clusterSecret))
101 require.NoError(t, mock.ExpectationsWereMet())
102 }
103
104 func mockAddClusterSecretQuery(mock sqlmock.Sqlmock, expirationTime string, result driver.Result) {
105 mock.ExpectExec(AddClusterSecretQuery).WithArgs(mockClusterSecretEdgeID, mockLeaseID, model.ClusterSecretTypeBreakglass.String(), "1", expirationTime, time.Now().UTC().Format(time.RFC3339), time.Now().UTC().Format(time.RFC3339), model.ClusterSecretTypeBreakglass.String()).
106 WillReturnResult(result)
107 }
108
109 func TestFetchClusterSecret(t *testing.T) {
110 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
111 require.NoError(t, err)
112 defer db.Close()
113
114 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
115 Username: testuser,
116 })
117 expirationTime := time.Now().UTC().AddDate(0, 0, 60).Format(time.RFC3339)
118 mockFetchClusterSecret(mock, expirationTime)
119
120 clusterSecretService := NewClusterSecretService(db, nil, config)
121 clusterSecret, err := clusterSecretService.FetchClusterSecret(ctx, mockClusterEdgeID, model.ClusterSecretTypeBreakglass)
122 require.NoError(t, err)
123 require.Equal(t, mockClusterSecretEdgeID, clusterSecret.SecretEdgeID)
124 require.Equal(t, mockLeaseID, clusterSecret.LeaseEdgeID)
125 require.Equal(t, "os-user-store", clusterSecret.Name)
126 require.Equal(t, expirationTime, clusterSecret.ExpirationTime)
127 require.Equal(t, model.ClusterSecretTypeBreakglass, clusterSecret.Type)
128 require.Equal(t, "1", clusterSecret.Version)
129 require.NoError(t, mock.ExpectationsWereMet())
130 }
131
132 func mockFetchClusterSecret(mock sqlmock.Sqlmock, expirationTime string) {
133 rows := mock.NewRows(clusterSecretColumns).AddRow(mockClusterSecretEdgeID, mockLeaseID, "os-user-store", "1", expirationTime)
134 mock.ExpectQuery(FetchClusterSecretQuery).WithArgs(mockClusterEdgeID, model.ClusterSecretTypeBreakglass).WillReturnRows(rows)
135 }
136
137 func TestUpdateClusterSecret(t *testing.T) {
138 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
139 require.NoError(t, err)
140 defer db.Close()
141 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
142 Username: testuser,
143 })
144 config.EdgeMaxSecretValidityPeriod = "60d"
145 expirationTime := time.Now().UTC().AddDate(0, 0, 60).Format(time.RFC3339)
146 mockUpdateClusterSecret(mock, "1", expirationTime, sqlmock.NewResult(1, 1))
147
148 clusterSecretService := NewClusterSecretService(db, nil, config)
149 require.NoError(t, clusterSecretService.UpdateClusterSecret(ctx, mockClusterSecretEdgeID, model.ClusterSecretTypeBreakglass, "1"))
150 require.NoError(t, mock.ExpectationsWereMet())
151 }
152
153 func mockUpdateClusterSecret(mock sqlmock.Sqlmock, version string, expirationTime string, result driver.Result) {
154 mock.ExpectExec(UpdateClusterSecretQuery).WithArgs(expirationTime, version, mockClusterSecretEdgeID, model.ClusterSecretTypeBreakglass).
155 WillReturnResult(result)
156 }
157
158 func TestFetchClusterSecretVersions(t *testing.T) {
159 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
160 require.NoError(t, err)
161 defer db.Close()
162 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
163 Username: testuser,
164 })
165 expirationTime := time.Now().UTC().AddDate(0, 0, 30).Format(time.RFC3339)
166 mockFetchClusterSecretVersions(mock, expirationTime, "5")
167
168 clusterSecretService := NewClusterSecretService(db, nil, config)
169 versionInfo, err := clusterSecretService.FetchClusterSecretVersions(ctx, mockClusterEdgeID, model.ClusterSecretTypeBreakglass)
170 require.NoError(t, err)
171 require.Equal(t, versionInfo[0].Version, "5")
172 require.Equal(t, versionInfo[0].ExpiresAt, expirationTime)
173 require.Equal(t, versionInfo[1].Version, "2")
174 require.Equal(t, versionInfo[1].ExpiresAt, "expires once latest version syncs to terminal(s) 78de6c9a-5d30-4860-bd01-be751c849d7e")
175 require.NoError(t, mock.ExpectationsWereMet())
176 }
177
178 func mockFetchClusterSecretVersions(mock sqlmock.Sqlmock, expirationTime string, version string) {
179 rows := mock.NewRows(fetchSecretVersionQueryType).AddRow(expirationTime, version)
180 mock.ExpectQuery(FetchSecretVersionInfoQuery).WithArgs(model.ClusterSecretTypeBreakglass, mockClusterEdgeID).WillReturnRows(rows)
181 rows = mock.NewRows([]string{"value"}).AddRow(`["os-user-store","2","78de6c9a-5d30-4860-bd01-be751c849d7e", "grub2store","1","78de6c9a-5d30-4860-bd01-be751c849d7e"]`)
182 mock.ExpectQuery(FetchLatestTerminalClusterSecretsQuery).WithArgs(mockClusterEdgeID).WillReturnRows(rows)
183 }
184
185 func TestFetchLatestTerminalClusterSecrets(t *testing.T) {
186 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
187 require.NoError(t, err)
188 defer db.Close()
189 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
190 Username: testuser,
191 })
192 mockFetchLatestTerminalClusterSecrets(mock)
193
194 clusterSecretService := NewClusterSecretService(db, nil, config)
195 terminalClusterSecrets, err := clusterSecretService.FetchLatestTerminalClusterSecrets(ctx, mockClusterEdgeID)
196 require.NoError(t, err)
197 require.Equal(t, 2, len(terminalClusterSecrets))
198 require.Equal(t, mockTerminalEdgeID, terminalClusterSecrets[0].TerminalEdgeID)
199 require.Equal(t, "os-user-store", terminalClusterSecrets[0].SecretType)
200 require.Equal(t, "2", terminalClusterSecrets[0].Version)
201 require.NoError(t, mock.ExpectationsWereMet())
202 }
203
204 func mockFetchLatestTerminalClusterSecrets(mock sqlmock.Sqlmock) {
205 rows := mock.NewRows([]string{"value"}).AddRow(`["os-user-store","2","78de6c9a-5d30-4860-bd01-be751c849d7e", "grub2store","2","78de6c9a-5d30-4860-bd01-be751c849d7e"]`)
206 mock.ExpectQuery(FetchLatestTerminalClusterSecretsQuery).WithArgs(mockClusterEdgeID).WillReturnRows(rows)
207 }
208
209 func TestExpireClusterSecret(t *testing.T) {
210 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
211 require.NoError(t, err)
212 defer db.Close()
213 ctx := middleware.NewContext(context.Background(), &types.AuthUser{
214 Username: testuser,
215 })
216 expirationTime := time.Now().Format(time.RFC3339)
217 mockExpireClusterSecret(mock, expirationTime, sqlmock.NewResult(2, 2))
218
219 clusterSecretService := NewClusterSecretService(db, nil, config)
220 require.NoError(t, clusterSecretService.ExpireClusterSecrets(ctx, mockLeaseID))
221 require.NoError(t, mock.ExpectationsWereMet())
222 }
223
224 func mockExpireClusterSecret(mock sqlmock.Sqlmock, expirationTime string, result driver.Result) {
225 mock.ExpectExec(ExpireClusterSecretsQuery).WithArgs(expirationTime, mockLeaseID).
226 WillReturnResult(result)
227 }
228
View as plain text