Skip to content

feat: add custom error message on signups disabled page #11959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func createOIDCConfig(ctx context.Context, vals *codersdk.DeploymentValues) (*co
UserRoleMapping: vals.OIDC.UserRoleMapping.Value,
UserRolesDefault: vals.OIDC.UserRolesDefault.GetSlice(),
SignInText: vals.OIDC.SignInText.String(),
SignupsDisabledText: vals.OIDC.SignupsDisabledText.String(),
IconURL: vals.OIDC.IconURL.String(),
IgnoreEmailVerified: vals.OIDC.IgnoreEmailVerified.Value(),
}, nil
Expand Down
4 changes: 4 additions & 0 deletions cli/testdata/coder_server_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,10 @@ OIDC OPTIONS:
--oidc-icon-url url, $CODER_OIDC_ICON_URL
URL pointing to the icon to use on the OpenID Connect login button.

--oidc-signups-disabled-text string, $CODER_OIDC_SIGNUPS_DISABLED_TEXT
The custom text to show on the error page informing about disabled
OIDC signups. Markdown format is supported.

PROVISIONING OPTIONS:
Tune the behavior of the provisioner, which is responsible for creating,
updating, and deleting workspace resources.
Expand Down
4 changes: 4 additions & 0 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ oidc:
# URL pointing to the icon to use on the OpenID Connect login button.
# (default: <unset>, type: url)
iconURL:
# The custom text to show on the error page informing about disabled OIDC signups.
# Markdown format is supported.
# (default: <unset>, type: string)
signupsDisabledText: ""
# Telemetry is critical to our ability to improve Coder. We strip all personal
# information before sending data to our servers. Please only disable telemetry
# when required by your organization's security policy.
Expand Down
3 changes: 3 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions coderd/parameter/plaintext.go → coderd/parameter/renderer.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package parameter

import (
"bytes"
"strings"

"github.com/charmbracelet/glamour"
"github.com/charmbracelet/glamour/ansi"
gomarkdown "github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
"golang.org/x/xerrors"
)

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

return strings.TrimSpace(output), nil
}

func HTML(markdown string) string {
p := parser.NewWithExtensions(parser.CommonExtensions)
doc := p.Parse([]byte(markdown))
renderer := html.NewRenderer(html.RendererOptions{
Flags: html.CommonFlags | html.SkipHTML,
},
)
return string(bytes.TrimSpace(gomarkdown.Render(doc, renderer)))
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,45 @@ __This is bold text.__
require.Equal(t, nothingChanges, stripped)
})
}

