forked from juju/juju
/
remove.go
156 lines (131 loc) · 4.08 KB
/
remove.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright 2012-2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package user
import (
"bufio"
"fmt"
"io"
"strings"
"github.com/juju/cmd/v3"
"github.com/juju/errors"
"github.com/juju/gnuflag"
"github.com/DavinZhang/juju/apiserver/params"
jujucmd "github.com/DavinZhang/juju/cmd"
"github.com/DavinZhang/juju/cmd/juju/block"
"github.com/DavinZhang/juju/cmd/modelcmd"
)
var removeUsageSummary = `
Deletes a Juju user from a controller.`[1:]
// TODO(redir): Get updated copy for add-user as that may need updates, too.
var removeUsageDetails = `
This removes a user permanently.
By default, the controller is the current controller.
Examples:
juju remove-user bob
juju remove-user bob --yes
See also:
unregister
revoke
show-user
list-users
switch-user
disable-user
enable-user
change-user-password`[1:]
var removeUserMsg = `
WARNING! This command will permanently archive the user %q on the %q
controller.
This action is irreversible. If you wish to temporarily disable the
user please use the`[1:] + " `juju disable-user` " + `command. See
` + " `juju help disable-user` " + `for more details.
Continue (y/N)? `
// RemoveUserAPI defines the usermanager API methods that the remove command
// uses.
type RemoveUserAPI interface {
RemoveUser(username string) error
Close() error
}
// NewRemoveCommand constructs a wrapped unexported removeCommand.
func NewRemoveCommand() cmd.Command {
return modelcmd.WrapController(&removeCommand{})
}
// removeCommand deletes a user from a Juju controller.
type removeCommand struct {
modelcmd.ControllerCommandBase
api RemoveUserAPI
UserName string
ConfirmDelete bool
}
// SetFlags adds command specific flags and then returns the flagset.
func (c *removeCommand) SetFlags(f *gnuflag.FlagSet) {
c.ControllerCommandBase.SetFlags(f)
f.BoolVar(&c.ConfirmDelete, "y", false, "Confirm deletion of the user")
f.BoolVar(&c.ConfirmDelete, "yes", false, "")
}
// Info implements Command.Info.
func (c *removeCommand) Info() *cmd.Info {
return jujucmd.Info(&cmd.Info{
Name: "remove-user",
Args: "<user name>",
Purpose: removeUsageSummary,
Doc: removeUsageDetails,
})
}
// Init implements Command.Init.
func (c *removeCommand) Init(args []string) error {
if len(args) == 0 {
return errors.Errorf("no username supplied")
}
c.UserName = args[0]
return cmd.CheckEmpty(args[1:])
}
// Run implements Command.Run.
func (c *removeCommand) Run(ctx *cmd.Context) error {
controllerName, err := c.ControllerName()
if err != nil {
return errors.Trace(err)
}
api := c.api // This is for testing.
if api == nil { // The real McCoy.
var err error
api, err = c.NewUserManagerAPIClient()
if err != nil {
return errors.Trace(err)
}
defer api.Close()
}
// Confirm deletion if the user didn't specify -y/--yes in the command.
if !c.ConfirmDelete {
if err := confirmDelete(ctx, controllerName, c.UserName); err != nil {
return errors.Trace(err)
}
}
if err := api.RemoveUser(c.UserName); err != nil {
// This is very awful, but it makes the user experience crisper. At
// least maybe more tenable until users and authn/z are overhauled.
if e, ok := err.(*params.Error); ok {
if e.Message == fmt.Sprintf("failed to delete user %q: user %q is permanently deleted", c.UserName, c.UserName) {
e.Message = fmt.Sprintf("failed to delete user %q: the user has already been permanently deleted", c.UserName)
err = e
}
}
return block.ProcessBlockedError(err, block.BlockChange)
}
fmt.Fprintf(ctx.Stdout, "User %q removed\n", c.UserName)
return nil
}
func confirmDelete(ctx *cmd.Context, controller, username string) error {
// Get confirmation from the user that they want to continue
fmt.Fprintf(ctx.Stdout, removeUserMsg, username, controller)
scanner := bufio.NewScanner(ctx.Stdin)
scanner.Scan()
err := scanner.Err()
if err != nil && err != io.EOF {
return errors.Annotate(err, "user deletion aborted")
}
answer := strings.ToLower(scanner.Text())
if answer != "y" && answer != "yes" {
return errors.New("user deletion aborted")
}
return nil
}