mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00:00 
			
		
		
		
	Allow creation of OAuth2 applications for orgs (#18084)
Adds the settings pages to create OAuth2 apps also to the org settings and allows to create apps for orgs. Refactoring: the oauth2 related templates are shared for instance-wide/org/user, and the backend code uses `OAuth2CommonHandlers` to share code for instance-wide/org/user. Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -225,7 +225,8 @@ func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error | ||||
|  | ||||
| func deleteOAuth2Application(ctx context.Context, id, userid int64) error { | ||||
| 	sess := db.GetEngine(ctx) | ||||
| 	if deleted, err := sess.Delete(&OAuth2Application{ID: id, UID: userid}); err != nil { | ||||
| 	// the userid could be 0 if the app is instance-wide | ||||
| 	if deleted, err := sess.Where(builder.Eq{"id": id, "uid": userid}).Delete(&OAuth2Application{}); err != nil { | ||||
| 		return err | ||||
| 	} else if deleted == 0 { | ||||
| 		return ErrOAuthApplicationNotFound{ID: id} | ||||
| @@ -476,7 +477,7 @@ func GetOAuth2GrantsByUserID(ctx context.Context, uid int64) ([]*OAuth2Grant, er | ||||
|  | ||||
| // RevokeOAuth2Grant deletes the grant with grantID and userID | ||||
| func RevokeOAuth2Grant(ctx context.Context, grantID, userID int64) error { | ||||
| 	_, err := db.DeleteByBean(ctx, &OAuth2Grant{ID: grantID, UserID: userID}) | ||||
| 	_, err := db.GetEngine(ctx).Where(builder.Eq{"id": grantID, "user_id": userID}).Delete(&OAuth2Grant{}) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										93
									
								
								routers/web/org/setting_oauth2.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								routers/web/org/setting_oauth2.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| // Copyright 2022 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package org | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/auth" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	user_setting "code.gitea.io/gitea/routers/web/user/setting" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	tplSettingsApplications         base.TplName = "org/settings/applications" | ||||
| 	tplSettingsOAuthApplicationEdit base.TplName = "org/settings/applications_oauth2_edit" | ||||
| ) | ||||
|  | ||||
| func newOAuth2CommonHandlers(org *context.Organization) *user_setting.OAuth2CommonHandlers { | ||||
| 	return &user_setting.OAuth2CommonHandlers{ | ||||
| 		OwnerID:            org.Organization.ID, | ||||
| 		BasePathList:       fmt.Sprintf("%s/org/%s/settings/applications", setting.AppSubURL, org.Organization.Name), | ||||
| 		BasePathEditPrefix: fmt.Sprintf("%s/org/%s/settings/applications/oauth2", setting.AppSubURL, org.Organization.Name), | ||||
| 		TplAppEdit:         tplSettingsOAuthApplicationEdit, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Applications render org applications page (for org, at the moment, there are only OAuth2 applications) | ||||
| func Applications(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings.applications") | ||||
| 	ctx.Data["PageIsOrgSettings"] = true | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, ctx.Org.Organization.ID) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetOAuth2ApplicationsByUserID", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["Applications"] = apps | ||||
|  | ||||
| 	ctx.HTML(http.StatusOK, tplSettingsApplications) | ||||
| } | ||||
|  | ||||
| // OAuthApplicationsPost response for adding an oauth2 application | ||||
| func OAuthApplicationsPost(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings.applications") | ||||
| 	ctx.Data["PageIsOrgSettings"] = true | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Org) | ||||
| 	oa.AddApp(ctx) | ||||
| } | ||||
|  | ||||
| // OAuth2ApplicationShow displays the given application | ||||
| func OAuth2ApplicationShow(ctx *context.Context) { | ||||
| 	ctx.Data["PageIsOrgSettings"] = true | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Org) | ||||
| 	oa.EditShow(ctx) | ||||
| } | ||||
|  | ||||
| // OAuth2ApplicationEdit response for editing oauth2 application | ||||
| func OAuth2ApplicationEdit(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings.applications") | ||||
| 	ctx.Data["PageIsOrgSettings"] = true | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Org) | ||||
| 	oa.EditSave(ctx) | ||||
| } | ||||
|  | ||||
| // OAuthApplicationsRegenerateSecret handles the post request for regenerating the secret | ||||
| func OAuthApplicationsRegenerateSecret(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | ||||
| 	ctx.Data["PageIsOrgSettings"] = true | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Org) | ||||
| 	oa.RegenerateSecret(ctx) | ||||
| } | ||||
|  | ||||
| // DeleteOAuth2Application deletes the given oauth2 application | ||||
| func DeleteOAuth2Application(ctx *context.Context) { | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Org) | ||||
| 	oa.DeleteApp(ctx) | ||||
| } | ||||
|  | ||||
| // TODO: revokes the grant with the given id | ||||
| @@ -5,79 +5,40 @@ | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/auth" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	tplSettingsOAuthApplications base.TplName = "user/settings/applications_oauth2_edit" | ||||
| 	tplSettingsOAuthApplicationEdit base.TplName = "user/settings/applications_oauth2_edit" | ||||
| ) | ||||
|  | ||||
| func newOAuth2CommonHandlers(userID int64) *OAuth2CommonHandlers { | ||||
| 	return &OAuth2CommonHandlers{ | ||||
| 		OwnerID:            userID, | ||||
| 		BasePathList:       setting.AppSubURL + "/user/settings/applications", | ||||
| 		BasePathEditPrefix: setting.AppSubURL + "/user/settings/applications/oauth2", | ||||
| 		TplAppEdit:         tplSettingsOAuthApplicationEdit, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // OAuthApplicationsPost response for adding a oauth2 application | ||||
| func OAuthApplicationsPost(ctx *context.Context) { | ||||
| 	form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm) | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	if ctx.HasError() { | ||||
| 		loadApplicationsData(ctx) | ||||
|  | ||||
| 		ctx.HTML(http.StatusOK, tplSettingsApplications) | ||||
| 		return | ||||
| 	} | ||||
| 	// TODO validate redirect URI | ||||
| 	app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{ | ||||
| 		Name:         form.Name, | ||||
| 		RedirectURIs: []string{form.RedirectURI}, | ||||
| 		UserID:       ctx.Doer.ID, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("CreateOAuth2Application", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.create_oauth2_application_success")) | ||||
| 	ctx.Data["App"] = app | ||||
| 	ctx.Data["ClientSecret"], err = app.GenerateClientSecret() | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GenerateClientSecret", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.HTML(http.StatusOK, tplSettingsOAuthApplications) | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Doer.ID) | ||||
| 	oa.AddApp(ctx) | ||||
| } | ||||
|  | ||||
| // OAuthApplicationsEdit response for editing oauth2 application | ||||
| func OAuthApplicationsEdit(ctx *context.Context) { | ||||
| 	form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm) | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	if ctx.HasError() { | ||||
| 		loadApplicationsData(ctx) | ||||
|  | ||||
| 		ctx.HTML(http.StatusOK, tplSettingsApplications) | ||||
| 		return | ||||
| 	} | ||||
| 	// TODO validate redirect URI | ||||
| 	var err error | ||||
| 	if ctx.Data["App"], err = auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{ | ||||
| 		ID:           ctx.ParamsInt64("id"), | ||||
| 		Name:         form.Name, | ||||
| 		RedirectURIs: []string{form.RedirectURI}, | ||||
| 		UserID:       ctx.Doer.ID, | ||||
| 	}); err != nil { | ||||
| 		ctx.ServerError("UpdateOAuth2Application", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success")) | ||||
| 	ctx.HTML(http.StatusOK, tplSettingsOAuthApplications) | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Doer.ID) | ||||
| 	oa.EditSave(ctx) | ||||
| } | ||||
|  | ||||
| // OAuthApplicationsRegenerateSecret handles the post request for regenerating the secret | ||||
| @@ -85,75 +46,24 @@ func OAuthApplicationsRegenerateSecret(ctx *context.Context) { | ||||
| 	ctx.Data["Title"] = ctx.Tr("settings") | ||||
| 	ctx.Data["PageIsSettingsApplications"] = true | ||||
|  | ||||
| 	app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id")) | ||||
| 	if err != nil { | ||||
| 		if auth.IsErrOAuthApplicationNotFound(err) { | ||||
| 			ctx.NotFound("Application not found", err) | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.ServerError("GetOAuth2ApplicationByID", err) | ||||
| 		return | ||||
| 	} | ||||
| 	if app.UID != ctx.Doer.ID { | ||||
| 		ctx.NotFound("Application not found", nil) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["App"] = app | ||||
| 	ctx.Data["ClientSecret"], err = app.GenerateClientSecret() | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GenerateClientSecret", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success")) | ||||
| 	ctx.HTML(http.StatusOK, tplSettingsOAuthApplications) | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Doer.ID) | ||||
| 	oa.RegenerateSecret(ctx) | ||||
| } | ||||
|  | ||||
| // OAuth2ApplicationShow displays the given application | ||||
| func OAuth2ApplicationShow(ctx *context.Context) { | ||||
| 	app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id")) | ||||
| 	if err != nil { | ||||
| 		if auth.IsErrOAuthApplicationNotFound(err) { | ||||
| 			ctx.NotFound("Application not found", err) | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.ServerError("GetOAuth2ApplicationByID", err) | ||||
| 		return | ||||
| 	} | ||||
| 	if app.UID != ctx.Doer.ID { | ||||
| 		ctx.NotFound("Application not found", nil) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["App"] = app | ||||
| 	ctx.HTML(http.StatusOK, tplSettingsOAuthApplications) | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Doer.ID) | ||||
| 	oa.EditShow(ctx) | ||||
| } | ||||
|  | ||||
| // DeleteOAuth2Application deletes the given oauth2 application | ||||
| func DeleteOAuth2Application(ctx *context.Context) { | ||||
| 	if err := auth.DeleteOAuth2Application(ctx.FormInt64("id"), ctx.Doer.ID); err != nil { | ||||
| 		ctx.ServerError("DeleteOAuth2Application", err) | ||||
| 		return | ||||
| 	} | ||||
| 	log.Trace("OAuth2 Application deleted: %s", ctx.Doer.Name) | ||||
|  | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.remove_oauth2_application_success")) | ||||
| 	ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| 		"redirect": setting.AppSubURL + "/user/settings/applications", | ||||
| 	}) | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Doer.ID) | ||||
| 	oa.DeleteApp(ctx) | ||||
| } | ||||
|  | ||||
| // RevokeOAuth2Grant revokes the grant with the given id | ||||
| func RevokeOAuth2Grant(ctx *context.Context) { | ||||
| 	if ctx.Doer.ID == 0 || ctx.FormInt64("id") == 0 { | ||||
| 		ctx.ServerError("RevokeOAuth2Grant", fmt.Errorf("user id or grant id is zero")) | ||||
| 		return | ||||
| 	} | ||||
| 	if err := auth.RevokeOAuth2Grant(ctx, ctx.FormInt64("id"), ctx.Doer.ID); err != nil { | ||||
| 		ctx.ServerError("RevokeOAuth2Grant", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.revoke_oauth2_grant_success")) | ||||
| 	ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| 		"redirect": setting.AppSubURL + "/user/settings/applications", | ||||
| 	}) | ||||
| 	oa := newOAuth2CommonHandlers(ctx.Doer.ID) | ||||
| 	oa.RevokeGrant(ctx) | ||||
| } | ||||
|   | ||||
							
								
								
									
										150
									
								
								routers/web/user/setting/oauth2_common.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								routers/web/user/setting/oauth2_common.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package setting | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/auth" | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| ) | ||||