func TestHTML(t *testing.T) {
t.Parallel()

tests := []struct {
name string
input string
expected string
}{
{
name: "Simple",
input: `**Coder** is in *early access* mode. To ~~register~~ request access, fill out [this form](https://internal.example.com). ***Thank you!***`,
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>`,
},
{
name: "Tricky",
input: `**Cod*er** is in *early a**ccess** <img src="foobar">mode`,
expected: `<p><strong>Cod*er</strong> is in *early a<strong>ccess</strong> mode</p>`,
},
{
name: "XSS",
input: `<p onclick="alert(\"omghax\")">Click here to get access!</p>?`,
expected: `<p>Click here to get access!?</p>`,
},
{
name: "No Markdown tags",
input: "This is a simple description, so nothing changes.",
expected: "<p>This is a simple description, so nothing changes.</p>",
},
}

for _, tt := range tests {
tt := tt

t.Run(tt.name, func(t *testing.T) {
t.Parallel()

rendered := parameter.HTML(tt.input)
require.Equal(t, tt.expected, rendered)
})
}
}
19 changes: 17 additions & 2 deletions coderd/userauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/parameter"
"github.com/coder/coder/v2/coderd/promoauth"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/userpassword"
Expand Down Expand Up @@ -740,6 +741,8 @@ type OIDCConfig struct {
SignInText string
// IconURL points to the URL of an icon to display on the OIDC login button
IconURL string
// SignupsDisabledText is the text do display on the static error page.
SignupsDisabledText string
}

func (cfg OIDCConfig) RoleSyncEnabled() bool {
Expand Down Expand Up @@ -1252,6 +1255,8 @@ type httpError struct {
msg string
detail string
renderStaticPage bool

renderDetailMarkdown bool
}

func (e httpError) Write(rw http.ResponseWriter, r *http.Request) {
Expand All @@ -1263,6 +1268,8 @@ func (e httpError) Write(rw http.ResponseWriter, r *http.Request) {
Description: e.detail,
RetryEnabled: false,
DashboardURL: "/login",

RenderDescriptionMarkdown: e.renderDetailMarkdown,
})
return
}
Expand Down Expand Up @@ -1313,9 +1320,17 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
}

if user.ID == uuid.Nil && !params.AllowSignups {
signupsDisabledText := "Please contact your Coder administrator to request access."
if api.OIDCConfig != nil && api.OIDCConfig.SignupsDisabledText != "" {
signupsDisabledText = parameter.HTML(api.OIDCConfig.SignupsDisabledText)
}
return httpError{
code: http.StatusForbidden,
msg: fmt.Sprintf("Signups are not allowed for login type %q", params.LoginType),
code: http.StatusForbidden,
msg: "Signups are disabled",
detail: signupsDisabledText,
renderStaticPage: true,

renderDetailMarkdown: true,
}
}

Expand Down
10 changes: 10 additions & 0 deletions codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ type OIDCConfig struct {
UserRolesDefault clibase.StringArray `json:"user_roles_default" typescript:",notnull"`
SignInText clibase.String `json:"sign_in_text" typescript:",notnull"`
IconURL clibase.URL `json:"icon_url" typescript:",notnull"`
SignupsDisabledText clibase.String `json:"signups_disabled_text" typescript:",notnull"`
}

type TelemetryConfig struct {
Expand Down Expand Up @@ -1266,6 +1267,15 @@ when required by your organization's security policy.`,
Group: &deploymentGroupOIDC,
YAML: "iconURL",
},
{
Name: "Signups disabled text",
Description: "The custom text to show on the error page informing about disabled OIDC signups. Markdown format is supported.",
Flag: "oidc-signups-disabled-text",
Env: "CODER_OIDC_SIGNUPS_DISABLED_TEXT",
Value: &c.OIDC.SignupsDisabledText,
Group: &deploymentGroupOIDC,
YAML: "signupsDisabledText",
},
// Telemetry settings
{
Name: "Telemetry Enable",
Expand Down
1 change: 1 addition & 0 deletions docs/api/general.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions docs/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions docs/cli/server.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions enterprise/cli/testdata/coder_server_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ OIDC OPTIONS:
--oidc-icon-url url, $CODER_OIDC_ICON_URL
URL pointing to the icon to use on the OpenID Connect login button.

--oidc-signups-disabled-text string, $CODER_OIDC_SIGNUPS_DISABLED_TEXT
The custom text to show on the error page informing about disabled
OIDC signups. Markdown format is supported.

PROVISIONING OPTIONS:
Tune the behavior of the provisioner, which is responsible for creating,
updating, and deleting workspace resources.
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,10 @@ require (

require go.uber.org/mock v0.4.0

require github.com/benbjohnson/clock v1.3.5
require (
github.com/benbjohnson/clock v1.3.5
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47
)

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

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 h1:k4Tw0nt6lwro3Uin8eqoET7MDA4JnT8YgbCjc/g5E3k=
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/flatbuffers v23.1.21+incompatible h1:bUqzx/MXCDxuS0hRJL2EfjyZL3uQrPbMocUa8zGqsTA=
Expand Down
9 changes: 8 additions & 1 deletion site/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,8 @@ type ErrorPageData struct {
RetryEnabled bool
DashboardURL string
Warnings []string

RenderDescriptionMarkdown bool
}

// RenderStaticErrorPage renders the static error page. This is used by app
Expand All @@ -781,12 +783,17 @@ type ErrorPageData struct {
func RenderStaticErrorPage(rw http.ResponseWriter, r *http.Request, data ErrorPageData) {
type outerData struct {
Error ErrorPageData

ErrorDescriptionHTML htmltemplate.HTML
}

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

err := errorTemplate.Execute(rw, outerData{Error: data})
err := errorTemplate.Execute(rw, outerData{
Error: data,
ErrorDescriptionHTML: htmltemplate.HTML(data.Description), //nolint:gosec // gosec thinks this is user-input, but it is from Coder deployment configuration.
})
if err != nil {
httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to render error page: " + err.Error(),
Expand Down
1 change: 1 addition & 0 deletions site/src/api/typesGenerated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion site/static/error.html
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ <h1>
{{- if not .Error.HideStatus }}{{ .Error.Status }} - {{end}}{{
.Error.Title }}
</h1>
{{- if .Error.RenderDescriptionMarkdown }} {{ .ErrorDescriptionHTML }} {{
else }}
<p>{{ .Error.Description }}</p>
{{- if .Error.Warnings }}
{{ end }} {{- if .Error.Warnings }}
<div class="warning">
<div class="warning-title">
<svg
Expand Down