Skip to content

Commit cdf0a5c

Browse files
committed
Add CORS configuration to security settings.
This patch adds a new CORS config textbox in Settings -> Security that allows configuring CORS origin domains per line. Closes #2724
1 parent 827a208 commit cdf0a5c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+151
-2
lines changed

cmd/handlers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ func initHTTPHandlers(e *echo.Echo, a *App) {
4040
e.DefaultHTTPErrorHandler(err, c)
4141
}
4242

43+
// Configure CORS middleware if domains are configured.
44+
if len(a.cfg.Security.CorsOrigins) > 0 {
45+
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
46+
AllowOrigins: a.cfg.Security.CorsOrigins,
47+
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
48+
}))
49+
}
50+
4351
// =================================================================
4452
// Authenticated non /api handlers.
4553
{

cmd/init.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ type Config struct {
117117
Secret string `koanf:"secret"`
118118
} `koanf:"hcaptcha"`
119119
} `koanf:"captcha"`
120+
121+
CorsOrigins []string `koanf:"cors_origins"`
120122
} `koanf:"security"`
121123

122124
Appearance struct {

cmd/settings.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"io"
66
"net/http"
7+
"net/url"
78
"regexp"
89
"runtime"
910
"strings"
@@ -256,6 +257,27 @@ func (a *App) UpdateSettings(c echo.Context) error {
256257
}
257258
set.DomainAllowlist = doms
258259

260+
// Validate and clean CORS domains.
261+
cors := make([]string, 0, len(set.SecurityCORSOrigins))
262+
for _, d := range set.SecurityCORSOrigins {
263+
if d = strings.TrimSpace(d); d != "" {
264+
if d == "*" {
265+
cors = append(cors, d)
266+
continue
267+
}
268+
269+
// Parse and validate the URL.
270+
u, err := url.Parse(d)
271+
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
272+
return echo.NewHTTPError(http.StatusBadRequest,
273+
a.i18n.Ts("globals.messages.invalidData")+": invalid CORS domain: "+d)
274+
}
275+
// Save clean scheme + host
276+
cors = append(cors, u.Scheme+"://"+u.Host)
277+
}
278+
}
279+
set.SecurityCORSOrigins = cors
280+
259281
// Validate slow query caching cron.
260282
if set.CacheSlowQueries {
261283
if _, err := cron.ParseStandard(set.CacheSlowQueriesInterval); err != nil {

cmd/upgrade.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ var migList = []migFunc{
4242
{"v4.1.0", migrations.V4_1_0},
4343
{"v5.0.0", migrations.V5_0_0},
4444
{"v5.1.0", migrations.V5_1_0},
45+
{"v5.2.0", migrations.V5_2_0},
4546
}
4647

4748
// upgrade upgrades the database to the current version by running SQL migration files

frontend/src/views/settings/security.vue

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,21 @@
131131
</b-field>
132132
</div>
133133
</div>
134-
</div>
134+
</div><!-- captcha -->
135+
136+
<hr />
137+
138+
<!-- CORS -->
139+
<div class="columns">
140+
<div class="column is-12">
141+
<h3 class="is-size-6"><strong>CORS</strong></h3><br />
142+
<b-field :label="$t('settings.security.CORSDomains')" label-position="on-border"
143+
:message="$t('settings.security.CORSDomainsHelp')">
144+
<b-input v-model="corsDomains" name="cors_origins" type="textarea" rows="5"
145+
placeholder="https://example.com" />
146+
</b-field>
147+
</div>
148+
</div><!-- cors -->
135149
</div>
136150
</template>
137151

@@ -161,6 +175,17 @@ export default Vue.extend({
161175
computed: {
162176
...mapState(['serverConfig', 'userRoles', 'listRoles']),
163177
178+
corsDomains: {
179+
get() {
180+
// Convert array to newline-separated string.
181+
const domains = this.data['security.cors_origins'];
182+
return domains && Array.isArray(domains) ? domains.join('\n') : '';
183+
},
184+
set(value) {
185+
this.$set(this.data, 'security.cors_origins', value.split('\n'));
186+
},
187+
},
188+
164189
captchaEnabled: {
165190
get() {
166191
return this.data['security.captcha'].altcha.enabled || this.data['security.captcha'].hcaptcha.enabled;

i18n/bg.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@
523523
"settings.privacy.recordOptinIP": "Записване на IP адреса на opt-in",
524524
"settings.privacy.recordOptinIPHelp": "Записване на IP адреса на двойния opt-in в атрибутите на абоната.",
525525
"settings.restart": "Рестартиране",
526+
"settings.security.CORSDomains": "Allowed origins",
527+
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
526528
"settings.security.OIDCAutoCreateUsers": "Автоматично създаване на потребители",
527529
"settings.security.OIDCAutoCreateUsersHelp": "Автоматично създаване на потребител при първо влизане, ако акаунтът не съществува.",
528530
"settings.security.OIDCClientID": "ID на клиент",

i18n/ca.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@
523523
"settings.privacy.recordOptinIP": "Registra l'adreça IP de l'opt-in",
524524
"settings.privacy.recordOptinIPHelp": "Registra l'adreça IP dels opt-ins dobles en els atributs del subscrit.",
525525
"settings.restart": "Reinicia",
526+
"settings.security.CORSDomains": "Allowed origins",
527+
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
526528
"settings.security.OIDCAutoCreateUsers": "Crea usuaris automàticament",
527529
"settings.security.OIDCAutoCreateUsersHelp": "Crea automàticament un usuari en el primer inici de sessió si el compte no existeix.",
528530
"settings.security.OIDCClientID": "ID del client",

i18n/cs-cz.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@
523523
"settings.privacy.recordOptinIP": "Zaznamenávat IP adresy pro opt-in",
524524
"settings.privacy.recordOptinIPHelp": "Zaznamenávat IP adresy pro dvojí opt-in v atributu odběratele.",
525525
"settings.restart": "Restartovat",
526+
"settings.security.CORSDomains": "Allowed origins",
527+
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
526528
"settings.security.OIDCAutoCreateUsers": "Automaticky vytvořit uživatele",
527529
"settings.security.OIDCAutoCreateUsersHelp": "Automaticky vytvořit uživatele při prvním přihlášení, pokud účet neexistuje.",
528530
"settings.security.OIDCClientID": "ID klienta",

i18n/cy.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@
523523
"settings.privacy.recordOptinIP": "Cofnodi cyfeiriad IP dewis mewn",
524524
"settings.privacy.recordOptinIPHelp": "Cofnodi cyfeiriad IP ar bwyntio dwbl yn manylion tanysgrifiwr.",
525525
"settings.restart": "Ailgychwyn",
526+
"settings.security.CORSDomains": "Allowed origins",
527+
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
526528
"settings.security.OIDCAutoCreateUsers": "Creu defnyddwyr yn awtomatig",
527529
"settings.security.OIDCAutoCreateUsersHelp": "Creu defnyddiwr yn awtomatig ar y mewngofnodi cyntaf os nad yw'r cyfrif yn bodoli.",
528530
"settings.security.OIDCClientID": "ID Cleient",

i18n/da.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@
523523
"settings.privacy.recordOptinIP": "Optag opt-in IP-adresse",
524524
"settings.privacy.recordOptinIPHelp": "Optag IP-adressen for dobbelt opt-ins i abonnentattributter.",
525525
"settings.restart": "Genstart",
526+
"settings.security.CORSDomains": "Allowed origins",
527+
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
526528
"settings.security.OIDCAutoCreateUsers": "Opret automatisk brugere",
527529
"settings.security.OIDCAutoCreateUsersHelp": "Opret automatisk bruger ved første login, hvis kontoen ikke eksisterer.",
528530
"settings.security.OIDCClientID": "Klient-ID",

0 commit comments

Comments
 (0)