forked from mdklein/candid
-
Notifications
You must be signed in to change notification settings - Fork 0
/
create-agent.go
173 lines (157 loc) · 4.69 KB
/
create-agent.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright 2017 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package admincmd
import (
"encoding/json"
"fmt"
"os"
"github.com/juju/cmd"
"github.com/juju/gnuflag"
"golang.org/x/net/context"
"gopkg.in/CanonicalLtd/candidclient.v1/params"
"gopkg.in/errgo.v1"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon-bakery.v2/httpbakery/agent"
"github.com/CanonicalLtd/candid/internal/auth"
)
type createAgentCommand struct {
*candidCommand
groups []string
agentFile string
agentFullName string
admin bool
publicKey *bakery.PublicKey
}
func newCreateAgentCommand(c *candidCommand) cmd.Command {
return &createAgentCommand{
candidCommand: c,
}
}
var createAgentDoc = `
The create-agent command creates an agent user on the Candid
server.
An agent user has an associated public key - the private key pair can
be used to authenticate as that agent.
The name of the agent is chosen by the identity manager itself
and is written to the agent file, except as a special case, if the
--admin flag is specified, when the agent information will only
be written locally (this is so that an admin agent file can be generated
before bootstrapping the Candid server for the first time).
The agent will be made a member of any of the specified groups as long
as the currently authenticated user is a member of those groups.
A new key will be generated unless a key is specified with the -k
flag or a key is found in the agent file (see below).
If the --agent-file flag is specified, the specified file will be updated with
the new agent information, otherwise the new agent information will be
printed to the standard output. Note when the -k flag is specified,
this information will be missing the private key.
`
func (c *createAgentCommand) Info() *cmd.Info {
return &cmd.Info{
Name: "create-agent",
Args: "[group...]",
Purpose: "create or update an agent user",
Doc: createAgentDoc,
}
}
func (c *createAgentCommand) SetFlags(f *gnuflag.FlagSet) {
c.candidCommand.SetFlags(f)
publicKeyVar(f, &c.publicKey, "k", "public key of agent")
publicKeyVar(f, &c.publicKey, "public-key", "")
f.StringVar(&c.agentFile, "f", "", "agent file to update")
f.StringVar(&c.agentFile, "agent-file", "", "")
f.BoolVar(&c.admin, "admin", false, "generate an agent file for the admin user; does not contact the identity manager service")
f.StringVar(&c.agentFullName, "name", "", "name of agent")
}
func (c *createAgentCommand) Init(args []string) error {
c.groups = args
if c.agentFile != "" && c.publicKey != nil {
return errgo.Newf("cannot specify public key and an agent file")
}
return errgo.Mask(c.candidCommand.Init(nil))
}
func (c *createAgentCommand) Run(cmdctx *cmd.Context) error {
defer c.Close(cmdctx)
ctx := context.Background()
client, err := c.Client(cmdctx)
if err != nil {
return errgo.Mask(err)
}
var key *bakery.KeyPair
var agents *agent.AuthInfo
if c.agentFile != "" {
agents, err = readAgentFile(cmdctx.AbsPath(c.agentFile))
if err != nil {
if !os.IsNotExist(errgo.Cause(err)) {
return errgo.Mask(err)
}
agents = new(agent.AuthInfo)
} else {
key = agents.Key
}
}
switch {
case key == nil && c.publicKey == nil:
key1, err := bakery.GenerateKey()
if err != nil {
return errgo.Notef(err, "cannot generate key")
}
key = key1
c.publicKey = &key.Public
case c.publicKey == nil:
c.publicKey = &key.Public
}
var username params.Username
if c.admin {
username = auth.AdminUsername
if len(c.groups) > 0 {
return errgo.Newf("cannot specify groups when using --admin flag")
}
} else {
resp, err := client.CreateAgent(ctx, ¶ms.CreateAgentRequest{
CreateAgentBody: params.CreateAgentBody{
FullName: c.agentFullName,
Groups: c.groups,
PublicKeys: []*bakery.PublicKey{c.publicKey},
},
})
if err != nil {
return errgo.Mask(err)
}
username = resp.Username
}
if agents != nil {
if agents.Key == nil {
agents.Key = key
}
agents.Agents = append(agents.Agents, agent.Agent{
URL: client.Client.BaseURL,
Username: string(username),
})
if err := writeAgentFile(cmdctx.AbsPath(c.agentFile), agents); err != nil {
return errgo.Mask(err)
}
fmt.Fprintf(cmdctx.Stdout, "added agent %s for %s to %s\n", username, client.Client.BaseURL, c.agentFile)
return nil
}
agentsData := &agent.AuthInfo{
Agents: []agent.Agent{{
URL: client.Client.BaseURL,
Username: string(username),
}},
}
if key != nil {
agentsData.Key = key
} else {
agentsData.Key = &bakery.KeyPair{
Public: *c.publicKey,
}
}
data, err := json.MarshalIndent(agentsData, "", "\t")
if err != nil {
return errgo.Mask(err)
}
data = append(data, '\n')
cmdctx.Stdout.Write(data)
return nil
}