Skip to content

Commit 810e867

Browse files
authored
Merge pull request #3 from cloudfoundry-community/main
Performance and auth improvements
2 parents b0a7655 + 064a24f commit 810e867

File tree

8 files changed

+269
-61
lines changed

8 files changed

+269
-61
lines changed

cf/auth_service.go

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cf
22

33
import (
4+
"encoding/base64"
5+
"encoding/json"
46
"fmt"
57
"net/url"
68
"os"
@@ -11,66 +13,105 @@ import (
1113
"golang.org/x/oauth2"
1214
)
1315

16+
type Logger interface {
17+
Info(tag, message string)
18+
Error(tag, message string)
19+
}
20+
1421
type AuthService struct {
1522
client *cf.Client
23+
logger Logger
1624
}
1725

18-
func NewAuthService(client *cf.Client) *AuthService {
26+
func NewAuthService(client *cf.Client, logger Logger) *AuthService {
1927
return &AuthService{
2028
client: client,
29+
logger: logger,
2130
}
2231
}
2332

2433
func (service *AuthService) Verify(auth string) error {
34+
tag := "AuthService.Verify"
35+
service.logger.Info(tag, "Starting verification process")
2536
username, err := getUsername(auth)
2637
if err != nil {
38+
service.logger.Error(tag, fmt.Sprintf("Error getting username: %v", err))
2739
return err
2840
}
41+
service.logger.Info(tag, fmt.Sprintf("Username obtained: %s", username))
2942

3043
user, err := service.getUser(username)
3144
if err != nil {
45+
service.logger.Error(tag, fmt.Sprintf("Error getting user: %v", err))
3246
return err
3347
}
48+
// Debugging = noisy
49+
// service.logger.Info(tag, fmt.Sprintf("User obtained: %v", user))
3450

3551
roles, err := service.getUserRoles(user)
3652
if err != nil {
53+
service.logger.Error(tag, fmt.Sprintf("Error getting user roles: %v", err))
3754
return err
3855
}
56+
// Debugging = noisy
57+
// service.logger.Info(tag, fmt.Sprintf("User roles obtained: %v", roles))
58+
59+
tokenScopes, err := getTokenScopes(auth, service.logger)
60+
if err != nil {
61+
service.logger.Error(tag, fmt.Sprintf("Error getting token scopes: %v", err))
62+
return err
63+
}
64+
service.logger.Info(tag, fmt.Sprintf("Token scopes obtained: %v", tokenScopes))
3965

4066
// Check all the roles, but return good early if we find one that works.
67+
68+
// Check token scopes for cloud_controller.admin
69+
for _, scope := range tokenScopes {
70+
if scope == "cloud_controller.admin" {
71+
service.logger.Info(tag, "User has cloud_controller.admin scope")
72+
return nil
73+
}
74+
}
75+
76+
// Check CF roles for space_manager or space_developer
4177
for _, role := range roles {
42-
// NOTE: we should definitely be checking space IDs, too, but that's tomorrow
43-
// guy's problem.
4478
if role.Type == "space_manager" || role.Type == "space_developer" {
79+
service.logger.Info(tag, fmt.Sprintf("User has role: %s", role.Type))
4580
return nil
4681
}
4782
}
4883

84+
service.logger.Error(tag, "User does not have sufficient permissions")
4985
return fmt.Errorf("insufficient permissions")
5086
}
5187

5288
func (service *AuthService) getUser(username string) (cf.User, error) {
89+
tag := "AuthService.getUser"
5390
query := url.Values{}
5491
query.Add("username", username)
5592

5693
users, err := service.client.ListUsersByQuery(query)
5794
if err != nil {
95+
service.logger.Error(tag, fmt.Sprintf("Error listing users by query: %v", err))
5896
return cf.User{}, err
5997
}
6098

6199
user := users.GetUserByUsername(username)
62100
if len(user.Guid) == 0 {
101+
service.logger.Error(tag, "No such user found")
63102
return cf.User{}, fmt.Errorf("no such user")
64103
}
65104

66105
return user, nil
67106
}
68107

69108
func (service *AuthService) getUserRoles(user cf.User) ([]cf.V3Role, error) {
109+
tag := "AuthService.getUserRoles"
70110
roleQuery := url.Values{}
71111
roleQuery.Add("user_guids", user.Guid)
72112
roles, err := service.client.ListV3RolesByQuery(roleQuery)
73113
if err != nil {
114+
service.logger.Error(tag, fmt.Sprintf("Error listing V3 roles by query: %v", err))
74115
return nil, err
75116
}
76117

@@ -125,3 +166,48 @@ func getBearer(auth string) (string, error) {
125166

126167
return parts[bearerLoc+1], nil
127168
}
169+
170+
// JWTClaims represents the claims in the JWT token
171+
type JWTClaims struct {
172+
Scope []string `json:"scope"`
173+
}
174+
175+
// DecodeJWT decodes the JWT token and extracts the claims
176+
func DecodeJWT(token string) (*JWTClaims, error) {
177+
parts := strings.Split(token, ".")
178+
if len(parts) < 3 {
179+
return nil, fmt.Errorf("invalid token format")
180+
}
181+
182+
payload := parts[1]
183+
payloadDecoded, err := base64.RawURLEncoding.DecodeString(payload)
184+
if err != nil {
185+
return nil, fmt.Errorf("failed to decode payload: %v", err)
186+
}
187+
188+
var claims JWTClaims
189+
err = json.Unmarshal(payloadDecoded, &claims)
190+
if err != nil {
191+
return nil, fmt.Errorf("failed to unmarshal claims: %v", err)
192+
}
193+
194+
return &claims, nil
195+
}
196+
197+
func getTokenScopes(auth string, logger Logger) ([]string, error) {
198+
tag := "AuthService.getTokenScopes"
199+
bearer, err := getBearer(auth)
200+
if err != nil {
201+
logger.Error(tag, fmt.Sprintf("Error getting bearer token: %v", err))
202+
return nil, err
203+
}
204+
205+
claims, err := DecodeJWT(bearer)
206+
if err != nil {
207+
logger.Error(tag, fmt.Sprintf("Error decoding JWT token: %v", err))
208+
return nil, err
209+
}
210+
211+
logger.Info(tag, fmt.Sprintf("Scopes found in token: %v", claims.Scope))
212+
return claims.Scope, nil
213+
}

cmd/scheduler/main.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func main() {
102102

103103
log.Info(tag, "got the cf client set up")
104104

105-
auth := cf.NewAuthService(cfclient)
105+
auth := cf.NewAuthService(cfclient, log)
106106
jobs := postgres.NewJobService(db)
107107
calls := postgres.NewCallService(db)
108108
info := cf.NewInfoService(cfclient)
@@ -168,6 +168,38 @@ func main() {
168168
}
169169
}
170170

171+
// Schedule cleanup tasks
172+
retentionPeriod := 5 * 30 * 24 * time.Hour // 30 days
173+
ticker := time.NewTicker(24 * time.Hour) // run cleanup every day
174+
quit := make(chan struct{})
175+
signalChan := make(chan os.Signal, 1) // New channel for OS signals
176+
177+
go func() {
178+
for {
179+
select {
180+
case <-ticker.C:
181+
log.Info(tag, "Starting cleanup of old executions")
182+
183+
deletedExecutions, err := executions.CleanupOldExecutions(retentionPeriod)
184+
if err != nil {
185+
log.Error(tag, fmt.Sprintf("Error cleaning up old executions: %s", err.Error()))
186+
} else {
187+
if len(deletedExecutions) == 0 {
188+
log.Info(tag, "No executions were deleted.")
189+
} else {
190+
for _, execution := range deletedExecutions {
191+
log.Info(tag, fmt.Sprintf("Deleted execution: %s, Last Updated: %s", execution["guid"], execution["execution_end_time"]))
192+
}
193+
}
194+
}
195+
196+
case <-quit:
197+
ticker.Stop()
198+
return
199+
}
200+
}
201+
}()
202+
171203
server := http.Server(fmt.Sprintf("0.0.0.0:%d", port), services)
172204

173205
go func() {
@@ -178,10 +210,12 @@ func main() {
178210

179211
log.Info(tag, fmt.Sprintf("listening for connections on %s", server.Addr))
180212

181-
quit := make(chan os.Signal)
182-
signal.Notify(quit, os.Interrupt)
213+
signal.Notify(signalChan, os.Interrupt) // Use signalChan for signal notification
214+
215+
<-signalChan // Wait for signal
183216

184-
<-quit
217+
// Stop the cleanup ticker
218+
close(quit)
185219

186220
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
187221
defer cancel()

core/jobs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type JobService interface {
3737
Delete(*Job) error
3838
Named(string) (*Job, error)
3939
Persist(*Job) (*Job, error)
40-
InSpace(string) []*Job
40+
InSpace(string) ([]*Job, error)
4141
Success(*Job) (*Job, error)
4242
Fail(*Job) (*Job, error)
4343
}

cron/cron_service.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ type CronService struct {
1616

1717
func NewCronService(log core.LogService) *CronService {
1818
return &CronService{
19-
cron.New(),
20-
log,
21-
make(map[string]cron.EntryID)}
19+
Cron: cron.New(),
20+
log: log,
21+
mapping: make(map[string]cron.EntryID),
22+
}
2223
}
2324

2425
func (service *CronService) Add(runnable core.Runnable) error {
@@ -60,6 +61,7 @@ func (service *CronService) Add(runnable core.Runnable) error {
6061
}
6162

6263
service.mapping[schedule.GUID] = id
64+
service.logMappingSize("Added job to cron service")
6365

6466
return nil
6567
}
@@ -71,6 +73,8 @@ func (service *CronService) Delete(runnable core.Runnable) error {
7173
}
7274

7375
service.Remove(id)
76+
delete(service.mapping, runnable.Schedule().GUID)
77+
service.logMappingSize("Deleted job from cron service")
7478

7579
return nil
7680
}
@@ -84,3 +88,12 @@ func (service *CronService) Validate(expression string) error {
8488

8589
return err
8690
}
91+
92+
func (service *CronService) MappingSize() int {
93+
return len(service.mapping)
94+
}
95+
96+
func (service *CronService) logMappingSize(action string) {
97+
size := service.MappingSize()
98+
service.log.Info("cron-service", fmt.Sprintf("%s: current mapping size is %d", action, size))
99+
}

0 commit comments

Comments
 (0)