-
Notifications
You must be signed in to change notification settings - Fork 242
/
create.go
144 lines (124 loc) 路 3.22 KB
/
create.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
package token
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"path"
"github.com/google/uuid"
"github.com/urfave/cli"
"go.step.sm/cli-utils/errs"
"go.step.sm/cli-utils/ui"
)
func createCommand() cli.Command {
return cli.Command{
Name: "create",
Action: cli.ActionFunc(createAction),
Usage: "create a new token",
UsageText: `**step api token create** <team> <crt-file> <key-file>
[**--api-url**=<url>] [**--audience**=<name>]
`,
Flags: []cli.Flag{
apiURLFlag,
audienceFlag,
},
Description: `**step ca api token create** creates a new token for connecting to the Smallstep API.
## POSITIONAL ARGUMENTS
<team>
: UUID or slug of the team the API token will be issued for. This is available in the Smallstep dashboard.
<crt-file>
: File to read the certificate (PEM format). This certificate must be signed by a trusted root configured in the Smallstep dashboard.
<key-file>
: File to read the private key (PEM format).
## EXAMPLES
Use a certificate to get a new API token:
'''
$ step api token create ff98be70-7cc3-4df5-a5db-37f5d3c96e23 internal.crt internal.key
'''
Get a token using the team slug:
'''
$ step api token create teamfoo internal.crt internal.key
'''
`,
}
}
type createTokenReq struct {
TeamID string `json:"teamID"`
TeamSlug string `json:"teamSlug"`
Bundle [][]byte `json:"bundle"`
Audience string `json:"audience,omitempty"`
}
type createTokenResp struct {
Token string `json:"token"`
Message string `json:"message"`
}
func createAction(ctx *cli.Context) (err error) {
if err := errs.NumberOfArguments(ctx, 3); err != nil {
return err
}
args := ctx.Args()
teamID := args.Get(0)
crtFile := args.Get(1)
keyFile := args.Get(2)
parsedURL, err := url.Parse(ctx.String("api-url"))
if err != nil {
return err
}
parsedURL.Path = path.Join(parsedURL.Path, "api/auth")
apiURL := parsedURL.String()
clientCert, err := tls.LoadX509KeyPair(crtFile, keyFile)
if err != nil {
return err
}
b := &bytes.Buffer{}
r := &createTokenReq{
Bundle: clientCert.Certificate,
Audience: ctx.String("audience"),
}
if err := uuid.Validate(teamID); err != nil {
r.TeamSlug = teamID
} else {
r.TeamID = teamID
}
err = json.NewEncoder(b).Encode(r)
if err != nil {
return err
}
post, err := http.NewRequest("POST", apiURL, b)
if err != nil {
return err
}
post.Header.Set("Content-Type", "application/json")
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{
GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &clientCert, nil
},
MinVersion: tls.VersionTLS12,
}
client := http.Client{
Transport: transport,
}
resp, err := client.Do(post)
if err != nil {
return err
}
defer resp.Body.Close()
respBody := &createTokenResp{}
if err := json.NewDecoder(resp.Body).Decode(respBody); err != nil {
return err
}
if resp.StatusCode != 201 {
if respBody.Message != "" {
return errors.New(respBody.Message)
}
return fmt.Errorf("failed to create token: %d", resp.StatusCode)
}
// Print message to stderr for humans and token to stdout for scripts
ui.PrintSelected("Token successfully created", "")
fmt.Println(respBody.Token)
return nil
}