1 package clustersecrets
2
3 import (
4 "context"
5 "database/sql"
6 "errors"
7 "time"
8
9 "github.com/google/uuid"
10
11 sqlerr "edge-infra.dev/pkg/edge/api/apierror/sql"
12 "edge-infra.dev/pkg/edge/api/graph/model"
13 "edge-infra.dev/pkg/edge/api/middleware"
14 )
15
16 var (
17
18 ErrLeaseAlreadyInUse = errors.New("lease is already owned by another user")
19
20
21 ErrUserDoesNotOwnLease = errors.New("user does not own the lease")
22
23 ErrCannotRevokeUser = errors.New("cannot revoke user from lease as they do not own it")
24
25 ErrFailedToUpdateDatabase = errors.New("failed to update database")
26 )
27
28
29 func (s *clusterSecretService) ObtainLease(ctx context.Context, clusterEdgeID string) (bool, error) {
30 lease, err := s.FetchLease(ctx, clusterEdgeID)
31 if err != nil {
32 return false, err
33 }
34 currentLeaseExpirationTime, err := time.Parse(time.RFC3339, lease.ExpiresAt)
35 if err != nil {
36 return false, err
37 }
38 user := middleware.ForContext(ctx)
39 currentTime := time.Now().UTC()
40 if currentLeaseExpirationTime.After(currentTime) {
41 if lease.Owner == user.Username {
42
43 return false, nil
44 }
45 return false, ErrLeaseAlreadyInUse
46 }
47
48 maxLeaseValidityPeriod, err := time.ParseDuration(s.Config.EdgeMaxLeaseValidityPeriod)
49 if err != nil {
50 return false, err
51 }
52 newExpirationTime := currentTime.Add(maxLeaseValidityPeriod).Format(time.RFC3339)
53 result, err := s.SQLDB.ExecContext(ctx, ObtainLeaseQuery, newExpirationTime, user.Username, currentTime.Format(time.RFC3339), clusterEdgeID)
54 if err != nil {
55 return false, err
56 }
57 rows, err := result.RowsAffected()
58 if err != nil {
59 return false, err
60 }
61 if rows == 0 {
62 return false, ErrFailedToUpdateDatabase
63 }
64 if lease.Owner != "" {
65 return true, nil
66 }
67 return false, nil
68 }
69
70
71 func (s *clusterSecretService) ReleaseLease(ctx context.Context, clusterEdgeID string) error {
72 user := middleware.ForContext(ctx)
73 currentTime := time.Now().UTC().Format(time.RFC3339)
74 result, err := s.SQLDB.ExecContext(ctx, ExpireLeaseQuery, currentTime, currentTime, clusterEdgeID, user.Username)
75 if err != nil {
76 return err
77 }
78 rows, err := result.RowsAffected()
79 if err != nil {
80 return err
81 }
82 if rows == 0 {
83 return ErrUserDoesNotOwnLease
84 }
85 return nil
86 }
87
88
89 func (s *clusterSecretService) RevokeLease(ctx context.Context, clusterEdgeID string, username string) error {
90 currentTime := time.Now().UTC().Format(time.RFC3339)
91 result, err := s.SQLDB.ExecContext(ctx, ExpireLeaseQuery, currentTime, currentTime, clusterEdgeID, username)
92 if err != nil {
93 return err
94 }
95 rows, err := result.RowsAffected()
96 if err != nil {
97 return err
98 }
99 if rows == 0 {
100 return ErrCannotRevokeUser
101 }
102 return nil
103 }
104
105
106 func (s *clusterSecretService) RemoveUserFromLease(ctx context.Context, clusterSecretLeaseEdgeID string) error {
107 currentTime := time.Now().UTC().Format(time.RFC3339)
108 result, err := s.SQLDB.ExecContext(ctx, RemoveUserFromLeaseQuery, currentTime, clusterSecretLeaseEdgeID)
109 if err != nil {
110 return err
111 }
112 rows, err := result.RowsAffected()
113 if err != nil {
114 return err
115 }
116 if rows == 0 {
117 return ErrFailedToUpdateDatabase
118 }
119 return nil
120 }
121
122
123 func (s *clusterSecretService) FetchLease(ctx context.Context, clusterEdgeID string) (model.ClusterSecretLease, error) {
124 secretLease := model.ClusterSecretLease{}
125 row := s.SQLDB.QueryRowContext(ctx, FetchLeaseQuery, clusterEdgeID)
126 var leaseID, expiration, owner string
127 if err := row.Scan(&leaseID, &expiration, &owner); err != nil {
128 return secretLease, err
129 }
130 var secretTypes []string
131 rows, err := s.SQLDB.QueryContext(ctx, FetchLeaseSecretTypesQuery, clusterEdgeID, leaseID)
132 if err != nil {
133 return secretLease, err
134 }
135 defer rows.Close()
136 for rows.Next() {
137 var secretType string
138 if err := rows.Scan(&secretType); err != nil {
139 return secretLease, err
140 }
141 secretTypes = append(secretTypes, secretType)
142 }
143 if err := rows.Err(); err != nil {
144 return secretLease, sqlerr.Wrap(err)
145 }
146
147 return model.ClusterSecretLease{
148 ExpiresAt: expiration,
149 Owner: owner,
150 SecretTypes: secretTypes,
151 }, nil
152 }
153
154
155 func (s *clusterSecretService) CreateLease(ctx context.Context, clusterEdgeID string) (string, error) {
156 leaseID := uuid.NewString()
157 currentTime := time.Now().UTC().Format(time.RFC3339)
158 result, err := s.SQLDB.ExecContext(ctx, CreateClusterSecretLeaseQuery, leaseID, clusterEdgeID, currentTime, currentTime, currentTime)
159 if err != nil {
160 return "", err
161 }
162 rows, err := result.RowsAffected()
163 if err != nil {
164 return "", err
165 }
166 if rows == 0 {
167 return "", ErrFailedToUpdateDatabase
168 }
169 return leaseID, nil
170 }
171
172
173 func (s *clusterSecretService) FetchLeaseID(ctx context.Context, clusterEdgeID string) (string, error) {
174 row := s.SQLDB.QueryRowContext(ctx, FetchLeaseIDQuery, clusterEdgeID)
175 var leaseID string
176 if err := row.Scan(&leaseID); err != nil {
177 return "", err
178 }
179 return leaseID, nil
180 }
181
182
183 func (s *clusterSecretService) VerifyLeaseExists(ctx context.Context, clusterEdgeID string) (string, error) {
184 var leaseID string
185 _, err := s.FetchLease(ctx, clusterEdgeID)
186 if errors.Is(err, sql.ErrNoRows) {
187 leaseID, err = s.CreateLease(ctx, clusterEdgeID)
188 if err != nil {
189 return "", err
190 }
191 } else if err != nil {
192 return "", err
193 }
194 return leaseID, nil
195 }
196
View as plain text