Merge pull request #7597 from n1lesh/n1lesh-remove-scope
Add remove/reset to auth refresh
This commit is contained in:
commit
c4e648a8ea
2 changed files with 179 additions and 13 deletions
|
|
@ -24,9 +24,11 @@ type RefreshOptions struct {
|
|||
|
||||
MainExecutable string
|
||||
|
||||
Hostname string
|
||||
Scopes []string
|
||||
AuthFlow func(*config.AuthConfig, *iostreams.IOStreams, string, []string, bool, bool) error
|
||||
Hostname string
|
||||
Scopes []string
|
||||
RemoveScopes []string
|
||||
ResetScopes bool
|
||||
AuthFlow func(*config.AuthConfig, *iostreams.IOStreams, string, []string, bool, bool) error
|
||||
|
||||
Interactive bool
|
||||
InsecureStorage bool
|
||||
|
|
@ -62,9 +64,12 @@ func NewCmdRefresh(f *cmdutil.Factory, runF func(*RefreshOptions) error) *cobra.
|
|||
your gh credentials to have. If no scopes are provided, the command
|
||||
maintains previously added scopes.
|
||||
|
||||
The command can only add additional scopes, but not remove previously
|
||||
added ones. To reset scopes to the default minimum set of scopes, you
|
||||
will need to create new credentials using the auth login command.
|
||||
The --remove-scope flag accepts a comma separated list of scopes you
|
||||
want to remove from your gh credentials. Scope removal is idempotent.
|
||||
The minimum set of scopes ("repo", "read:org" and "gist") cannot be removed.
|
||||
|
||||
The --reset-scopes flag resets the scopes for your gh credentials to
|
||||
the default set of scopes for your auth flow.
|
||||
`),
|
||||
Example: heredoc.Doc(`
|
||||
$ gh auth refresh --scopes write:org,read:public_key
|
||||
|
|
@ -72,6 +77,12 @@ func NewCmdRefresh(f *cmdutil.Factory, runF func(*RefreshOptions) error) *cobra.
|
|||
|
||||
$ gh auth refresh
|
||||
# => open a browser to ensure your authentication credentials have the correct minimum scopes
|
||||
|
||||
$ gh auth refresh --remove-scope delete_repo
|
||||
# => open a browser to idempotently remove the delete_repo scope
|
||||
|
||||
$ gh auth refresh --reset-scopes
|
||||
# => open a browser to re-authenticate with the default minimum scopes
|
||||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Interactive = opts.IO.CanPrompt()
|
||||
|
|
@ -90,6 +101,8 @@ func NewCmdRefresh(f *cmdutil.Factory, runF func(*RefreshOptions) error) *cobra.
|
|||
|
||||
cmd.Flags().StringVarP(&opts.Hostname, "hostname", "h", "", "The GitHub host to use for authentication")
|
||||
cmd.Flags().StringSliceVarP(&opts.Scopes, "scopes", "s", nil, "Additional authentication scopes for gh to have")
|
||||
cmd.Flags().StringSliceVarP(&opts.RemoveScopes, "remove-scope", "r", nil, "The authentication scope to remove from gh")
|
||||
cmd.Flags().BoolVarP(&opts.ResetScopes, "reset-scopes", "R", false, "Reset authentication scopes to the default minimum set of scopes")
|
||||
// secure storage became the default on 2023/4/04; this flag is left as a no-op for backwards compatibility
|
||||
var secureStorage bool
|
||||
cmd.Flags().BoolVar(&secureStorage, "secure-storage", false, "Save authentication credentials in secure credential store")
|
||||
|
|
@ -144,12 +157,14 @@ func refreshRun(opts *RefreshOptions) error {
|
|||
}
|
||||
|
||||
var additionalScopes []string
|
||||
if oldToken, _ := authCfg.Token(hostname); oldToken != "" {
|
||||
if oldScopes, err := shared.GetScopes(opts.HttpClient, hostname, oldToken); err == nil {
|
||||
for _, s := range strings.Split(oldScopes, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if s != "" {
|
||||
additionalScopes = append(additionalScopes, s)
|
||||
if !opts.ResetScopes {
|
||||
if oldToken, _ := authCfg.Token(hostname); oldToken != "" {
|
||||
if oldScopes, err := shared.GetScopes(opts.HttpClient, hostname, oldToken); err == nil {
|
||||
for _, s := range strings.Split(oldScopes, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if s != "" {
|
||||
additionalScopes = append(additionalScopes, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -168,7 +183,24 @@ func refreshRun(opts *RefreshOptions) error {
|
|||
additionalScopes = append(additionalScopes, credentialFlow.Scopes()...)
|
||||
}
|
||||
|
||||
if err := opts.AuthFlow(authCfg, opts.IO, hostname, append(opts.Scopes, additionalScopes...), opts.Interactive, !opts.InsecureStorage); err != nil {
|
||||
additionalScopes = append(opts.Scopes, additionalScopes...)
|
||||
if len(opts.RemoveScopes) > 0 {
|
||||
var scopes []string
|
||||
for _, scope := range additionalScopes {
|
||||
var match bool
|
||||
for _, rmScope := range opts.RemoveScopes {
|
||||
if rmScope == scope {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
scopes = append(scopes, scope)
|
||||
}
|
||||
}
|
||||
additionalScopes = scopes
|
||||
}
|
||||
if err := opts.AuthFlow(authCfg, opts.IO, hostname, additionalScopes, opts.Interactive, !opts.InsecureStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,46 @@ func Test_NewCmdRefresh(t *testing.T) {
|
|||
InsecureStorage: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reset scopes",
|
||||
tty: true,
|
||||
cli: "--reset-scopes",
|
||||
wants: RefreshOptions{
|
||||
ResetScopes: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reset scopes shorthand",
|
||||
tty: true,
|
||||
cli: "-R",
|
||||
wants: RefreshOptions{
|
||||
ResetScopes: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove scope",
|
||||
tty: true,
|
||||
cli: "--remove-scope read:public_key",
|
||||
wants: RefreshOptions{
|
||||
RemoveScopes: []string{"read:public_key"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove multiple scopes",
|
||||
tty: true,
|
||||
cli: "--remove-scope workflow,read:public_key",
|
||||
wants: RefreshOptions{
|
||||
RemoveScopes: []string{"workflow", "read:public_key"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove scope shorthand",
|
||||
tty: true,
|
||||
cli: "-r read:public_key",
|
||||
wants: RefreshOptions{
|
||||
RemoveScopes: []string{"read:public_key"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -280,6 +320,100 @@ func Test_refreshRun(t *testing.T) {
|
|||
scopes: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reset scopes",
|
||||
cfgHosts: []string{
|
||||
"github.com",
|
||||
},
|
||||
oldScopes: "delete_repo, codespace",
|
||||
opts: &RefreshOptions{
|
||||
Hostname: "github.com",
|
||||
ResetScopes: true,
|
||||
},
|
||||
wantAuthArgs: authArgs{
|
||||
hostname: "github.com",
|
||||
scopes: nil,
|
||||
secureStorage: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reset scopes and add some scopes",
|
||||
cfgHosts: []string{
|
||||
"github.com",
|
||||
},
|
||||
oldScopes: "repo:invite, delete_repo, codespace",
|
||||
opts: &RefreshOptions{
|
||||
Scopes: []string{"public_key:read", "workflow"},
|
||||
ResetScopes: true,
|
||||
},
|
||||
wantAuthArgs: authArgs{
|
||||
hostname: "github.com",
|
||||
scopes: []string{"public_key:read", "workflow"},
|
||||
secureStorage: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove scopes",
|
||||
cfgHosts: []string{
|
||||
"github.com",
|
||||
},
|
||||
oldScopes: "delete_repo, codespace, repo:invite, public_key:read",
|
||||
opts: &RefreshOptions{
|
||||
Hostname: "github.com",
|
||||
RemoveScopes: []string{"delete_repo", "repo:invite"},
|
||||
},
|
||||
wantAuthArgs: authArgs{
|
||||
hostname: "github.com",
|
||||
scopes: []string{"codespace", "public_key:read"},
|
||||
secureStorage: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove scope but no old scope",
|
||||
cfgHosts: []string{
|
||||
"github.com",
|
||||
},
|
||||
opts: &RefreshOptions{
|
||||
Hostname: "github.com",
|
||||
RemoveScopes: []string{"delete_repo"},
|
||||
},
|
||||
wantAuthArgs: authArgs{
|
||||
hostname: "github.com",
|
||||
scopes: nil,
|
||||
secureStorage: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove and add scopes at the same time",
|
||||
cfgHosts: []string{
|
||||
"github.com",
|
||||
},
|
||||
oldScopes: "repo:invite, delete_repo, codespace",
|
||||
opts: &RefreshOptions{
|
||||
Scopes: []string{"repo:invite", "public_key:read", "workflow"},
|
||||
RemoveScopes: []string{"codespace", "repo:invite", "workflow"},
|
||||
},
|
||||
wantAuthArgs: authArgs{
|
||||
hostname: "github.com",
|
||||
scopes: []string{"public_key:read", "delete_repo"},
|
||||
secureStorage: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove scopes that don't exist",
|
||||
cfgHosts: []string{
|
||||
"github.com",
|
||||
},
|
||||
oldScopes: "repo:invite, delete_repo, codespace",
|
||||
opts: &RefreshOptions{
|
||||
RemoveScopes: []string{"codespace", "repo:invite", "public_key:read"},
|
||||
},
|
||||
wantAuthArgs: authArgs{
|
||||
hostname: "github.com",
|
||||
scopes: []string{"delete_repo"},
|
||||
secureStorage: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue