aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonas Franz <info@jonasfranz.software>2019-04-17 10:18:16 +0200
committerLunny Xiao <xiaolunwen@gmail.com>2019-04-17 16:18:16 +0800
commit7a4c29c739fa9b08f901220ebcb2948daf491692 (patch)
tree73e44404a6c13061b832624582e230eedb801c3c
parent34548369e1d78eb1141aecd4ab02acf59f2949ae (diff)
downloadgitea-7a4c29c739fa9b08f901220ebcb2948daf491692.tar.gz
gitea-7a4c29c739fa9b08f901220ebcb2948daf491692.zip
OAuth2 Grant UI (#6625)
* Add oauth2 grants ui Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add delete functionality Add translations Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit tests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit tests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Refactor DeleteOAuth2Grant Use results.Close() Signed-off-by: Jonas Franz <info@jonasfranz.software> * Refactor DeleteOAuth2Grant (again) Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if user ID is zero Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if grant ID is zero Signed-off-by: Jonas Franz <info@jonasfranz.software>
-rw-r--r--models/oauth2_application.go55
-rw-r--r--models/oauth2_application_test.go19
-rw-r--r--options/locale/locale_en-US.ini7
-rw-r--r--routers/routes/routes.go1
-rw-r--r--routers/user/setting/applications.go5
-rw-r--r--routers/user/setting/oauth2.go19
-rw-r--r--templates/user/settings/applications.tmpl1
-rw-r--r--templates/user/settings/grants_oauth2.tmpl39
8 files changed, 140 insertions, 6 deletions
diff --git a/models/oauth2_application.go b/models/oauth2_application.go
index dd80a79b48..1e69dd6430 100644
--- a/models/oauth2_application.go
+++ b/models/oauth2_application.go
@@ -340,12 +340,13 @@ func getOAuth2AuthorizationByCode(e Engine, code string) (auth *OAuth2Authorizat
// OAuth2Grant represents the permission of an user for a specifc application to access resources
type OAuth2Grant struct {
- ID int64 `xorm:"pk autoincr"`
- UserID int64 `xorm:"INDEX unique(user_application)"`
- ApplicationID int64 `xorm:"INDEX unique(user_application)"`
- Counter int64 `xorm:"NOT NULL DEFAULT 1"`
- CreatedUnix util.TimeStamp `xorm:"created"`
- UpdatedUnix util.TimeStamp `xorm:"updated"`
+ ID int64 `xorm:"pk autoincr"`
+ UserID int64 `xorm:"INDEX unique(user_application)"`
+ Application *OAuth2Application `xorm:"-"`
+ ApplicationID int64 `xorm:"INDEX unique(user_application)"`
+ Counter int64 `xorm:"NOT NULL DEFAULT 1"`
+ CreatedUnix util.TimeStamp `xorm:"created"`
+ UpdatedUnix util.TimeStamp `xorm:"updated"`
}
// TableName sets the table name to `oauth2_grant`
@@ -410,6 +411,48 @@ func getOAuth2GrantByID(e Engine, id int64) (grant *OAuth2Grant, err error) {
return
}
+// GetOAuth2GrantsByUserID lists all grants of a certain user
+func GetOAuth2GrantsByUserID(uid int64) ([]*OAuth2Grant, error) {
+ return getOAuth2GrantsByUserID(x, uid)
+}
+
+func getOAuth2GrantsByUserID(e Engine, uid int64) ([]*OAuth2Grant, error) {
+ type joinedOAuth2Grant struct {
+ Grant *OAuth2Grant `xorm:"extends"`
+ Application *OAuth2Application `xorm:"extends"`
+ }
+ var results *xorm.Rows
+ var err error
+ if results, err = e.
+ Table("oauth2_grant").
+ Where("user_id = ?", uid).
+ Join("INNER", "oauth2_application", "application_id = oauth2_application.id").
+ Rows(new(joinedOAuth2Grant)); err != nil {
+ return nil, err
+ }
+ defer results.Close()
+ grants := make([]*OAuth2Grant, 0)
+ for results.Next() {
+ joinedGrant := new(joinedOAuth2Grant)
+ if err := results.Scan(joinedGrant); err != nil {
+ return nil, err
+ }
+ joinedGrant.Grant.Application = joinedGrant.Application
+ grants = append(grants, joinedGrant.Grant)
+ }
+ return grants, nil
+}
+
+// RevokeOAuth2Grant deletes the grant with grantID and userID
+func RevokeOAuth2Grant(grantID, userID int64) error {
+ return revokeOAuth2Grant(x, grantID, userID)
+}
+
+func revokeOAuth2Grant(e Engine, grantID, userID int64) error {
+ _, err := e.Delete(&OAuth2Grant{ID: grantID, UserID: userID})
+ return err
+}
+
//////////////////////////////////////////////////////////////
// OAuth2TokenType represents the type of token for an oauth application
diff --git a/models/oauth2_application_test.go b/models/oauth2_application_test.go
index b06d9356c0..3afdf50f53 100644
--- a/models/oauth2_application_test.go
+++ b/models/oauth2_application_test.go
@@ -135,6 +135,25 @@ func TestOAuth2Grant_TableName(t *testing.T) {
assert.Equal(t, "oauth2_grant", new(OAuth2Grant).TableName())
}
+func TestGetOAuth2GrantsByUserID(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+ result, err := GetOAuth2GrantsByUserID(1)
+ assert.NoError(t, err)
+ assert.Len(t, result, 1)
+ assert.Equal(t, int64(1), result[0].ID)
+ assert.Equal(t, result[0].ApplicationID, result[0].Application.ID)
+
+ result, err = GetOAuth2GrantsByUserID(34134)
+ assert.NoError(t, err)
+ assert.Empty(t, result)
+}
+
+func TestRevokeOAuth2Grant(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+ assert.NoError(t, RevokeOAuth2Grant(1, 1))
+ AssertNotExistsBean(t, &OAuth2Grant{ID: 1, UserID: 1})
+}
+
//////////////////// Authorization Code
func TestGetOAuth2AuthorizationByCode(t *testing.T) {
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 23d1949203..9fc1e3da2a 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -499,6 +499,13 @@ oauth2_application_edit = Edit
oauth2_application_create_description = OAuth2 applications gives your third-party application access to user accounts on this instance.
oauth2_application_remove_description = Removing an OAuth2 application will prevent it to access authorized user accounts on this instance. Continue?
+authorized_oauth2_applications = Authorized OAuth2 Applications
+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.
+revoke_key = Revoke
+revoke_oauth2_grant = Revoke Access
+revoke_oauth2_grant_description = Revoking access for this third party application will prevent this application from accessing your data. Are you sure?
+revoke_oauth2_grant_success = You've revoked access successfully.
+
twofa_desc = Two-factor authentication enhances the security of your account.
twofa_is_enrolled = Your account is currently <strong>enrolled</strong> in two-factor authentication.
twofa_not_enrolled = Your account is not currently enrolled in two-factor authentication.
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index 5c6d36befa..9602bbed4a 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -344,6 +344,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/:id/regenerate_secret", userSetting.OAuthApplicationsRegenerateSecret)
m.Post("", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsPost)
m.Post("/delete", userSetting.DeleteOAuth2Application)
+ m.Post("/revoke", userSetting.RevokeOAuth2Grant)
})
m.Combo("/applications").Get(userSetting.Applications).
Post(bindIgnErr(auth.NewAccessTokenForm{}), userSetting.ApplicationsPost)
diff --git a/routers/user/setting/applications.go b/routers/user/setting/applications.go
index bc8633f72d..90e34d9e1a 100644
--- a/routers/user/setting/applications.go
+++ b/routers/user/setting/applications.go
@@ -81,5 +81,10 @@ func loadApplicationsData(ctx *context.Context) {
ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
return
}
+ ctx.Data["Grants"], err = models.GetOAuth2GrantsByUserID(ctx.User.ID)
+ if err != nil {
+ ctx.ServerError("GetOAuth2GrantsByUserID", err)
+ return
+ }
}
}
diff --git a/routers/user/setting/oauth2.go b/routers/user/setting/oauth2.go
index 1068b5db49..265e32642b 100644
--- a/routers/user/setting/oauth2.go
+++ b/routers/user/setting/oauth2.go
@@ -5,6 +5,8 @@
package setting
import (
+ "fmt"
+
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
@@ -138,3 +140,20 @@ func DeleteOAuth2Application(ctx *context.Context) {
"redirect": setting.AppSubURL + "/user/settings/applications",
})
}
+
+// RevokeOAuth2Grant revokes the grant with the given id
+func RevokeOAuth2Grant(ctx *context.Context) {
+ if ctx.User.ID == 0 || ctx.QueryInt64("id") == 0 {
+ ctx.ServerError("RevokeOAuth2Grant", fmt.Errorf("user id or grant id is zero"))
+ return
+ }
+ if err := models.RevokeOAuth2Grant(ctx.QueryInt64("id"), ctx.User.ID); err != nil {
+ ctx.ServerError("RevokeOAuth2Grant", err)
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("settings.revoke_oauth2_grant_success"))
+ ctx.JSON(200, map[string]interface{}{
+ "redirect": setting.AppSubURL + "/user/settings/applications",
+ })
+}
diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl
index 1a3ab5efaf..08b2ca7195 100644
--- a/templates/user/settings/applications.tmpl
+++ b/templates/user/settings/applications.tmpl
@@ -47,6 +47,7 @@
</div>
{{if .EnableOAuth2}}
+ {{template "user/settings/grants_oauth2" .}}
{{template "user/settings/applications_oauth2" .}}
{{end}}
</div>
diff --git a/templates/user/settings/grants_oauth2.tmpl b/templates/user/settings/grants_oauth2.tmpl
new file mode 100644
index 0000000000..4dc5b9446b
--- /dev/null
+++ b/templates/user/settings/grants_oauth2.tmpl
@@ -0,0 +1,39 @@
+<h4 class="ui top attached header">
+ {{.i18n.Tr "settings.authorized_oauth2_applications"}}
+</h4>
+<div class="ui attached segment">
+ <div class="ui key list">
+ <div class="item">
+ {{.i18n.Tr "settings.authorized_oauth2_applications_description"}}
+ </div>
+ {{range $grant := .Grants}}
+ <div class="item">
+ <div class="right floated content">
+ <button class="ui red tiny button delete-button" id="revoke-gitea-oauth2-grant"
+ data-url="{{AppSubUrl}}/user/settings/applications/oauth2/revoke"
+ data-id="{{$grant.ID}}">
+ {{$.i18n.Tr "settings.revoke_key"}}
+ </button>
+ </div>
+ <i class="big key icon"></i>
+ <div class="content">
+ <strong>{{$grant.Application.Name}}</strong>
+ <div class="activity meta">
+ <i>{{$.i18n.Tr "settings.add_on"}} <span>{{$grant.CreatedUnix.FormatShort}}</span></i>
+ </div>
+ </div>
+ </div>
+ {{end}}
+ </div>
+</div>
+
+<div class="ui small basic delete modal" id="revoke-gitea-oauth2-grant">
+ <div class="ui icon header">
+ <i class="shield alternate icon"></i>
+ {{.i18n.Tr "settings.revoke_oauth2_grant"}}
+ </div>
+ <div class="content">
+ <p>{{.i18n.Tr "settings.revoke_oauth2_grant_description"}}</p>
+ </div>
+ {{template "base/delete_modal_actions" .}}
+</div>