Skip to content

Commit ad8e0db

Browse files
authored
feat: add custom error message on signups disabled page (#11959)
1 parent e070a55 commit ad8e0db

18 files changed

+135
-5
lines changed

cli/server.go

+1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ func createOIDCConfig(ctx context.Context, vals *codersdk.DeploymentValues) (*co
179179
UserRoleMapping: vals.OIDC.UserRoleMapping.Value,
180180
UserRolesDefault: vals.OIDC.UserRolesDefault.GetSlice(),
181181
SignInText: vals.OIDC.SignInText.String(),
182+
SignupsDisabledText: vals.OIDC.SignupsDisabledText.String(),
182183
IconURL: vals.OIDC.IconURL.String(),
183184
IgnoreEmailVerified: vals.OIDC.IgnoreEmailVerified.Value(),
184185
}, nil

cli/testdata/coder_server_--help.golden

+4
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,10 @@ OIDC OPTIONS:
426426
--oidc-icon-url url, $CODER_OIDC_ICON_URL
427427
URL pointing to the icon to use on the OpenID Connect login button.
428428

429+
--oidc-signups-disabled-text string, $CODER_OIDC_SIGNUPS_DISABLED_TEXT
430+
The custom text to show on the error page informing about disabled
431+
OIDC signups. Markdown format is supported.
432+
429433
PROVISIONING OPTIONS:
430434
Tune the behavior of the provisioner, which is responsible for creating,
431435
updating, and deleting workspace resources.

cli/testdata/server-config.yaml.golden

+4
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ oidc:
348348
# URL pointing to the icon to use on the OpenID Connect login button.
349349
# (default: <unset>, type: url)
350350
iconURL:
351+
# The custom text to show on the error page informing about disabled OIDC signups.
352+
# Markdown format is supported.
353+
# (default: <unset>, type: string)
354+
signupsDisabledText: ""
351355
# Telemetry is critical to our ability to improve Coder. We strip all personal
352356
# information before sending data to our servers. Please only disable telemetry
353357
# when required by your organization's security policy.

coderd/apidoc/docs.go

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/parameter/plaintext.go renamed to coderd/parameter/renderer.go

+14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package parameter
22

33
import (
4+
"bytes"
45
"strings"
56

67
"github.com/charmbracelet/glamour"
78
"github.com/charmbracelet/glamour/ansi"
9+
gomarkdown "github.com/gomarkdown/markdown"
10+
"github.com/gomarkdown/markdown/html"
11+
"github.com/gomarkdown/markdown/parser"
812
"golang.org/x/xerrors"
913
)
1014

@@ -95,3 +99,13 @@ func Plaintext(markdown string) (string, error) {
9599

96100
return strings.TrimSpace(output), nil
97101
}
102+
103+
func HTML(markdown string) string {
104+
p := parser.NewWithExtensions(parser.CommonExtensions)
105+
doc := p.Parse([]byte(markdown))
106+
renderer := html.NewRenderer(html.RendererOptions{
107+
Flags: html.CommonFlags | html.SkipHTML,
108+
},
109+
)
110+
return string(bytes.TrimSpace(gomarkdown.Render(doc, renderer)))
111+
}

coderd/parameter/plaintext_test.go renamed to coderd/parameter/renderer_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,45 @@ __This is bold text.__
4747
require.Equal(t, nothingChanges, stripped)
4848
})
4949
}
50+
51+
func TestHTML(t *testing.T) {
52+
t.Parallel()
53+
54+
tests := []struct {
55+
name string
56+
input string
57+
expected string
58+
}{
59+
{
60+
name: "Simple",
61+
input: `**Coder** is in *early access* mode. To ~~register~~ request access, fill out [this form](https://internal.example.com). ***Thank you!***`,
62+
expected: `<p><strong>Coder</strong> is in <em>early access</em> mode. To <del>register</del> request access, fill out <a href="https://internal.example.com">this form</a>. <strong><em>Thank you!</em></strong></p>`,
63+
},
64+
{
65+
name: "Tricky",
66+
input: `**Cod*er** is in *early a**ccess** <img src="foobar">mode`,
67+
expected: `<p><strong>Cod*er</strong> is in *early a<strong>ccess</strong> mode</p>`,
68+
},
69+
{
70+
name: "XSS",
71+
input: `<p onclick="alert(\"omghax\")">Click here to get access!</p>?`,
72+
expected: `<p>Click here to get access!?</p>`,
73+
},
74+
{
75+
name: "No Markdown tags",
76+
input: "This is a simple description, so nothing changes.",
77+
expected: "<p>This is a simple description, so nothing changes.</p>",
78+
},
79+
}
80+
81+
for _, tt := range tests {
82+
tt := tt
83+
84+
t.Run(tt.name, func(t *testing.T) {
85+
t.Parallel()
86+
87+
rendered := parameter.HTML(tt.input)
88+
require.Equal(t, tt.expected, rendered)
89+
})
90+
}
91+
}

coderd/userauth.go

+17-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/coder/coder/v2/coderd/database/dbtime"
3232
"github.com/coder/coder/v2/coderd/httpapi"
3333
"github.com/coder/coder/v2/coderd/httpmw"
34+
"github.com/coder/coder/v2/coderd/parameter"
3435
"github.com/coder/coder/v2/coderd/promoauth"
3536
"github.com/coder/coder/v2/coderd/rbac"
3637
"github.com/coder/coder/v2/coderd/userpassword"
@@ -740,6 +741,8 @@ type OIDCConfig struct {
740741
SignInText string
741742
// IconURL points to the URL of an icon to display on the OIDC login button
742743
IconURL string
744+
// SignupsDisabledText is the text do display on the static error page.
745+
SignupsDisabledText string
743746
}
744747

745748
func (cfg OIDCConfig) RoleSyncEnabled() bool {
@@ -1252,6 +1255,8 @@ type httpError struct {
12521255
msg string
12531256
detail string
12541257
renderStaticPage bool
1258+
1259+
renderDetailMarkdown bool
12551260
}
12561261

12571262
func (e httpError) Write(rw http.ResponseWriter, r *http.Request) {
@@ -1263,6 +1268,8 @@ func (e httpError) Write(rw http.ResponseWriter, r *http.Request) {
12631268
Description: e.detail,
12641269
RetryEnabled: false,
12651270
DashboardURL: "/login",
1271+
1272+
RenderDescriptionMarkdown: e.renderDetailMarkdown,
12661273
})
12671274
return
12681275
}
@@ -1313,9 +1320,17 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
13131320
}
13141321

13151322
if user.ID == uuid.Nil && !params.AllowSignups {
1323+
signupsDisabledText := "Please contact your Coder administrator to request access."
1324+
if api.OIDCConfig != nil && api.OIDCConfig.SignupsDisabledText != "" {
1325+
signupsDisabledText = parameter.HTML(api.OIDCConfig.SignupsDisabledText)
1326+
}
13161327
return httpError{
1317-
code: http.StatusForbidden,
1318-
msg: fmt.Sprintf("Signups are not allowed for login type %q", params.LoginType),
1328+
code: http.StatusForbidden,
1329+
msg: "Signups are disabled",
1330+
detail: signupsDisabledText,
1331+
renderStaticPage: true,
1332+
1333+
renderDetailMarkdown: true,
13191334
}
13201335
}
13211336

codersdk/deployment.go

+10
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ type OIDCConfig struct {
303303
UserRolesDefault clibase.StringArray `json:"user_roles_default" typescript:",notnull"`
304304
SignInText clibase.String `json:"sign_in_text" typescript:",notnull"`
305305
IconURL clibase.URL `json:"icon_url" typescript:",notnull"`
306+
SignupsDisabledText clibase.String `json:"signups_disabled_text" typescript:",notnull"`
306307
}
307308

