-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathui.go
200 lines (178 loc) · 5.83 KB
/
ui.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright 2015 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
// Package ui embeds the assets for the web UI into the Cockroach binary.
//
// By default, it serves a stub web UI. Linking with distoss or distccl will
// replace the stubs with the OSS UI or the CCL UI, respectively. The exported
// symbols in this package are thus function pointers instead of functions so
// that they can be mutated by init hooks.
package ui
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/fs"
"net/http"
"regexp"
"github.com/cockroachdb/cockroach/pkg/base"
"github.com/cockroachdb/cockroach/pkg/build"
"github.com/cockroachdb/cockroach/pkg/server/serverpb"
"github.com/cockroachdb/cockroach/pkg/settings"
"github.com/cockroachdb/cockroach/pkg/util/httputil"
"github.com/cockroachdb/cockroach/pkg/util/log"
)
const (
utc int64 = iota
americaNewYork
)
var _ = settings.RegisterEnumSetting(
settings.SystemOnly,
"ui.display_timezone",
"the timezone used to format timestamps in the ui",
"UTC",
map[int64]string{
utc: "UTC",
americaNewYork: "America/New_York",
},
)
// Assets is used for embedded JS assets required for UI.
// In case the binary is built without UI, it provides single index.html file with
// the same content as indexHTML as a fallback.
var Assets fs.FS
// HaveUI tells whether the admin UI has been linked into the binary.
var HaveUI = false
// indexTemplate takes arguments about the current session and returns HTML
// which includes the UI JavaScript bundles, plus a script tag which sets the
// currently logged in user so that the UI JavaScript can decide whether to show
// a login page.
var indexHTML = []byte(`<!DOCTYPE html>
<html>
<head>
<title>Cockroach Console</title>
<meta charset="UTF-8">
<link href="favicon.ico" rel="shortcut icon">
</head>
<body>
<div id="react-layout"></div>
<script src="bundle.js" type="text/javascript"></script>
</body>
</html>
`)
type indexHTMLArgs struct {
// Insecure means disable auth entirely - anyone can use.
Insecure bool
LoggedInUser *string
Tag string
Version string
NodeID string
OIDCAutoLogin bool
OIDCLoginEnabled bool
OIDCButtonText string
FeatureFlags serverpb.FeatureFlags
}
// OIDCUIConf is a variable that stores data required by the
// Admin UI to display and manage the OIDC login flow. It is
// provided by the `oidcAuthenticationServer` at runtime
// since that's where all the OIDC configuration is centralized.
type OIDCUIConf struct {
ButtonText string
AutoLogin bool
Enabled bool
}
// OIDCUI is an interface that our OIDC configuration must implement in order to be able
// to pass relevant configuration info to the ui module. This is to pass through variables that
// are necessary to render an appropriate user interface for OIDC support and to set the state
// cookie that OIDC requires for securing auth requests.
type OIDCUI interface {
GetOIDCConf() OIDCUIConf
}
// bareIndexHTML is used in place of indexHTMLTemplate when the binary is built
// without the web UI.
var bareIndexHTML = []byte(fmt.Sprintf(`<!DOCTYPE html>
<title>CockroachDB</title>
Binary built without web UI.
<hr>
<em>%s</em>`, build.GetInfo().Short()))
// Config contains the configuration parameters for Handler.
type Config struct {
Insecure bool
NodeID *base.NodeIDContainer
GetUser func(ctx context.Context) *string
OIDC OIDCUI
Flags serverpb.FeatureFlags
}
var uiConfigPath = regexp.MustCompile("^/uiconfig$")
// Handler returns an http.Handler that serves the UI,
// including index.html, which has some login-related variables
// templated into it, as well as static assets.
func Handler(cfg Config) http.Handler {
// etags is used to provide a unique per-file checksum for each served file,
// which enables client-side caching using Cache-Control and ETag headers.
etags := make(map[string]string)
if HaveUI && Assets != nil {
// Only compute hashes for UI-enabled builds
err := httputil.ComputeEtags(Assets, etags)
if err != nil {
log.Errorf(context.Background(), "Unable to compute asset hashes: %+v", err)
}
}
fileHandlerChain := httputil.EtagHandler(
etags,
http.FileServer(
http.FS(Assets),
),
)
buildInfo := build.GetInfo()
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
oidcConf := cfg.OIDC.GetOIDCConf()
args := indexHTMLArgs{
Insecure: cfg.Insecure,
LoggedInUser: cfg.GetUser(r.Context()),
Tag: buildInfo.Tag,
Version: build.BinaryVersionPrefix(),
OIDCAutoLogin: oidcConf.AutoLogin,
OIDCLoginEnabled: oidcConf.Enabled,
OIDCButtonText: oidcConf.ButtonText,
FeatureFlags: cfg.Flags,
}
if cfg.NodeID != nil {
args.NodeID = cfg.NodeID.String()
}
if uiConfigPath.MatchString(r.URL.Path) {
argBytes, err := json.Marshal(args)
if err != nil {
log.Errorf(r.Context(), "unable to deserialize ui config args: %v", err)
http.Error(w, err.Error(), 500)
return
}
_, err = w.Write(argBytes)
if err != nil {
log.Errorf(r.Context(), "unable to write ui config args: %v", err)
http.Error(w, err.Error(), 500)
return
}
return
}
if r.Header.Get("Crdb-Development") != "" {
http.ServeContent(w, r, "index.html", buildInfo.GoTime(), bytes.NewReader(indexHTML))
return
}
if !HaveUI {
http.ServeContent(w, r, "index.html", buildInfo.GoTime(), bytes.NewReader(bareIndexHTML))
return
}
if r.URL.Path != "/" {
fileHandlerChain.ServeHTTP(w, r)
return
}
http.ServeContent(w, r, "index.html", buildInfo.GoTime(), bytes.NewReader(indexHTML))
})
}