Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added scope checking to API keys #187

Merged
merged 5 commits into from
Feb 22, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,33 @@ func readLocalToken(homeDir string, expectedScopes []string) (string, []string,
return token.AccessToken, currentScopes, nil
}

// Checkw whether or not a token has all of the required scopes. Returns a
dylanratcliffe marked this conversation as resolved.
Show resolved Hide resolved
// boolean and an error which will be populated if we couldn't read the token
func tokenHasAllScopes(token string, requiredScopes []string) (bool, error) {
claims, err := extractClaims(token)

if err != nil {
return false, fmt.Errorf("error extracting claims from token: %w", err)
}

// Check that the token has the right scopes
for _, scope := range requiredScopes {
if !claims.HasScope(scope) {
return false, nil
}
}

return true, nil
}

// ensureToken
// TODO: This needs to complain if the permissions aren't right when using an API key
func ensureToken(ctx context.Context, requiredScopes []string) (context.Context, error) {
// get a token from the api key if present
if viper.GetString("api-key") != "" {
log.WithContext(ctx).Debug("using provided token for authentication")
apiKey := viper.GetString("api-key")
var accessToken string
if strings.HasPrefix(apiKey, "ovm_api_") {
// exchange api token for JWT
client := UnauthenticatedApiKeyClient(ctx)
Expand All @@ -164,11 +185,22 @@ func ensureToken(ctx context.Context, requiredScopes []string) (context.Context,
return ctx, fmt.Errorf("error authenticating the API token: %w", err)
}
log.WithContext(ctx).Debug("successfully authenticated")
apiKey = resp.Msg.GetAccessToken()
accessToken = resp.Msg.GetAccessToken()
} else {
return ctx, errors.New("OVM_API_KEY does not match pattern 'ovm_api_*'")
}
return context.WithValue(ctx, sdp.UserTokenContextKey{}, apiKey), nil

// Validate that the API_KEY has the required scopes
dylanratcliffe marked this conversation as resolved.
Show resolved Hide resolved
ok, err := tokenHasAllScopes(accessToken, requiredScopes)

if err != nil {
return ctx, fmt.Errorf("error checking token scopes in API KEY token: %w", err)
} else if !ok {
return ctx, fmt.Errorf("authenticated successfully, but your API key doesn't have the required permissions: %v", requiredScopes)
} else {
// In this case the token is good
return context.WithValue(ctx, sdp.UserTokenContextKey{}, accessToken), nil
}
}

var localScopes []string
Expand Down Expand Up @@ -302,16 +334,16 @@ func getChangeUuid(ctx context.Context, expectedStatus sdp.ChangeStatus, errNotF
// Finally look through all open changes to find one with a matching ticket link
client := AuthenticatedChangesClient(ctx)

var maybeChangeUuid *uuid.UUID
changesList, err := client.ListChangesByStatus(ctx, &connect.Request[sdp.ListChangesByStatusRequest]{
Msg: &sdp.ListChangesByStatusRequest{
Status: expectedStatus,
},
})
if err != nil {
return uuid.Nil, fmt.Errorf("failed to searching for existing changes: %w", err)
return uuid.Nil, fmt.Errorf("failed to search for existing changes: %w", err)
}

var maybeChangeUuid *uuid.UUID
for _, c := range changesList.Msg.GetChanges() {
if c.GetProperties().GetTicketLink() == ticketLink {
maybeChangeUuid = c.GetMetadata().GetUUIDParsed()
Expand Down
Loading