mirror of https://github.com/go-gitea/gitea.git
add skip secondary authorization option for public oauth2 clients (#31454)
This commit is contained in:
parent
e9aa39bda4
commit
a8d0c879c3
|
@ -37,10 +37,11 @@ type OAuth2Application struct {
|
||||||
// https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
|
// https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
|
||||||
// "Authorization servers MUST record the client type in the client registration details"
|
// "Authorization servers MUST record the client type in the client registration details"
|
||||||
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
|
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
|
||||||
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
|
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
|
||||||
RedirectURIs []string `xorm:"redirect_uris JSON TEXT"`
|
SkipSecondaryAuthorization bool `xorm:"NOT NULL DEFAULT FALSE"`
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
RedirectURIs []string `xorm:"redirect_uris JSON TEXT"`
|
||||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -251,21 +252,23 @@ func GetOAuth2ApplicationByID(ctx context.Context, id int64) (app *OAuth2Applica
|
||||||
|
|
||||||
// CreateOAuth2ApplicationOptions holds options to create an oauth2 application
|
// CreateOAuth2ApplicationOptions holds options to create an oauth2 application
|
||||||
type CreateOAuth2ApplicationOptions struct {
|
type CreateOAuth2ApplicationOptions struct {
|
||||||
Name string
|
Name string
|
||||||
UserID int64
|
UserID int64
|
||||||
ConfidentialClient bool
|
ConfidentialClient bool
|
||||||
RedirectURIs []string
|
SkipSecondaryAuthorization bool
|
||||||
|
RedirectURIs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOAuth2Application inserts a new oauth2 application
|
// CreateOAuth2Application inserts a new oauth2 application
|
||||||
func CreateOAuth2Application(ctx context.Context, opts CreateOAuth2ApplicationOptions) (*OAuth2Application, error) {
|
func CreateOAuth2Application(ctx context.Context, opts CreateOAuth2ApplicationOptions) (*OAuth2Application, error) {
|
||||||
clientID := uuid.New().String()
|
clientID := uuid.New().String()
|
||||||
app := &OAuth2Application{
|
app := &OAuth2Application{
|
||||||
UID: opts.UserID,
|
UID: opts.UserID,
|
||||||
Name: opts.Name,
|
Name: opts.Name,
|
||||||
ClientID: clientID,
|
ClientID: clientID,
|
||||||
RedirectURIs: opts.RedirectURIs,
|
RedirectURIs: opts.RedirectURIs,
|
||||||
ConfidentialClient: opts.ConfidentialClient,
|
ConfidentialClient: opts.ConfidentialClient,
|
||||||
|
SkipSecondaryAuthorization: opts.SkipSecondaryAuthorization,
|
||||||
}
|
}
|
||||||
if err := db.Insert(ctx, app); err != nil {
|
if err := db.Insert(ctx, app); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -275,11 +278,12 @@ func CreateOAuth2Application(ctx context.Context, opts CreateOAuth2ApplicationOp
|
||||||
|
|
||||||
// UpdateOAuth2ApplicationOptions holds options to update an oauth2 application
|
// UpdateOAuth2ApplicationOptions holds options to update an oauth2 application
|
||||||
type UpdateOAuth2ApplicationOptions struct {
|
type UpdateOAuth2ApplicationOptions struct {
|
||||||
ID int64
|
ID int64
|
||||||
Name string
|
Name string
|
||||||
UserID int64
|
UserID int64
|
||||||
ConfidentialClient bool
|
ConfidentialClient bool
|
||||||
RedirectURIs []string
|
SkipSecondaryAuthorization bool
|
||||||
|
RedirectURIs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOAuth2Application updates an oauth2 application
|
// UpdateOAuth2Application updates an oauth2 application
|
||||||
|
@ -305,6 +309,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp
|
||||||
app.Name = opts.Name
|
app.Name = opts.Name
|
||||||
app.RedirectURIs = opts.RedirectURIs
|
app.RedirectURIs = opts.RedirectURIs
|
||||||
app.ConfidentialClient = opts.ConfidentialClient
|
app.ConfidentialClient = opts.ConfidentialClient
|
||||||
|
app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization
|
||||||
|
|
||||||
if err = updateOAuth2Application(ctx, app); err != nil {
|
if err = updateOAuth2Application(ctx, app); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -315,7 +320,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
|
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
|
||||||
if _, err := db.GetEngine(ctx).ID(app.ID).UseBool("confidential_client").Update(app); err != nil {
|
if _, err := db.GetEngine(ctx).ID(app.ID).UseBool("confidential_client", "skip_secondary_authorization").Update(app); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -593,6 +593,8 @@ var migrations = []Migration{
|
||||||
NewMigration("Add content version to issue and comment table", v1_23.AddContentVersionToIssueAndComment),
|
NewMigration("Add content version to issue and comment table", v1_23.AddContentVersionToIssueAndComment),
|
||||||
// v300 -> v301
|
// v300 -> v301
|
||||||
NewMigration("Add force-push branch protection support", v1_23.AddForcePushBranchProtection),
|
NewMigration("Add force-push branch protection support", v1_23.AddForcePushBranchProtection),
|
||||||
|
// v301 -> v302
|
||||||
|
NewMigration("Add skip_secondary_authorization option to oauth2 application table", v1_23.AddSkipSecondaryAuthColumnToOAuth2ApplicationTable),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import "xorm.io/xorm"
|
||||||
|
|
||||||
|
// AddSkipSeconderyAuthToOAuth2ApplicationTable: add SkipSecondaryAuthorization column, setting existing rows to false
|
||||||
|
func AddSkipSecondaryAuthColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
|
||||||
|
type oauth2Application struct {
|
||||||
|
SkipSecondaryAuthorization bool `xorm:"NOT NULL DEFAULT FALSE"`
|
||||||
|
}
|
||||||
|
return x.Sync(new(oauth2Application))
|
||||||
|
}
|
|
@ -31,21 +31,23 @@ type CreateAccessTokenOption struct {
|
||||||
|
|
||||||
// CreateOAuth2ApplicationOptions holds options to create an oauth2 application
|
// CreateOAuth2ApplicationOptions holds options to create an oauth2 application
|
||||||
type CreateOAuth2ApplicationOptions struct {
|
type CreateOAuth2ApplicationOptions struct {
|
||||||
Name string `json:"name" binding:"Required"`
|
Name string `json:"name" binding:"Required"`
|
||||||
ConfidentialClient bool `json:"confidential_client"`
|
ConfidentialClient bool `json:"confidential_client"`
|
||||||
RedirectURIs []string `json:"redirect_uris" binding:"Required"`
|
SkipSecondaryAuthorization bool `json:"skip_secondary_authorization"`
|
||||||
|
RedirectURIs []string `json:"redirect_uris" binding:"Required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OAuth2Application represents an OAuth2 application.
|
// OAuth2Application represents an OAuth2 application.
|
||||||
// swagger:response OAuth2Application
|
// swagger:response OAuth2Application
|
||||||
type OAuth2Application struct {
|
type OAuth2Application struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ClientID string `json:"client_id"`
|
ClientID string `json:"client_id"`
|
||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
ConfidentialClient bool `json:"confidential_client"`
|
ConfidentialClient bool `json:"confidential_client"`
|
||||||
RedirectURIs []string `json:"redirect_uris"`
|
SkipSecondaryAuthorization bool `json:"skip_secondary_authorization"`
|
||||||
Created time.Time `json:"created"`
|
RedirectURIs []string `json:"redirect_uris"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OAuth2ApplicationList represents a list of OAuth2 applications.
|
// OAuth2ApplicationList represents a list of OAuth2 applications.
|
||||||
|
|
|
@ -914,6 +914,7 @@ create_oauth2_application_success = You have successfully created a new OAuth2 a
|
||||||
update_oauth2_application_success = You have successfully updated the OAuth2 application.
|
update_oauth2_application_success = You have successfully updated the OAuth2 application.
|
||||||
oauth2_application_name = Application Name
|
oauth2_application_name = Application Name
|
||||||
oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps.
|
oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps.
|
||||||
|
oauth2_skip_secondary_authorization = Skip authorization for public clients after granting access once. <strong>May pose a security risk.</strong>
|
||||||
oauth2_redirect_uris = Redirect URIs. Please use a new line for every URI.
|
oauth2_redirect_uris = Redirect URIs. Please use a new line for every URI.
|
||||||
save_application = Save
|
save_application = Save
|
||||||
oauth2_client_id = Client ID
|
oauth2_client_id = Client ID
|
||||||
|
|
|
@ -223,10 +223,11 @@ func CreateOauth2Application(ctx *context.APIContext) {
|
||||||
data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
|
data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
|
||||||
|
|
||||||
app, err := auth_model.CreateOAuth2Application(ctx, auth_model.CreateOAuth2ApplicationOptions{
|
app, err := auth_model.CreateOAuth2Application(ctx, auth_model.CreateOAuth2ApplicationOptions{
|
||||||
Name: data.Name,
|
Name: data.Name,
|
||||||
UserID: ctx.Doer.ID,
|
UserID: ctx.Doer.ID,
|
||||||
RedirectURIs: data.RedirectURIs,
|
RedirectURIs: data.RedirectURIs,
|
||||||
ConfidentialClient: data.ConfidentialClient,
|
ConfidentialClient: data.ConfidentialClient,
|
||||||
|
SkipSecondaryAuthorization: data.SkipSecondaryAuthorization,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusBadRequest, "", "error creating oauth2 application")
|
ctx.Error(http.StatusBadRequest, "", "error creating oauth2 application")
|
||||||
|
@ -381,11 +382,12 @@ func UpdateOauth2Application(ctx *context.APIContext) {
|
||||||
data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
|
data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
|
||||||
|
|
||||||
app, err := auth_model.UpdateOAuth2Application(ctx, auth_model.UpdateOAuth2ApplicationOptions{
|
app, err := auth_model.UpdateOAuth2Application(ctx, auth_model.UpdateOAuth2ApplicationOptions{
|
||||||
Name: data.Name,
|
Name: data.Name,
|
||||||
UserID: ctx.Doer.ID,
|
UserID: ctx.Doer.ID,
|
||||||
ID: appID,
|
ID: appID,
|
||||||
RedirectURIs: data.RedirectURIs,
|
RedirectURIs: data.RedirectURIs,
|
||||||
ConfidentialClient: data.ConfidentialClient,
|
ConfidentialClient: data.ConfidentialClient,
|
||||||
|
SkipSecondaryAuthorization: data.SkipSecondaryAuthorization,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
|
if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
|
||||||
|
|
|
@ -469,9 +469,9 @@ func AuthorizeOAuth(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect if user already granted access and the application is confidential.
|
// Redirect if user already granted access and the application is confidential or trusted otherwise
|
||||||
// I.e. always require authorization for public clients as recommended by RFC 6749 Section 10.2
|
// I.e. always require authorization for untrusted public clients as recommended by RFC 6749 Section 10.2
|
||||||
if app.ConfidentialClient && grant != nil {
|
if (app.ConfidentialClient || app.SkipSecondaryAuthorization) && grant != nil {
|
||||||
code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
|
code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleServerError(ctx, form.State, form.RedirectURI)
|
handleServerError(ctx, form.State, form.RedirectURI)
|
||||||
|
|
|
@ -49,10 +49,11 @@ func (oa *OAuth2CommonHandlers) AddApp(ctx *context.Context) {
|
||||||
|
|
||||||
// TODO validate redirect URI
|
// TODO validate redirect URI
|
||||||
app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
|
app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
|
||||||
Name: form.Name,
|
Name: form.Name,
|
||||||
RedirectURIs: util.SplitTrimSpace(form.RedirectURIs, "\n"),
|
RedirectURIs: util.SplitTrimSpace(form.RedirectURIs, "\n"),
|
||||||
UserID: oa.OwnerID,
|
UserID: oa.OwnerID,
|
||||||
ConfidentialClient: form.ConfidentialClient,
|
ConfidentialClient: form.ConfidentialClient,
|
||||||
|
SkipSecondaryAuthorization: form.SkipSecondaryAuthorization,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("CreateOAuth2Application", err)
|
ctx.ServerError("CreateOAuth2Application", err)
|
||||||
|
@ -102,11 +103,12 @@ func (oa *OAuth2CommonHandlers) EditSave(ctx *context.Context) {
|
||||||
// TODO validate redirect URI
|
// TODO validate redirect URI
|
||||||
var err error
|
var err error
|
||||||
if ctx.Data["App"], err = auth.UpdateOAuth2Application(ctx, auth.UpdateOAuth2ApplicationOptions{
|
if ctx.Data["App"], err = auth.UpdateOAuth2Application(ctx, auth.UpdateOAuth2ApplicationOptions{
|
||||||
ID: ctx.PathParamInt64("id"),
|
ID: ctx.PathParamInt64("id"),
|
||||||
Name: form.Name,
|
Name: form.Name,
|
||||||
RedirectURIs: util.SplitTrimSpace(form.RedirectURIs, "\n"),
|
RedirectURIs: util.SplitTrimSpace(form.RedirectURIs, "\n"),
|
||||||
UserID: oa.OwnerID,
|
UserID: oa.OwnerID,
|
||||||
ConfidentialClient: form.ConfidentialClient,
|
ConfidentialClient: form.ConfidentialClient,
|
||||||
|
SkipSecondaryAuthorization: form.SkipSecondaryAuthorization,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
ctx.ServerError("UpdateOAuth2Application", err)
|
ctx.ServerError("UpdateOAuth2Application", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -455,13 +455,14 @@ func ToTopicResponse(topic *repo_model.Topic) *api.TopicResponse {
|
||||||
// ToOAuth2Application convert from auth.OAuth2Application to api.OAuth2Application
|
// ToOAuth2Application convert from auth.OAuth2Application to api.OAuth2Application
|
||||||
func ToOAuth2Application(app *auth.OAuth2Application) *api.OAuth2Application {
|
func ToOAuth2Application(app *auth.OAuth2Application) *api.OAuth2Application {
|
||||||
return &api.OAuth2Application{
|
return &api.OAuth2Application{
|
||||||
ID: app.ID,
|
ID: app.ID,
|
||||||
Name: app.Name,
|
Name: app.Name,
|
||||||
ClientID: app.ClientID,
|
ClientID: app.ClientID,
|
||||||
ClientSecret: app.ClientSecret,
|
ClientSecret: app.ClientSecret,
|
||||||
ConfidentialClient: app.ConfidentialClient,
|
ConfidentialClient: app.ConfidentialClient,
|
||||||
RedirectURIs: app.RedirectURIs,
|
SkipSecondaryAuthorization: app.SkipSecondaryAuthorization,
|
||||||
Created: app.CreatedUnix.AsTime(),
|
RedirectURIs: app.RedirectURIs,
|
||||||
|
Created: app.CreatedUnix.AsTime(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -365,9 +365,10 @@ func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) {
|
||||||
|
|
||||||
// EditOAuth2ApplicationForm form for editing oauth2 applications
|
// EditOAuth2ApplicationForm form for editing oauth2 applications
|
||||||
type EditOAuth2ApplicationForm struct {
|
type EditOAuth2ApplicationForm struct {
|
||||||
Name string `binding:"Required;MaxSize(255)" form:"application_name"`
|
Name string `binding:"Required;MaxSize(255)" form:"application_name"`
|
||||||
RedirectURIs string `binding:"Required" form:"redirect_uris"`
|
RedirectURIs string `binding:"Required" form:"redirect_uris"`
|
||||||
ConfidentialClient bool `form:"confidential_client"`
|
ConfidentialClient bool `form:"confidential_client"`
|
||||||
|
SkipSecondaryAuthorization bool `form:"skip_secondary_authorization"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
|
|
|
@ -19875,6 +19875,10 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"x-go-name": "RedirectURIs"
|
"x-go-name": "RedirectURIs"
|
||||||
|
},
|
||||||
|
"skip_secondary_authorization": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "SkipSecondaryAuthorization"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
@ -23002,6 +23006,10 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"x-go-name": "RedirectURIs"
|
"x-go-name": "RedirectURIs"
|
||||||
|
},
|
||||||
|
"skip_secondary_authorization": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "SkipSecondaryAuthorization"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
|
|
@ -44,7 +44,13 @@
|
||||||
<div class="field {{if .Err_ConfidentialClient}}error{{end}}">
|
<div class="field {{if .Err_ConfidentialClient}}error{{end}}">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
|
<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
|
||||||
<input type="checkbox" name="confidential_client" {{if .App.ConfidentialClient}}checked{{end}}>
|
<input class="disable-setting" type="checkbox" name="confidential_client" data-target="#skip-secondary-authorization" {{if .App.ConfidentialClient}}checked{{end}}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field {{if .Err_SkipSecondaryAuthorization}}error{{end}} {{if .App.ConfidentialClient}}disabled{{end}}" id="skip-secondary-authorization">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<label>{{ctx.Locale.Tr "settings.oauth2_skip_secondary_authorization"}}</label>
|
||||||
|
<input type="checkbox" name="skip_secondary_authorization" {{if .App.SkipSecondaryAuthorization}}checked{{end}}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="ui primary button">
|
<button class="ui primary button">
|
||||||
|
|
|
@ -64,7 +64,13 @@
|
||||||
<div class="field {{if .Err_ConfidentialClient}}error{{end}}">
|
<div class="field {{if .Err_ConfidentialClient}}error{{end}}">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
|
<label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
|
||||||
<input type="checkbox" name="confidential_client" checked>
|
<input class="disable-setting" type="checkbox" name="confidential_client" data-target="#skip-secondary-authorization" checked>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field {{if .Err_SkipSecondaryAuthorization}}error{{end}} disabled" id="skip-secondary-authorization">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<label>{{ctx.Locale.Tr "settings.oauth2_skip_secondary_authorization"}}</label>
|
||||||
|
<input type="checkbox" name="skip_secondary_authorization">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="ui primary button">
|
<button class="ui primary button">
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export function initOAuth2SettingsDisableCheckbox() {
|
||||||
|
for (const e of document.querySelectorAll('.disable-setting')) e.addEventListener('change', ({target}) => {
|
||||||
|
document.querySelector(e.getAttribute('data-target')).classList.toggle('disabled', target.checked);
|
||||||
|
});
|
||||||
|
}
|
|
@ -78,6 +78,7 @@ import {initDirAuto} from './modules/dirauto.ts';
|
||||||
import {initRepositorySearch} from './features/repo-search.ts';
|
import {initRepositorySearch} from './features/repo-search.ts';
|
||||||
import {initColorPickers} from './features/colorpicker.ts';
|
import {initColorPickers} from './features/colorpicker.ts';
|
||||||
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
|
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
|
||||||
|
import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
|
||||||
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
|
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
|
||||||
import {
|
import {
|
||||||
initFootLanguageMenu,
|
initFootLanguageMenu,
|
||||||
|
@ -225,5 +226,7 @@ onDomReady(() => {
|
||||||
initPdfViewer,
|
initPdfViewer,
|
||||||
initScopedAccessTokenCategories,
|
initScopedAccessTokenCategories,
|
||||||
initColorPickers,
|
initColorPickers,
|
||||||
|
|
||||||
|
initOAuth2SettingsDisableCheckbox,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue