Skip to content
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

Allow creation of OAuth2 applications for orgs #18084

Merged
merged 52 commits into from
Oct 9, 2022
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
840d1e0
Allow creation of OAuth2 applications for orgs
qwerty287 Dec 23, 2021
bdda6a1
Merge branch 'main' into org-oauth2
qwerty287 Dec 23, 2021
5dd629c
Merge branch 'main' into org-oauth2
qwerty287 Dec 24, 2021
e844523
Merge branch 'main' into org-oauth2
qwerty287 Jan 4, 2022
2e3bccf
Fix import
qwerty287 Jan 7, 2022
d52f718
Merge branch 'main' into org-oauth2
qwerty287 Jan 7, 2022
171701a
fmt
qwerty287 Jan 7, 2022
8bb9912
Merge branch 'main' into org-oauth2
qwerty287 Jan 22, 2022
4567ea9
Merge branch 'main' into org-oauth2
qwerty287 Feb 14, 2022
7e5c6dc
Merge branch 'main' into org-oauth2
qwerty287 Feb 19, 2022
5940d7f
Merge branch 'main' into org-oauth2
qwerty287 Mar 21, 2022
86e733c
Merge branch 'main' into org-oauth2
qwerty287 Mar 27, 2022
a8dfb7b
Merge branch 'org-oauth2' of github.com:qwerty287/gitea into org-oauth2
qwerty287 Mar 27, 2022
1f920df
Fix merge
qwerty287 Mar 27, 2022
3775a32
Merge branch 'main' into org-oauth2
qwerty287 Mar 27, 2022
e4fe3c6
Merge branch 'main' into org-oauth2
qwerty287 Apr 5, 2022
cb9c5aa
Merge branch 'main' into org-oauth2
qwerty287 Apr 15, 2022
2804d63
Merge branch 'main' into org-oauth2
qwerty287 Apr 28, 2022
4f6579e
Merge branch 'main' into org-oauth2
qwerty287 May 4, 2022
0d53655
Merge branch 'main' into org-oauth2
qwerty287 Jun 5, 2022
c04065b
Adapt refactors
qwerty287 Jun 5, 2022
97b795d
Merge branch 'main' into org-oauth2
qwerty287 Jun 5, 2022
443b1bb
Merge branch 'main' into org-oauth2
qwerty287 Jun 5, 2022
6d9650e
Merge branch 'main' into org-oauth2
qwerty287 Jun 16, 2022
0d76da1
Merge branch 'main' into org-oauth2
qwerty287 Jul 18, 2022
c1fd02a
Merge branch 'main' into org-oauth2
qwerty287 Aug 15, 2022
eb58018
Merge branch 'main' into org-oauth2
qwerty287 Aug 31, 2022
e59ecb4
Merge branch 'main' into org-oauth2
qwerty287 Sep 25, 2022
fe95171
Use `locale.Tr`
qwerty287 Sep 26, 2022
5805461
Merge branch 'main' into org-oauth2
qwerty287 Sep 26, 2022
09c2a7e
Merge branch 'main' into org-oauth2
qwerty287 Sep 27, 2022
dbdd8c5
Merge branch 'main' into org-oauth2
6543 Sep 28, 2022
599ef41
Merge branch 'main' into org-oauth2
6543 Sep 29, 2022
975a01d
Merge branch 'main' into org-oauth2
qwerty287 Sep 29, 2022
64b1b9b
Merge branch 'main' into org-oauth2
6543 Sep 29, 2022
48f9907
Merge branch 'main' into org-oauth2
qwerty287 Sep 29, 2022
cfa7bae
Merge branch 'main' into org-oauth2
qwerty287 Oct 2, 2022
dcfed4b
Merge branch 'main' into org-oauth2
qwerty287 Oct 3, 2022
16d6a90
Remove modal
qwerty287 Oct 3, 2022
4be12c4
Merge branch 'main' into org-oauth2
6543 Oct 3, 2022
20183ef
Merge branch 'main' into org-oauth2
qwerty287 Oct 4, 2022
4031fa8
Remove dupl code on tmpls
qwerty287 Oct 5, 2022
6facd1e
Fixes and move edit form to separate tmpl
qwerty287 Oct 5, 2022
cafd775
fix incent
qwerty287 Oct 5, 2022
57595d5
Merge branch 'main' into org-oauth2
qwerty287 Oct 5, 2022
280bcef
refactor
wxiaoguang Oct 6, 2022
95d7686
fix org application url
wxiaoguang Oct 6, 2022
c512332
fix links
wxiaoguang Oct 6, 2022
e385e92
fix lint
wxiaoguang Oct 6, 2022
6c4770d
Merge branch 'main' into org-oauth2
wxiaoguang Oct 6, 2022
dc778a4
Merge branch 'main' into org-oauth2
wxiaoguang Oct 7, 2022
f95470f
Merge branch 'main' into org-oauth2
wxiaoguang Oct 9, 2022
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
156 changes: 156 additions & 0 deletions routers/web/org/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
package org

import (
"fmt"
"net/http"
"net/url"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
Expand Down Expand Up @@ -38,6 +40,10 @@ const (
tplSettingsHooks base.TplName = "org/settings/hooks"
// tplSettingsLabels template path for render labels settings
tplSettingsLabels base.TplName = "org/settings/labels"
// tplSettingsLabels template path for render application settings
tplSettingsApplications base.TplName = "org/settings/applications"
// tplSettingsLabels template path for render application edit settings
tplSettingsEditApplication base.TplName = "org/settings/applications_edit"
)

// Settings render the main settings page
Expand Down Expand Up @@ -247,3 +253,153 @@ func Labels(ctx *context.Context) {
ctx.Data["LabelTemplates"] = repo_module.LabelTemplates
ctx.HTML(http.StatusOK, tplSettingsLabels)
}

// Applications render org applications page
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)
}

// ApplicationsPost response for adding an oauth2 application
func ApplicationsPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm)
ctx.Data["Title"] = ctx.Tr("settings.applications")
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsApplications"] = true

if ctx.HasError() {
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)
return
}

app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
Name: form.Name,
RedirectURIs: []string{form.RedirectURI},
UserID: ctx.Org.Organization.ID,
})
if err != nil {
ctx.ServerError("CreateOAuth2Application", err)
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.create_oauth2_application_success"))
ctx.HTML(http.StatusOK, tplSettingsEditApplication)
}

// EditApplication response for editing oauth2 application
func EditApplication(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.Org.Organization.ID {
ctx.NotFound("Application not found", nil)
return
}
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsApplications"] = true
ctx.Data["App"] = app
ctx.HTML(http.StatusOK, tplSettingsEditApplication)
}

// EditApplicationPost response for editing oauth2 application
func EditApplicationPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm)
ctx.Data["Title"] = ctx.Tr("settings.applications")
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsApplications"] = true

if ctx.HasError() {
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)
return
}
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.Org.Organization.ID,
}); err != nil {
ctx.ServerError("UpdateOAuth2Application", err)
return
}
ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success"))
ctx.HTML(http.StatusOK, tplSettingsEditApplication)
}

// ApplicationsRegenerateSecret handles the post request for regenerating the secret
func ApplicationsRegenerateSecret(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsApplications"] = true
ctx.Data["PageIsOrgSettings"] = 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.Org.Organization.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, tplSettingsEditApplication)
}

// DeleteApplication deletes the given oauth2 application
func DeleteApplication(ctx *context.Context) {
if err := auth.DeleteOAuth2Application(ctx.FormInt64("id"), ctx.Org.Organization.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": fmt.Sprintf("%s/org/%s/settings/applications", setting.AppSubURL, ctx.Org.Organization.Name),
})
}
16 changes: 16 additions & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.Combo("").Get(org.Applications).
Post(bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.ApplicationsPost)
m.Group("/{id}", func() {
m.Combo("").Get(org.EditApplication).Post(bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.EditApplicationPost)
m.Post("/regenerate_secret", org.ApplicationsRegenerateSecret)
m.Post("/delete", org.DeleteApplication)
})
}, func(ctx *context.Context) {
if !setting.OAuth2.Enable {
ctx.Error(http.StatusForbidden)
return
}
})

m.Group("/hooks", func() {
m.Get("", org.Webhooks)
Expand Down Expand Up @@ -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)
Expand Down
72 changes: 72 additions & 0 deletions templates/org/settings/applications.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{{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>
<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}}/{{$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}}/{{.ID}}/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 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}}" 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>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}
67 changes: 67 additions & 0 deletions templates/org/settings/applications_edit.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{{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.edit_oauth2_application"}}
</h4>
<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">
{{.locale.Tr "settings.oauth2_regenerate_secret_hint"}}
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/org/{{.Org.Name}}/settings/applications/{{.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}}/org/{{.Org.Name}}/settings/applications/{{.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>
</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" .}}
</div>
qwerty287 marked this conversation as resolved.
Show resolved Hide resolved
{{template "base/footer" .}}
5 changes: 5 additions & 0 deletions templates/org/settings/navbar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down