File: teams.go

package info (click to toggle)
golang-github-smallstep-cli 0.15.16%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 4,404 kB
  • sloc: sh: 512; makefile: 99
file content (111 lines) | stat: -rw-r--r-- 2,606 bytes parent folder | download | duplicates (2)
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
package cautils

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/smallstep/certificates/pki"
	"github.com/smallstep/cli/exec"
	"github.com/urfave/cli"

	"github.com/pkg/errors"
)

type bootstrapAPIResponse struct {
	CaURL       string `json:"url"`
	Fingerprint string `json:"fingerprint"`
	RedirectURL string `json:"redirect-url"`
}

// BootstrapTeam does a request to api.smallstep.com to bootstrap the
// configuration of the given team ID (slug).
func BootstrapTeam(ctx *cli.Context, teamID string) error {
	apiEndpoint := ctx.String("team-url")
	if apiEndpoint == "" {
		// Use the default endpoint..
		u := url.URL{
			Scheme: "https",
			Host:   "api.smallstep.com",
			Path:   "/v1/teams/" + teamID + "/authorities/ssh",
		}
		apiEndpoint = u.String()
	} else {
		// The user specified a custom endpoint..
		apiEndpoint = strings.ReplaceAll(apiEndpoint, "<>", teamID)
		u, err := url.Parse(apiEndpoint)
		if err != nil {
			return errors.Wrapf(err, "error parsing %s", apiEndpoint)
		}
		apiEndpoint = u.String()
	}

	// Using public PKI
	resp, err := http.Get(apiEndpoint)
	if err != nil {
		return errors.Wrap(err, "error getting team data")
	}
	if resp.StatusCode >= 400 {
		if resp.StatusCode == http.StatusNotFound {
			return errors.New("error getting team data: team not found")
		}
		return errors.Wrap(readError(resp.Body), "error getting team data")
	}

	var r bootstrapAPIResponse
	if err := readJSON(resp.Body, &r); err != nil {
		return errors.Wrap(err, "error getting team data")
	}

	if r.RedirectURL == "" {
		r.RedirectURL = "https://smallstep.com/app/teams/sso/success"
	}

	args := []string{"ca", "bootstrap",
		"--ca-url", r.CaURL,
		"--fingerprint", r.Fingerprint,
		"--redirect-url", r.RedirectURL,
	}
	if ctx.Bool("force") {
		args = append(args, "--force")
	}
	if _, err := exec.Step(args...); err != nil {
		return errors.Wrap(err, "error getting team data")
	}

	// Set ca-url and root certificate
	ctx.Set("ca-url", r.CaURL)
	ctx.Set("fingerprint", r.Fingerprint)
	ctx.Set("root", pki.GetRootCAPath())

	return nil
}

type apiError struct {
	StatusCode int    `json:"statusCode"`
	Err        string `json:"error"`
	Message    string `json:"message"`
}

func (e *apiError) Error() string {
	return e.Message
}

func readJSON(r io.ReadCloser, v interface{}) error {
	defer r.Close()
	if err := json.NewDecoder(r).Decode(v); err != nil {
		return err
	}
	return nil
}

func readError(r io.ReadCloser) error {
	defer r.Close()
	apiErr := new(apiError)
	if err := json.NewDecoder(r).Decode(apiErr); err != nil {
		return err
	}
	return apiErr
}