308309
type TelemetryConfig struct {
@@ -1266,6 +1267,15 @@ when required by your organization's security policy.`,
12661267
Group: &deploymentGroupOIDC,
12671268
YAML: "iconURL",
12681269
},
1270+
{
1271+
Name: "Signups disabled text",
1272+
Description: "The custom text to show on the error page informing about disabled OIDC signups. Markdown format is supported.",
1273+
Flag: "oidc-signups-disabled-text",
1274+
Env: "CODER_OIDC_SIGNUPS_DISABLED_TEXT",
1275+
Value: &c.OIDC.SignupsDisabledText,
1276+
Group: &deploymentGroupOIDC,
1277+
YAML: "signupsDisabledText",
1278+
},
12691279
// Telemetry settings
12701280
{
12711281
Name: "Telemetry Enable",

docs/api/general.md

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/api/schemas.md

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/server.md

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enterprise/cli/testdata/coder_server_--help.golden

+4
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,10 @@ OIDC OPTIONS:
427427
--oidc-icon-url url, $CODER_OIDC_ICON_URL
428428
URL pointing to the icon to use on the OpenID Connect login button.
429429

430+
--oidc-signups-disabled-text string, $CODER_OIDC_SIGNUPS_DISABLED_TEXT
431+
The custom text to show on the error page informing about disabled
432+
OIDC signups. Markdown format is supported.
433+
430434
PROVISIONING OPTIONS:
431435
Tune the behavior of the provisioner, which is responsible for creating,
432436
updating, and deleting workspace resources.

go.mod

+4-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ require (
206206

207207
require go.uber.org/mock v0.4.0
208208

209-
require github.com/benbjohnson/clock v1.3.5
209+
require (
210+
github.com/benbjohnson/clock v1.3.5
211+
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47
212+
)
210213

211214
require github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 // indirect
212215

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
417417
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
418418
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
419419
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
420+
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 h1:k4Tw0nt6lwro3Uin8eqoET7MDA4JnT8YgbCjc/g5E3k=
421+
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
420422
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
421423
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
422424
github.com/google/flatbuffers v23.1.21+incompatible h1:bUqzx/MXCDxuS0hRJL2EfjyZL3uQrPbMocUa8zGqsTA=

site/site.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,8 @@ type ErrorPageData struct {
773773
RetryEnabled bool
774774
DashboardURL string
775775
Warnings []string
776+
777+
RenderDescriptionMarkdown bool
776778
}
777779

778780
// RenderStaticErrorPage renders the static error page. This is used by app
@@ -781,12 +783,17 @@ type ErrorPageData struct {
781783
func RenderStaticErrorPage(rw http.ResponseWriter, r *http.Request, data ErrorPageData) {
782784
type outerData struct {
783785
Error ErrorPageData
786+
787+
ErrorDescriptionHTML htmltemplate.HTML
784788
}
785789

786790
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
787791
rw.WriteHeader(data.Status)
788792

789-
err := errorTemplate.Execute(rw, outerData{Error: data})
793+
err := errorTemplate.Execute(rw, outerData{
794+
Error: data,
795+
ErrorDescriptionHTML: htmltemplate.HTML(data.Description), //nolint:gosec // gosec thinks this is user-input, but it is from Coder deployment configuration.
796+
})
790797
if err != nil {
791798
httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
792799
Message: "Failed to render error page: " + err.Error(),

site/src/api/typesGenerated.ts

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/error.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ <h1>
167167
{{- if not .Error.HideStatus }}{{ .Error.Status }} - {{end}}{{
168168
.Error.Title }}
169169
</h1>
170+
{{- if .Error.RenderDescriptionMarkdown }} {{ .ErrorDescriptionHTML }} {{
171+
else }}
170172
<p>{{ .Error.Description }}</p>
171-
{{- if .Error.Warnings }}
173+
{{ end }} {{- if .Error.Warnings }}
172174
<div class="warning">
173175
<div class="warning-title">
174176
<svg

0 commit comments

Comments
 (0)