|  | ||||
| type OAuth2CommonHandlers struct { | ||||
| 	OwnerID            int64        // 0 for instance-wide, otherwise OrgID or UserID | ||||
| 	BasePathList       string       // the base URL for the application list page, eg: "/user/setting/applications" | ||||
| 	BasePathEditPrefix string       // the base URL for the application edit page, will be appended with app id, eg: "/user/setting/applications/oauth2" | ||||
| 	TplAppEdit         base.TplName // the template for the application edit page | ||||
| } | ||||
|  | ||||
| func (oa *OAuth2CommonHandlers) renderEditPage(ctx *context.Context) { | ||||
| 	app := ctx.Data["App"].(*auth.OAuth2Application) | ||||
| 	ctx.Data["FormActionPath"] = fmt.Sprintf("%s/%d", oa.BasePathEditPrefix, app.ID) | ||||
| 	ctx.HTML(http.StatusOK, oa.TplAppEdit) | ||||
| } | ||||
|  | ||||
| // AddApp adds an oauth2 application | ||||
| func (oa *OAuth2CommonHandlers) AddApp(ctx *context.Context) { | ||||
| 	form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm) | ||||
| 	if ctx.HasError() { | ||||
| 		// go to the application list page | ||||
| 		ctx.Redirect(oa.BasePathList) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// TODO validate redirect URI | ||||
| 	app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{ | ||||
| 		Name:         form.Name, | ||||
| 		RedirectURIs: []string{form.RedirectURI}, | ||||
| 		UserID:       oa.OwnerID, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("CreateOAuth2Application", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// render the edit page with secret | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.create_oauth2_application_success"), true) | ||||
| 	ctx.Data["App"] = app | ||||
| 	ctx.Data["ClientSecret"], err = app.GenerateClientSecret() | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GenerateClientSecret", err) | ||||
| 		return | ||||
| 	} | ||||
| 	oa.renderEditPage(ctx) | ||||
| } | ||||
|  | ||||
| // EditShow displays the given application | ||||
| func (oa *OAuth2CommonHandlers) EditShow(ctx *context.Context) { | ||||
| 	app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id")) | ||||
| 	if err != nil { | ||||
| 		if auth.IsErrOAuthApplicationNotFound(err) { | ||||
| 			ctx.NotFound("Application not found", err) | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.ServerError("GetOAuth2ApplicationByID", err) | ||||
| 		return | ||||
| 	} | ||||
| 	if app.UID != oa.OwnerID { | ||||
| 		ctx.NotFound("Application not found", nil) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["App"] = app | ||||
| 	oa.renderEditPage(ctx) | ||||
| } | ||||
|  | ||||
| // EditSave saves the oauth2 application | ||||
| func (oa *OAuth2CommonHandlers) EditSave(ctx *context.Context) { | ||||
| 	form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm) | ||||
|  | ||||
| 	if ctx.HasError() { | ||||
| 		oa.renderEditPage(ctx) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// TODO validate redirect URI | ||||
| 	var err error | ||||
| 	if ctx.Data["App"], err = auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{ | ||||
| 		ID:           ctx.ParamsInt64("id"), | ||||
| 		Name:         form.Name, | ||||
| 		RedirectURIs: []string{form.RedirectURI}, | ||||
| 		UserID:       oa.OwnerID, | ||||
| 	}); err != nil { | ||||
| 		ctx.ServerError("UpdateOAuth2Application", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success")) | ||||
| 	ctx.Redirect(oa.BasePathList) | ||||
| } | ||||
|  | ||||
| // RegenerateSecret regenerates the secret | ||||
| func (oa *OAuth2CommonHandlers) RegenerateSecret(ctx *context.Context) { | ||||
| 	app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id")) | ||||
| 	if err != nil { | ||||
| 		if auth.IsErrOAuthApplicationNotFound(err) { | ||||
| 			ctx.NotFound("Application not found", err) | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.ServerError("GetOAuth2ApplicationByID", err) | ||||
| 		return | ||||
| 	} | ||||
| 	if app.UID != oa.OwnerID { | ||||
| 		ctx.NotFound("Application not found", nil) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["App"] = app | ||||
| 	ctx.Data["ClientSecret"], err = app.GenerateClientSecret() | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GenerateClientSecret", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success"), true) | ||||
| 	oa.renderEditPage(ctx) | ||||
| } | ||||
|  | ||||
| // DeleteApp deletes the given oauth2 application | ||||
| func (oa *OAuth2CommonHandlers) DeleteApp(ctx *context.Context) { | ||||
| 	if err := auth.DeleteOAuth2Application(ctx.ParamsInt64("id"), oa.OwnerID); err != nil { | ||||
| 		ctx.ServerError("DeleteOAuth2Application", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.remove_oauth2_application_success")) | ||||
| 	ctx.JSON(http.StatusOK, map[string]interface{}{"redirect": oa.BasePathList}) | ||||
| } | ||||
|  | ||||
| // RevokeGrant revokes the grant | ||||
| func (oa *OAuth2CommonHandlers) RevokeGrant(ctx *context.Context) { | ||||
| 	if err := auth.RevokeOAuth2Grant(ctx, ctx.ParamsInt64("grantId"), oa.OwnerID); err != nil { | ||||
| 		ctx.ServerError("RevokeOAuth2Grant", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ctx.Flash.Success(ctx.Tr("settings.revoke_oauth2_grant_success")) | ||||
| 	ctx.JSON(http.StatusOK, map[string]interface{}{"redirect": oa.BasePathList}) | ||||
| } | ||||
| @@ -427,8 +427,8 @@ func RegisterRoutes(m *web.Route) { | ||||
| 			m.Post("/{id}", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsEdit) | ||||
| 			m.Post("/{id}/regenerate_secret", user_setting.OAuthApplicationsRegenerateSecret) | ||||
| 			m.Post("", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsPost) | ||||
| 			m.Post("/delete", user_setting.DeleteOAuth2Application) | ||||
| 			m.Post("/revoke", user_setting.RevokeOAuth2Grant) | ||||
| 			m.Post("/{id}/delete", user_setting.DeleteOAuth2Application) | ||||
| 			m.Post("/{id}/revoke/{grantId}", user_setting.RevokeOAuth2Grant) | ||||
| 		}) | ||||
| 		m.Combo("/applications").Get(user_setting.Applications). | ||||
| 			Post(bindIgnErr(forms.NewAccessTokenForm{}), user_setting.ApplicationsPost) | ||||
| @@ -662,6 +662,20 @@ func RegisterRoutes(m *web.Route) { | ||||
| 					Post(bindIgnErr(forms.UpdateOrgSettingForm{}), org.SettingsPost) | ||||
| 				m.Post("/avatar", bindIgnErr(forms.AvatarForm{}), org.SettingsAvatar) | ||||
| 				m.Post("/avatar/delete", org.SettingsDeleteAvatar) | ||||
| 				m.Group("/applications", func() { | ||||
| 					m.Get("", org.Applications) | ||||
| 					m.Post("/oauth2", bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.OAuthApplicationsPost) | ||||
| 					m.Group("/oauth2/{id}", func() { | ||||
| 						m.Combo("").Get(org.OAuth2ApplicationShow).Post(bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.OAuth2ApplicationEdit) | ||||
| 						m.Post("/regenerate_secret", org.OAuthApplicationsRegenerateSecret) | ||||
| 						m.Post("/delete", org.DeleteOAuth2Application) | ||||
| 					}) | ||||
| 				}, func(ctx *context.Context) { | ||||
| 					if !setting.OAuth2.Enable { | ||||
| 						ctx.Error(http.StatusForbidden) | ||||
| 						return | ||||
| 					} | ||||
| 				}) | ||||
|  | ||||
| 				m.Group("/hooks", func() { | ||||
| 					m.Get("", org.Webhooks) | ||||
| @@ -702,6 +716,8 @@ func RegisterRoutes(m *web.Route) { | ||||
| 				}) | ||||
|  | ||||
| 				m.Route("/delete", "GET,POST", org.SettingsDelete) | ||||
| 			}, func(ctx *context.Context) { | ||||
| 				ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable | ||||
| 			}) | ||||
| 		}, context.OrgAssignment(true, true)) | ||||
| 	}, reqSignIn) | ||||
|   | ||||
							
								
								
									
										18
									
								
								templates/org/settings/applications.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								templates/org/settings/applications.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| {{template "base/head" .}} | ||||
| <div class="page-content organization settings options"> | ||||
| 	{{template "org/header" .}} | ||||
| 	<div class="ui container"> | ||||
| 		<div class="ui grid"> | ||||
| 			{{template "org/settings/navbar" .}} | ||||
| 			<div class="twelve wide column content"> | ||||
| 				{{template "base/alert" .}} | ||||
| 				<h4 class="ui top attached header"> | ||||
| 					{{.locale.Tr "settings.applications"}} | ||||
| 				</h4> | ||||
|  | ||||
| 				{{template "user/settings/applications_oauth2_list" .}} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
| {{template "base/footer" .}} | ||||
							
								
								
									
										7
									
								
								templates/org/settings/applications_oauth2_edit.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								templates/org/settings/applications_oauth2_edit.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| {{template "base/head" .}} | ||||
| <div class="page-content organization settings options"> | ||||
| 	{{template "org/header" .}} | ||||
|  | ||||
| 	{{template "user/settings/applications_oauth2_edit_form" .}} | ||||
| </div> | ||||
| {{template "base/footer" .}} | ||||
| @@ -12,6 +12,11 @@ | ||||
| 		<a class="{{if .PageIsOrgSettingsLabels}}active{{end}} item" href="{{.OrgLink}}/settings/labels"> | ||||
| 			{{.locale.Tr "repo.labels"}} | ||||
| 		</a> | ||||
| 		{{if .EnableOAuth2}} | ||||
| 		<a class="{{if .PageIsSettingsApplications}}active{{end}} item" href="{{.OrgLink}}/settings/applications"> | ||||
| 			{{.locale.Tr "settings.applications"}} | ||||
| 		</a> | ||||
| 		{{end}} | ||||
| 		<a class="{{if .PageIsSettingsDelete}}active{{end}} item" href="{{.OrgLink}}/settings/delete"> | ||||
| 			{{.locale.Tr "org.settings.delete"}} | ||||
| 		</a> | ||||
|   | ||||
| @@ -1,59 +1,6 @@ | ||||
| <h4 class="ui top attached header"> | ||||
| 	{{.locale.Tr "settings.manage_oauth2_applications"}} | ||||
| </h4> | ||||
| <div class="ui attached segment"> | ||||
| 	<div class="ui key list"> | ||||
| 		<div class="item"> | ||||
| 			{{.locale.Tr "settings.oauth2_application_create_description"}} | ||||
| 		</div> | ||||
| 		{{range $app := .Applications}} | ||||
| 			<div class="item"> | ||||
| 				<div class="right floated content"> | ||||
| 					<a href="{{$.Link}}/oauth2/{{$app.ID}}" class="ui primary tiny button"> | ||||
| 						{{svg "octicon-pencil" 16 "mr-2"}} | ||||
| 						{{$.locale.Tr "settings.oauth2_application_edit"}} | ||||
| 					</a> | ||||
| 					<button class="ui red tiny button delete-button" data-modal-id="remove-gitea-oauth2-application" | ||||
| 							data-url="{{AppSubUrl}}/user/settings/applications/oauth2/delete" | ||||
| 							data-id="{{$app.ID}}"> | ||||
| 						{{svg "octicon-trash" 16 "mr-2"}} | ||||
| 						{{$.locale.Tr "settings.delete_key"}} | ||||
| 					</button> | ||||
| 				</div> | ||||
| 				<div class="content"> | ||||
| 					<strong>{{$app.Name}}</strong> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		{{end}} | ||||
| 	</div> | ||||
| </div> | ||||
| <div class="ui attached bottom segment"> | ||||
| 	<h5 class="ui top header"> | ||||
| 		{{.locale.Tr "settings.create_oauth2_application"}} | ||||
| 	</h5> | ||||
| 	<form class="ui form ignore-dirty" action="{{.Link}}/oauth2" method="post"> | ||||
| 		{{.CsrfTokenHtml}} | ||||
| 		<div class="field {{if .Err_AppName}}error{{end}}"> | ||||
| 			<label for="application-name">{{.locale.Tr "settings.oauth2_application_name"}}</label> | ||||
| 			<input id="application-name" name="application_name" value="{{.application_name}}" required> | ||||
| 		</div> | ||||
| 		<div class="field {{if .Err_RedirectURI}}error{{end}}"> | ||||
| 			<label for="redirect-uri">{{.locale.Tr "settings.oauth2_redirect_uri"}}</label> | ||||
| 			<input type="url" name="redirect_uri" id="redirect-uri"> | ||||
| 		</div> | ||||
| 		<button class="ui green button"> | ||||
| 			{{.locale.Tr "settings.create_oauth2_application_button"}} | ||||
| 		</button> | ||||
| 	</form> | ||||
| </div> | ||||
|  | ||||
| <div class="ui small basic delete modal" id="remove-gitea-oauth2-application"> | ||||
| 	<div class="ui icon header"> | ||||
| 		{{svg "octicon-trash"}} | ||||
| 		{{.locale.Tr "settings.remove_oauth2_application"}} | ||||
| 	</div> | ||||
| 	<div class="content"> | ||||
| 		<p>{{.locale.Tr "settings.oauth2_application_remove_description"}}</p> | ||||
| 	</div> | ||||
| 	{{template "base/delete_modal_actions" .}} | ||||
| </div> | ||||
| {{template "user/settings/applications_oauth2_list" .}} | ||||
|  | ||||
|   | ||||
| @@ -1,68 +1,8 @@ | ||||
| {{template "base/head" .}} | ||||
| <div class="page-content user settings applications"> | ||||
| 	{{template "user/settings/navbar" .}} | ||||
| 	<div class="ui container"> | ||||
| 		{{template "base/alert" .}} | ||||
| 		<h4 class="ui top attached header"> | ||||
| 			{{.locale.Tr "settings.edit_oauth2_application"}} | ||||
| 		</h4> | ||||
| 		<div class="ui attached segment"> | ||||
| 			<p>{{.locale.Tr "settings.oauth2_application_create_description"}}</p> | ||||
| 		</div> | ||||
| 		<div class="ui attached segment form ignore-dirty"> | ||||
| 			{{.CsrfTokenHtml}} | ||||
| 			<div class="field"> | ||||
| 				<label for="client-id">{{.locale.Tr "settings.oauth2_client_id"}}</label> | ||||
| 				<input id="client-id" readonly value="{{.App.ClientID}}"> | ||||
| 			</div> | ||||
| 			{{if .ClientSecret}} | ||||
| 				<div class="field"> | ||||
| 					<label for="client-secret">{{.locale.Tr "settings.oauth2_client_secret"}}</label> | ||||
| 					<input id="client-secret" type="text" readonly value="{{.ClientSecret}}"> | ||||
| 				</div> | ||||
| 			{{else}} | ||||
| 				<div class="field"> | ||||
| 					<label for="client-secret">{{.locale.Tr "settings.oauth2_client_secret"}}</label> | ||||
| 					<input id="client-secret" type="password" readonly value="averysecuresecret"> | ||||
| 				</div> | ||||
| 			{{end}} | ||||
| 			<div class="item"> | ||||
| 				<!-- TODO add regenerate secret functionality */ --> | ||||
| 				{{.locale.Tr "settings.oauth2_regenerate_secret_hint"}} | ||||
| 				<form class="ui form ignore-dirty" action="{{AppSubUrl}}/user/settings/applications/oauth2/{{.App.ID}}/regenerate_secret" method="post"> | ||||
| 					{{.CsrfTokenHtml}} | ||||
| 					<a href="#" onclick="event.target.parentNode.submit()">{{.locale.Tr "settings.oauth2_regenerate_secret"}}</a> | ||||
| 				</form> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="ui attached bottom segment"> | ||||
| 			<form class="ui form ignore-dirty" action="{{AppSubUrl}}/user/settings/applications/oauth2/{{.App.ID}}" method="post"> | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				<div class="field {{if .Err_AppName}}error{{end}}"> | ||||
| 					<label for="application-name">{{.locale.Tr "settings.oauth2_application_name"}}</label> | ||||
| 					<input id="application-name" value="{{.App.Name}}" name="application_name" required> | ||||
| 				</div> | ||||
| 				<div class="field {{if .Err_RedirectURI}}error{{end}}"> | ||||
| 					<label for="redirect-uri">{{.locale.Tr "settings.oauth2_redirect_uri"}}</label> | ||||
| 					<input type="url" name="redirect_uri" value="{{.App.PrimaryRedirectURI}}" id="redirect-uri"> | ||||
| 				</div> | ||||
| 				<button class="ui green button"> | ||||
| 					{{.locale.Tr "settings.save_application"}} | ||||
| 				</button> | ||||
| 			</form> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
|  | ||||
| <div class="ui small basic delete modal" id="delete-oauth2-application"> | ||||
| 	<div class="ui icon header"> | ||||
| 		{{svg "octicon-trash"}} | ||||
| 		{{.locale.Tr "settings.remove_oauth2_application"}} | ||||
| 	</div> | ||||
| 	<div class="content"> | ||||
| 		<p>{{.locale.Tr "settings.remove_oauth2_application_desc"}}</p> | ||||
| 	</div> | ||||
| 	{{template "base/delete_modal_actions" .}} | ||||
| 	{{template "user/settings/applications_oauth2_edit_form" .}} | ||||
| </div> | ||||
|  | ||||
| {{template "base/footer" .}} | ||||
|   | ||||
							
								
								
									
										51
									
								
								templates/user/settings/applications_oauth2_edit_form.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								templates/user/settings/applications_oauth2_edit_form.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <div class="ui container"> | ||||
| 	{{template "base/alert" .}} | ||||
| 	<h4 class="ui top attached header"> | ||||
| 		{{.locale.Tr "settings.edit_oauth2_application"}} | ||||
| 	</h4> | ||||
| 	<div class="ui attached segment"> | ||||
| 		<p>{{.locale.Tr "settings.oauth2_application_create_description"}}</p> | ||||
| 	</div> | ||||
| 	<div class="ui attached segment form ignore-dirty"> | ||||
| 		{{.CsrfTokenHtml}} | ||||
| 		<div class="field"> | ||||
| 			<label for="client-id">{{.locale.Tr "settings.oauth2_client_id"}}</label> | ||||
| 			<input id="client-id" readonly value="{{.App.ClientID}}"> | ||||
| 		</div> | ||||
| 		{{if .ClientSecret}} | ||||
| 			<div class="field"> | ||||
| 				<label for="client-secret">{{.locale.Tr "settings.oauth2_client_secret"}}</label> | ||||
| 				<input id="client-secret" type="text" readonly value="{{.ClientSecret}}"> | ||||
| 			</div> | ||||
| 		{{else}} | ||||
| 			<div class="field"> | ||||
| 				<label for="client-secret">{{.locale.Tr "settings.oauth2_client_secret"}}</label> | ||||
| 				<input id="client-secret" type="password" readonly value="averysecuresecret"> | ||||
| 			</div> | ||||
| 		{{end}} | ||||
| 		<div class="item"> | ||||
| 			<!-- TODO add regenerate secret functionality */ --> | ||||
| 			<form class="ui form ignore-dirty" action="{{.FormActionPath}}/regenerate_secret" method="post"> | ||||
| 				{{.CsrfTokenHtml}} | ||||
| 				{{.locale.Tr "settings.oauth2_regenerate_secret_hint"}} | ||||
| 				<button class="ui tertiary button" type="submit">{{.locale.Tr "settings.oauth2_regenerate_secret"}}</button> | ||||
| 			</form> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="ui attached bottom segment"> | ||||
| 		<form class="ui form ignore-dirty" action="{{.FormActionPath}}" method="post"> | ||||
| 			{{.CsrfTokenHtml}} | ||||
| 			<div class="field {{if .Err_AppName}}error{{end}}"> | ||||
| 				<label for="application-name">{{.locale.Tr "settings.oauth2_application_name"}}</label> | ||||
| 				<input id="application-name" value="{{.App.Name}}" name="application_name" required> | ||||
| 			</div> | ||||
| 			<div class="field {{if .Err_RedirectURI}}error{{end}}"> | ||||
| 				<label for="redirect-uri">{{.locale.Tr "settings.oauth2_redirect_uri"}}</label> | ||||
| 				<input type="url" name="redirect_uri" value="{{.App.PrimaryRedirectURI}}" id="redirect-uri"> | ||||
| 			</div> | ||||
| 			<button class="ui green button"> | ||||
| 				{{.locale.Tr "settings.save_application"}} | ||||
| 			</button> | ||||
| 		</form> | ||||
| 	</div> | ||||
| </div> | ||||
							
								
								
									
										55
									
								
								templates/user/settings/applications_oauth2_list.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								templates/user/settings/applications_oauth2_list.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <div class="ui attached segment"> | ||||
| 	<div class="ui key list"> | ||||
| 		<div class="item"> | ||||
| 			{{.locale.Tr "settings.oauth2_application_create_description"}} | ||||
| 		</div> | ||||
| 		{{range $app := .Applications}} | ||||
| 			<div class="item"> | ||||
| 				<div class="right floated content"> | ||||
| 					<a href="{{$.Link}}/oauth2/{{$app.ID}}" class="ui primary tiny button"> | ||||
| 						{{svg "octicon-pencil" 16 "mr-2"}} | ||||
| 						{{$.locale.Tr "settings.oauth2_application_edit"}} | ||||
| 					</a> | ||||
| 					<button class="ui red tiny button delete-button" data-modal-id="remove-gitea-oauth2-application" | ||||
| 							data-url="{{$.Link}}/oauth2/{{$app.ID}}/delete"> | ||||
| 						{{svg "octicon-trash" 16 "mr-2"}} | ||||
| 						{{$.locale.Tr "settings.delete_key"}} | ||||
| 					</button> | ||||
| 				</div> | ||||
| 				<div class="content"> | ||||
| 					<strong>{{$app.Name}}</strong> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		{{end}} | ||||
| 	</div> | ||||
| </div> | ||||
| <div class="ui attached bottom segment"> | ||||
| 	<h5 class="ui top header"> | ||||
| 		{{.locale.Tr "settings.create_oauth2_application"}} | ||||
| 	</h5> | ||||
| 	<form class="ui form ignore-dirty" action="{{.Link}}/oauth2" method="post"> | ||||
| 		{{.CsrfTokenHtml}} | ||||
| 		<div class="field {{if .Err_AppName}}error{{end}}"> | ||||
| 			<label for="application-name">{{.locale.Tr "settings.oauth2_application_name"}}</label> | ||||
| 			<input id="application-name" name="application_name" value="{{.application_name}}" required> | ||||
| 		</div> | ||||
| 		<div class="field {{if .Err_RedirectURI}}error{{end}}"> | ||||
| 			<label for="redirect-uri">{{.locale.Tr "settings.oauth2_redirect_uri"}}</label> | ||||
| 			<input type="url" name="redirect_uri" id="redirect-uri"> | ||||
| 		</div> | ||||
| 		<button class="ui green button"> | ||||
| 			{{.locale.Tr "settings.create_oauth2_application_button"}} | ||||
| 		</button> | ||||
| 	</form> | ||||
| </div> | ||||
|  | ||||
| <div class="ui small basic delete modal" id="remove-gitea-oauth2-application"> | ||||
| 	<div class="ui icon header"> | ||||
| 		{{svg "octicon-trash"}} | ||||
| 		{{.locale.Tr "settings.remove_oauth2_application"}} | ||||
| 	</div> | ||||
| 	<div class="content"> | ||||
| 		<p>{{.locale.Tr "settings.oauth2_application_remove_description"}}</p> | ||||
| 	</div> | ||||
| 	{{template "base/delete_modal_actions" .}} | ||||
| </div> | ||||
| @@ -10,8 +10,7 @@ | ||||
| 			<div class="item"> | ||||
| 				<div class="right floated content"> | ||||
| 					<button class="ui red tiny button delete-button" data-modal-id="revoke-gitea-oauth2-grant" | ||||
| 							data-url="{{AppSubUrl}}/user/settings/applications/oauth2/revoke" | ||||
| 							data-id="{{$grant.ID}}"> | ||||
| 							data-url="{{AppSubUrl}}/user/settings/applications/oauth2/{{$grant.ApplicationID}}/revoke/{{$grant.ID}}"> | ||||
| 						{{$.locale.Tr "settings.revoke_key"}} | ||||
| 					</button> | ||||
| 				</div> | ||||
|   | ||||
| @@ -1804,7 +1804,9 @@ a.ui.label:hover { | ||||
|   border: 1px solid var(--color-light-border); | ||||
|   color: var(--color-text); | ||||
| } | ||||
|  | ||||
| .ui.tertiary.button { | ||||
|   border: none; | ||||
| } | ||||
| .page-content .ui.button { | ||||
|   box-shadow: none !important; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user