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>
This commit is contained in:
		
							parent
							
								
									34548369e1
								
							
						
					
					
						commit
						7a4c29c739
					
				
					 8 changed files with 140 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,6 +47,7 @@
 | 
			
		|||
		</div>
 | 
			
		||||
 | 
			
		||||
		{{if .EnableOAuth2}}
 | 
			
		||||
			{{template "user/settings/grants_oauth2" .}}
 | 
			
		||||
			{{template "user/settings/applications_oauth2" .}}
 | 
			
		||||
		{{end}}
 | 
			
		||||
	</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										39
									
								
								templates/user/settings/grants_oauth2.tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								templates/user/settings/grants_oauth2.tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue