Skip to content

Commit 7a4c29c

Browse files
jonasfranzlunny
authored andcommitted
OAuth2 Grant UI (#6625)
* Add oauth2 grants ui Signed-off-by: Jonas Franz <[email protected]> * Add delete functionality Add translations Signed-off-by: Jonas Franz <[email protected]> * Fix unit tests Signed-off-by: Jonas Franz <[email protected]> * Fix unit tests Signed-off-by: Jonas Franz <[email protected]> * Refactor DeleteOAuth2Grant Use results.Close() Signed-off-by: Jonas Franz <[email protected]> * Refactor DeleteOAuth2Grant (again) Signed-off-by: Jonas Franz <[email protected]> * Check if user ID is zero Signed-off-by: Jonas Franz <[email protected]> * Check if grant ID is zero Signed-off-by: Jonas Franz <[email protected]>
1 parent 3454836 commit 7a4c29c

File tree

8 files changed

+140
-6
lines changed

8 files changed

+140
-6
lines changed

models/oauth2_application.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,13 @@ func getOAuth2AuthorizationByCode(e Engine, code string) (auth *OAuth2Authorizat
340340

341341
// OAuth2Grant represents the permission of an user for a specifc application to access resources
342342
type OAuth2Grant struct {
343-
ID int64 `xorm:"pk autoincr"`
344-
UserID int64 `xorm:"INDEX unique(user_application)"`
345-
ApplicationID int64 `xorm:"INDEX unique(user_application)"`
346-
Counter int64 `xorm:"NOT NULL DEFAULT 1"`
347-
CreatedUnix util.TimeStamp `xorm:"created"`
348-
UpdatedUnix util.TimeStamp `xorm:"updated"`
343+
ID int64 `xorm:"pk autoincr"`
344+
UserID int64 `xorm:"INDEX unique(user_application)"`
345+
Application *OAuth2Application `xorm:"-"`
346+
ApplicationID int64 `xorm:"INDEX unique(user_application)"`
347+
Counter int64 `xorm:"NOT NULL DEFAULT 1"`
348+
CreatedUnix util.TimeStamp `xorm:"created"`
349+
UpdatedUnix util.TimeStamp `xorm:"updated"`
349350
}
350351

351352
// TableName sets the table name to `oauth2_grant`
@@ -410,6 +411,48 @@ func getOAuth2GrantByID(e Engine, id int64) (grant *OAuth2Grant, err error) {
410411
return
411412
}
412413

414+
// GetOAuth2GrantsByUserID lists all grants of a certain user
415+
func GetOAuth2GrantsByUserID(uid int64) ([]*OAuth2Grant, error) {
416+
return getOAuth2GrantsByUserID(x, uid)
417+
}
418+
419+
func getOAuth2GrantsByUserID(e Engine, uid int64) ([]*OAuth2Grant, error) {
420+
type joinedOAuth2Grant struct {
421+
Grant *OAuth2Grant `xorm:"extends"`
422+
Application *OAuth2Application `xorm:"extends"`
423+
}
424+
var results *xorm.Rows
425+
var err error
426+
if results, err = e.
427+
Table("oauth2_grant").
428+
Where("user_id = ?", uid).
429+
Join("INNER", "oauth2_application", "application_id = oauth2_application.id").
430+
Rows(new(joinedOAuth2Grant)); err != nil {
431+
return nil, err
432+
}
433+
defer results.Close()
434+
grants := make([]*OAuth2Grant, 0)
435+
for results.Next() {
436+
joinedGrant := new(joinedOAuth2Grant)
437+
if err := results.Scan(joinedGrant); err != nil {
438+
return nil, err
439+
}
440+
joinedGrant.Grant.Application = joinedGrant.Application
441+
grants = append(grants, joinedGrant.Grant)
442+
}
443+
return grants, nil
444+
}
445+
446+
// RevokeOAuth2Grant deletes the grant with grantID and userID
447+
func RevokeOAuth2Grant(grantID, userID int64) error {
448+
return revokeOAuth2Grant(x, grantID, userID)
449+
}
450+
451+
func revokeOAuth2Grant(e Engine, grantID, userID int64) error {
452+
_, err := e.Delete(&OAuth2Grant{ID: grantID, UserID: userID})
453+
return err
454+
}
455+
413456
//////////////////////////////////////////////////////////////
414457

415458
// OAuth2TokenType represents the type of token for an oauth application

models/oauth2_application_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,25 @@ func TestOAuth2Grant_TableName(t *testing.T) {
135135
assert.Equal(t, "oauth2_grant", new(OAuth2Grant).TableName())
136136
}
137137

138+
func TestGetOAuth2GrantsByUserID(t *testing.T) {
139+
assert.NoError(t, PrepareTestDatabase())
140+
result, err := GetOAuth2GrantsByUserID(1)
141+
assert.NoError(t, err)
142+
assert.Len(t, result, 1)
143+
assert.Equal(t, int64(1), result[0].ID)
144+
assert.Equal(t, result[0].ApplicationID, result[0].Application.ID)
145+
146+
result, err = GetOAuth2GrantsByUserID(34134)
147+
assert.NoError(t, err)
148+
assert.Empty(t, result)
149+
}
150+
151+
func TestRevokeOAuth2Grant(t *testing.T) {
152+
assert.NoError(t, PrepareTestDatabase())
153+
assert.NoError(t, RevokeOAuth2Grant(1, 1))
154+
AssertNotExistsBean(t, &OAuth2Grant{ID: 1, UserID: 1})
155+
}
156+
138157
//////////////////// Authorization Code
139158

140159
func TestGetOAuth2AuthorizationByCode(t *testing.T) {

options/locale/locale_en-US.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,13 @@ oauth2_application_edit = Edit
499499
oauth2_application_create_description = OAuth2 applications gives your third-party application access to user accounts on this instance.
500500
oauth2_application_remove_description = Removing an OAuth2 application will prevent it to access authorized user accounts on this instance. Continue?
501501
502+
authorized_oauth2_applications = Authorized OAuth2 Applications
503+
authorized_oauth2_applications_description = You've granted access to your personal Gitea account to these third party applications. Please revoke access for applications no longer needed.
504+
revoke_key = Revoke
505+
revoke_oauth2_grant = Revoke Access
506+
revoke_oauth2_grant_description = Revoking access for this third party application will prevent this application from accessing your data. Are you sure?
507+
revoke_oauth2_grant_success = You've revoked access successfully.
508+
502509
twofa_desc = Two-factor authentication enhances the security of your account.
503510
twofa_is_enrolled = Your account is currently <strong>enrolled</strong> in two-factor authentication.
504511
twofa_not_enrolled = Your account is not currently enrolled in two-factor authentication.

routers/routes/routes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ func RegisterRoutes(m *macaron.Macaron) {
344344
m.Post("/:id/regenerate_secret", userSetting.OAuthApplicationsRegenerateSecret)
345345
m.Post("", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsPost)
346346
m.Post("/delete", userSetting.DeleteOAuth2Application)
347+
m.Post("/revoke", userSetting.RevokeOAuth2Grant)
347348
})
348349
m.Combo("/applications").Get(userSetting.Applications).
349350
Post(bindIgnErr(auth.NewAccessTokenForm{}), userSetting.ApplicationsPost)

routers/user/setting/applications.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,10 @@ func loadApplicationsData(ctx *context.Context) {
8181
ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
8282
return
8383
}
84+
ctx.Data["Grants"], err = models.GetOAuth2GrantsByUserID(ctx.User.ID)
85+
if err != nil {
86+
ctx.ServerError("GetOAuth2GrantsByUserID", err)
87+
return
88+
}
8489
}
8590
}

routers/user/setting/oauth2.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package setting
66

77
import (
8+
"fmt"
9+
810
"code.gitea.io/gitea/models"
911
"code.gitea.io/gitea/modules/auth"
1012
"code.gitea.io/gitea/modules/base"
@@ -138,3 +140,20 @@ func DeleteOAuth2Application(ctx *context.Context) {
138140
"redirect": setting.AppSubURL + "/user/settings/applications",
139141
})
140142
}
143+
144+
// RevokeOAuth2Grant revokes the grant with the given id
145+
func RevokeOAuth2Grant(ctx *context.Context) {
146+
if ctx.User.ID == 0 || ctx.QueryInt64("id") == 0 {
147+
ctx.ServerError("RevokeOAuth2Grant", fmt.Errorf("user id or grant id is zero"))
148+
return
149+
}
150+
if err := models.RevokeOAuth2Grant(ctx.QueryInt64("id"), ctx.User.ID); err != nil {
151+
ctx.ServerError("RevokeOAuth2Grant", err)
152+
return
153+
}
154+
155+
ctx.Flash.Success(ctx.Tr("settings.revoke_oauth2_grant_success"))
156+
ctx.JSON(200, map[string]interface{}{
157+
"redirect": setting.AppSubURL + "/user/settings/applications",
158+
})
159+
}

templates/user/settings/applications.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
</div>
4848

4949
{{if .EnableOAuth2}}
50+
{{template "user/settings/grants_oauth2" .}}
5051
{{template "user/settings/applications_oauth2" .}}
5152
{{end}}
5253
</div>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<h4 class="ui top attached header">
2+
{{.i18n.Tr "settings.authorized_oauth2_applications"}}
3+
</h4>
4+
<div class="ui attached segment">
5+
<div class="ui key list">
6+
<div class="item">
7+
{{.i18n.Tr "settings.authorized_oauth2_applications_description"}}
8+
</div>
9+
{{range $grant := .Grants}}
10+
<div class="item">
11+
<div class="right floated content">
12+
<button class="ui red tiny button delete-button" id="revoke-gitea-oauth2-grant"
13+
data-url="{{AppSubUrl}}/user/settings/applications/oauth2/revoke"
14+
data-id="{{$grant.ID}}">
15+
{{$.i18n.Tr "settings.revoke_key"}}
16+
</button>
17+
</div>
18+
<i class="big key icon"></i>
19+
<div class="content">
20+
<strong>{{$grant.Application.Name}}</strong>
21+
<div class="activity meta">
22+
<i>{{$.i18n.Tr "settings.add_on"}} <span>{{$grant.CreatedUnix.FormatShort}}</span></i>
23+
</div>
24+
</div>
25+
</div>
26+
{{end}}
27+
</div>
28+
</div>
29+
30+
<div class="ui small basic delete modal" id="revoke-gitea-oauth2-grant">
31+
<div class="ui icon header">
32+
<i class="shield alternate icon"></i>
33+
{{.i18n.Tr "settings.revoke_oauth2_grant"}}
34+
</div>
35+
<div class="content">
36+
<p>{{.i18n.Tr "settings.revoke_oauth2_grant_description"}}</p>
37+
</div>
38+
{{template "base/delete_modal_actions" .}}
39+
</div>

0 commit comments

Comments
 (0)