mirror of https://github.com/coder/coder.git
feat: add custom error message on signups disabled page (#11959)
This commit is contained in:
parent
e070a55142
commit
ad8e0db172
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -10071,6 +10071,9 @@ const docTemplate = `{
|
|||
"sign_in_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"signups_disabled_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_role_field": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
@ -9052,6 +9052,9 @@
|
|||
"sign_in_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"signups_disabled_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_role_field": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
@ -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)))
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
|
|
@ -298,6 +298,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
|
|||
"issuer_url": "string",
|
||||
"scopes": ["string"],
|
||||
"sign_in_text": "string",
|
||||
"signups_disabled_text": "string",
|
||||
"user_role_field": "string",
|
||||
"user_role_mapping": {},
|
||||
"user_roles_default": ["string"],
|
||||
|
|
|
@ -2273,6 +2273,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"issuer_url": "string",
|
||||
"scopes": ["string"],
|
||||
"sign_in_text": "string",
|
||||
"signups_disabled_text": "string",
|
||||
"user_role_field": "string",
|
||||
"user_role_mapping": {},
|
||||
"user_roles_default": ["string"],
|
||||
|
@ -2640,6 +2641,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"issuer_url": "string",
|
||||
"scopes": ["string"],
|
||||
"sign_in_text": "string",
|
||||
"signups_disabled_text": "string",
|
||||
"user_role_field": "string",
|
||||
"user_role_mapping": {},
|
||||
"user_roles_default": ["string"],
|
||||
|
@ -3747,6 +3749,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
"issuer_url": "string",
|
||||
"scopes": ["string"],
|
||||
"sign_in_text": "string",
|
||||
"signups_disabled_text": "string",
|
||||
"user_role_field": "string",
|
||||
"user_role_mapping": {},
|
||||
"user_roles_default": ["string"],
|
||||
|
@ -3777,6 +3780,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
| `issuer_url` | string | false | | |
|
||||
| `scopes` | array of string | false | | |
|
||||
| `sign_in_text` | string | false | | |
|
||||
| `signups_disabled_text` | string | false | | |
|
||||
| `user_role_field` | string | false | | |
|
||||
| `user_role_mapping` | object | false | | |
|
||||
| `user_roles_default` | array of string | false | | |
|
||||
|
|
|
@ -897,6 +897,16 @@ Controls if the 'Secure' property is set on browser session cookies.
|
|||
|
||||
The token expiry duration for browser sessions. Sessions may last longer if they are actively making requests, but this functionality can be disabled via --disable-session-expiry-refresh.
|
||||
|
||||
### --oidc-signups-disabled-text
|
||||
|
||||
| | |
|
||||
| ----------- | ---------------------------------------------- |
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_OIDC_SIGNUPS_DISABLED_TEXT</code> |
|
||||
| YAML | <code>oidc.signupsDisabledText</code> |
|
||||
|
||||
The custom text to show on the error page informing about disabled OIDC signups. Markdown format is supported.
|
||||
|
||||
### --log-stackdriver
|
||||
|
||||
| | |
|
||||
|
|
|
@ -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.
|
||||
|
|
5
go.mod
5
go.mod
|
@ -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
|
||||
|
||||
|
|
2
go.sum
2
go.sum
|
@ -417,6 +417,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=
|
||||
|
|
|
@ -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
|
||||
|
@ -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(),
|
||||
|
|
|
@ -752,6 +752,7 @@ export interface OIDCConfig {
|
|||
readonly user_roles_default: string[];
|
||||
readonly sign_in_text: string;
|
||||
readonly icon_url: string;
|
||||
readonly signups_disabled_text: string;
|
||||
}
|
||||
|
||||
// From codersdk/organizations.go
|
||||
|
|
|
@ -167,8 +167,10 @@ running). */}}
|
|||
{{- 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
|
||||
|
|
Loading…
Reference in New Issue