@@ -307,14 +307,48 @@ func ensureToken(ctx context.Context, requiredScopes []string) (context.Context,
307307 return ctx , fmt .Errorf ("error extracting claims from token: %w" , err )
308308 }
309309
310+ ok , missing := HasScopesFlexible (claims , requiredScopes )
311+
312+ if ! ok {
313+ return ctx , fmt .Errorf ("authenticated successfully, but you don't have the required permission: '%v'" , missing )
314+ }
315+
316+ // Add the token to the context
317+ return context .WithValue (ctx , sdp.UserTokenContextKey {}, accessToken ), nil
318+ }
319+
320+ // Returns whether a set of claims has all of the required scopes. It also
321+ // accounts for when a user has write access but required read access, they
322+ // aren't the same but the user will have access anyway so this will pass
323+ //
324+ // Returns a bool and the missing permission as a string of any
325+ func HasScopesFlexible (claims * sdp.CustomClaims , requiredScopes []string ) (bool , string ) {
326+ if claims == nil {
327+ return false , ""
328+ }
329+
310330 for _ , scope := range requiredScopes {
311331 if ! claims .HasScope (scope ) {
312- return ctx , fmt .Errorf ("authenticated successfully, but you don't have the required permission: '%v'" , scope )
332+ // If they don't have the *exact* scope, check to see if they have
333+ // write access to the same service
334+ sections := strings .Split (scope , ":" )
335+ var hasWriteInstead bool
336+
337+ if len (sections ) == 2 {
338+ service , action := sections [0 ], sections [1 ]
339+
340+ if action == "read" {
341+ hasWriteInstead = claims .HasScope (fmt .Sprintf ("%v:write" , service ))
342+ }
343+ }
344+
345+ if ! hasWriteInstead {
346+ return false , scope
347+ }
313348 }
314349 }
315350
316- // Add the token to the context
317- return context .WithValue (ctx , sdp.UserTokenContextKey {}, accessToken ), nil
351+ return true , ""
318352}
319353
320354// getChangeUuid returns the UUID of a change, as selected by --uuid or --change, or a state with the specified status and having --ticket-link
0 commit comments