forked from forgejo/forgejo
Integrate OAuth2 Provider (#5378)
This commit is contained in:
parent
9d3732dfd5
commit
e777c6bdc6
37 changed files with 2667 additions and 11 deletions
452
routers/user/oauth.go
Normal file
452
routers/user/oauth.go
Normal file
|
@ -0,0 +1,452 @@
|
|||
// 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 user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/go-macaron/binding"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/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/util"
|
||||
)
|
||||
|
||||
const (
|
||||
tplGrantAccess base.TplName = "user/auth/grant"
|
||||
tplGrantError base.TplName = "user/auth/grant_error"
|
||||
)
|
||||
|
||||
// TODO move error and responses to SDK or models
|
||||
|
||||
// AuthorizeErrorCode represents an error code specified in RFC 6749
|
||||
type AuthorizeErrorCode string
|
||||
|
||||
const (
|
||||
// ErrorCodeInvalidRequest represents the according error in RFC 6749
|
||||
ErrorCodeInvalidRequest AuthorizeErrorCode = "invalid_request"
|
||||
// ErrorCodeUnauthorizedClient represents the according error in RFC 6749
|
||||
ErrorCodeUnauthorizedClient AuthorizeErrorCode = "unauthorized_client"
|
||||
// ErrorCodeAccessDenied represents the according error in RFC 6749
|
||||
ErrorCodeAccessDenied AuthorizeErrorCode = "access_denied"
|
||||
// ErrorCodeUnsupportedResponseType represents the according error in RFC 6749
|
||||
ErrorCodeUnsupportedResponseType AuthorizeErrorCode = "unsupported_response_type"
|
||||
// ErrorCodeInvalidScope represents the according error in RFC 6749
|
||||
ErrorCodeInvalidScope AuthorizeErrorCode = "invalid_scope"
|
||||
// ErrorCodeServerError represents the according error in RFC 6749
|
||||
ErrorCodeServerError AuthorizeErrorCode = "server_error"
|
||||
// ErrorCodeTemporaryUnavailable represents the according error in RFC 6749
|
||||
ErrorCodeTemporaryUnavailable AuthorizeErrorCode = "temporarily_unavailable"
|
||||
)
|
||||
|
||||
// AuthorizeError represents an error type specified in RFC 6749
|
||||
type AuthorizeError struct {
|
||||
ErrorCode AuthorizeErrorCode `json:"error" form:"error"`
|
||||
ErrorDescription string
|
||||
State string
|
||||
}
|
||||
|
||||
// Error returns the error message
|
||||
func (err AuthorizeError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", err.ErrorCode, err.ErrorDescription)
|
||||
}
|
||||
|
||||
// AccessTokenErrorCode represents an error code specified in RFC 6749
|
||||
type AccessTokenErrorCode string
|
||||
|
||||
const (
|
||||
// AccessTokenErrorCodeInvalidRequest represents an error code specified in RFC 6749
|
||||
AccessTokenErrorCodeInvalidRequest AccessTokenErrorCode = "invalid_request"
|
||||
// AccessTokenErrorCodeInvalidClient represents an error code specified in RFC 6749
|
||||
AccessTokenErrorCodeInvalidClient = "invalid_client"
|
||||
// AccessTokenErrorCodeInvalidGrant represents an error code specified in RFC 6749
|
||||
AccessTokenErrorCodeInvalidGrant = "invalid_grant"
|
||||
// AccessTokenErrorCodeUnauthorizedClient represents an error code specified in RFC 6749
|
||||
AccessTokenErrorCodeUnauthorizedClient = "unauthorized_client"
|
||||
// AccessTokenErrorCodeUnsupportedGrantType represents an error code specified in RFC 6749
|
||||
AccessTokenErrorCodeUnsupportedGrantType = "unsupported_grant_type"
|
||||
// AccessTokenErrorCodeInvalidScope represents an error code specified in RFC 6749
|
||||
AccessTokenErrorCodeInvalidScope = "invalid_scope"
|
||||
)
|
||||
|
||||
// AccessTokenError represents an error response specified in RFC 6749
|
||||
type AccessTokenError struct {
|
||||
ErrorCode AccessTokenErrorCode `json:"error" form:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
|
||||
// Error returns the error message
|
||||
func (err AccessTokenError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", err.ErrorCode, err.ErrorDescription)
|
||||
}
|
||||
|
||||
// TokenType specifies the kind of token
|
||||
type TokenType string
|
||||
|
||||
const (
|
||||
// TokenTypeBearer represents a token type specified in RFC 6749
|
||||
TokenTypeBearer TokenType = "bearer"
|
||||
// TokenTypeMAC represents a token type specified in RFC 6749
|
||||
TokenTypeMAC = "mac"
|
||||
)
|
||||
|
||||
// AccessTokenResponse represents a successful access token response
|
||||
type AccessTokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType TokenType `json:"token_type"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
// TODO implement RefreshToken
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
func newAccessTokenResponse(grant *models.OAuth2Grant) (*AccessTokenResponse, *AccessTokenError) {
|
||||
if err := grant.IncreaseCounter(); err != nil {
|
||||
return nil, &AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeInvalidGrant,
|
||||
ErrorDescription: "cannot increase the grant counter",
|
||||
}
|
||||
}
|
||||
// generate access token to access the API
|
||||
expirationDate := util.TimeStampNow().Add(setting.OAuth2.AccessTokenExpirationTime)
|
||||
accessToken := &models.OAuth2Token{
|
||||
GrantID: grant.ID,
|
||||
Type: models.TypeAccessToken,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: expirationDate.AsTime().Unix(),
|
||||
},
|
||||
}
|
||||
signedAccessToken, err := accessToken.SignToken()
|
||||
if err != nil {
|
||||
return nil, &AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeInvalidRequest,
|
||||
ErrorDescription: "cannot sign token",
|
||||
}
|
||||
}
|
||||
|
||||
// generate refresh token to request an access token after it expired later
|
||||
refreshExpirationDate := util.TimeStampNow().Add(setting.OAuth2.RefreshTokenExpirationTime * 60 * 60).AsTime().Unix()
|
||||
refreshToken := &models.OAuth2Token{
|
||||
GrantID: grant.ID,
|
||||
Counter: grant.Counter,
|
||||
Type: models.TypeRefreshToken,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: refreshExpirationDate,
|
||||
},
|
||||
}
|
||||
signedRefreshToken, err := refreshToken.SignToken()
|
||||
if err != nil {
|
||||
return nil, &AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeInvalidRequest,
|
||||
ErrorDescription: "cannot sign token",
|
||||
}
|
||||
}
|
||||
|
||||
return &AccessTokenResponse{
|
||||
AccessToken: signedAccessToken,
|
||||
TokenType: TokenTypeBearer,
|
||||
ExpiresIn: setting.OAuth2.AccessTokenExpirationTime,
|
||||
RefreshToken: signedRefreshToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AuthorizeOAuth manages authorize requests
|
||||
func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) {
|
||||
errs := binding.Errors{}
|
||||
errs = form.Validate(ctx.Context, errs)
|
||||
|
||||
app, err := models.GetOAuth2ApplicationByClientID(form.ClientID)
|
||||
if err != nil {
|
||||
if models.IsErrOauthClientIDInvalid(err) {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
ErrorCode: ErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "Client ID not registered",
|
||||
State: form.State,
|
||||
}, "")
|
||||
return
|
||||
}
|
||||
ctx.ServerError("GetOAuth2ApplicationByClientID", err)
|
||||
return
|
||||
}
|
||||
if err := app.LoadUser(); err != nil {
|
||||
ctx.ServerError("LoadUser", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !app.ContainsRedirectURI(form.RedirectURI) {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
ErrorCode: ErrorCodeInvalidRequest,
|
||||
ErrorDescription: "Unregistered Redirect URI",
|
||||
State: form.State,
|
||||
}, "")
|
||||
return
|
||||
}
|
||||
|
||||
if form.ResponseType != "code" {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
ErrorCode: ErrorCodeUnsupportedResponseType,
|
||||
ErrorDescription: "Only code response type is supported.",
|
||||
State: form.State,
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
|
||||
// pkce support
|
||||
switch form.CodeChallengeMethod {
|
||||
case "S256":
|
||||
case "plain":
|
||||
if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallengeMethod); err != nil {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
ErrorCode: ErrorCodeServerError,
|
||||
ErrorDescription: "cannot set code challenge method",
|
||||
State: form.State,
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallenge); err != nil {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
ErrorCode: ErrorCodeServerError,
|
||||
ErrorDescription: "cannot set code challenge",
|
||||
State: form.State,
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
break
|
||||
case "":
|
||||
break
|
||||
default:
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
ErrorCode: ErrorCodeInvalidRequest,
|
||||
ErrorDescription: "unsupported code challenge method",
|
||||
State: form.State,
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
|
||||
grant, err := app.GetGrantByUserID(ctx.User.ID)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
|
||||
// Redirect if user already granted access
|
||||
if grant != nil {
|
||||
code, err := grant.GenerateNewAuthorizationCode(form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
redirect, err := code.GenerateRedirectURI(form.State)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
ctx.Redirect(redirect.String(), 302)
|
||||
return
|
||||
}
|
||||
|
||||
// show authorize page to grant access
|
||||
ctx.Data["Application"] = app
|
||||
ctx.Data["RedirectURI"] = form.RedirectURI
|
||||
ctx.Data["State"] = form.State
|
||||
ctx.Data["ApplicationUserLink"] = "<a href=\"" + setting.LocalURL + app.User.LowerName + "\">@" + app.User.Name + "</a>"
|
||||
ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + form.RedirectURI + "</strong>"
|
||||
// TODO document SESSION <=> FORM
|
||||
ctx.Session.Set("client_id", app.ClientID)
|
||||
ctx.Session.Set("redirect_uri", form.RedirectURI)
|
||||
ctx.Session.Set("state", form.State)
|
||||
ctx.HTML(200, tplGrantAccess)
|
||||
}
|
||||
|
||||
// GrantApplicationOAuth manages the post request submitted when a user grants access to an application
|
||||
func GrantApplicationOAuth(ctx *context.Context, form auth.GrantApplicationForm) {
|
||||
if ctx.Session.Get("client_id") != form.ClientID || ctx.Session.Get("state") != form.State ||
|
||||
ctx.Session.Get("redirect_uri") != form.RedirectURI {
|
||||
ctx.Error(400)
|
||||
return
|
||||
}
|
||||
app, err := models.GetOAuth2ApplicationByClientID(form.ClientID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetOAuth2ApplicationByClientID", err)
|
||||
return
|
||||
}
|
||||
grant, err := app.CreateGrant(ctx.User.ID)
|
||||
if err != nil {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
State: form.State,
|
||||
ErrorDescription: "cannot create grant for user",
|
||||
ErrorCode: ErrorCodeServerError,
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
|
||||
var codeChallenge, codeChallengeMethod string
|
||||
codeChallenge, _ = ctx.Session.Get("CodeChallenge").(string)
|
||||
codeChallengeMethod, _ = ctx.Session.Get("CodeChallengeMethod").(string)
|
||||
|
||||
code, err := grant.GenerateNewAuthorizationCode(form.RedirectURI, codeChallenge, codeChallengeMethod)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
redirect, err := code.GenerateRedirectURI(form.State)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
}
|
||||
ctx.Redirect(redirect.String(), 302)
|
||||
}
|
||||
|
||||
// AccessTokenOAuth manages all access token requests by the client
|
||||
func AccessTokenOAuth(ctx *context.Context, form auth.AccessTokenForm) {
|
||||
switch form.GrantType {
|
||||
case "refresh_token":
|
||||
handleRefreshToken(ctx, form)
|
||||
return
|
||||
case "authorization_code":
|
||||
handleAuthorizationCode(ctx, form)
|
||||
return
|
||||
default:
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnsupportedGrantType,
|
||||
ErrorDescription: "Only refresh_token or authorization_code grant type is supported",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func handleRefreshToken(ctx *context.Context, form auth.AccessTokenForm) {
|
||||
token, err := models.ParseOAuth2Token(form.RefreshToken)
|
||||
if err != nil {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
// get grant before increasing counter
|
||||
grant, err := models.GetOAuth2GrantByID(token.GrantID)
|
||||
if err != nil || grant == nil {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeInvalidGrant,
|
||||
ErrorDescription: "grant does not exist",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// check if token got already used
|
||||
if grant.Counter != token.Counter || token.Counter == 0 {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "token was already used",
|
||||
})
|
||||
log.Warn("A client tried to use a refresh token for grant_id = %d was used twice!", grant.ID)
|
||||
return
|
||||
}
|
||||
accessToken, tokenErr := newAccessTokenResponse(grant)
|
||||
if tokenErr != nil {
|
||||
handleAccessTokenError(ctx, *tokenErr)
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, accessToken)
|
||||
}
|
||||
|
||||
func handleAuthorizationCode(ctx *context.Context, form auth.AccessTokenForm) {
|
||||
app, err := models.GetOAuth2ApplicationByClientID(form.ClientID)
|
||||
if err != nil {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeInvalidClient,
|
||||
ErrorDescription: "cannot load client",
|
||||
})
|
||||
return
|
||||
}
|
||||
if !app.ValidateClientSecret([]byte(form.ClientSecret)) {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
if form.RedirectURI != "" && !app.ContainsRedirectURI(form.RedirectURI) {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
authorizationCode, err := models.GetOAuth2AuthorizationByCode(form.Code)
|
||||
if err != nil || authorizationCode == nil {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
// check if code verifier authorizes the client, PKCE support
|
||||
if !authorizationCode.ValidateCodeChallenge(form.CodeVerifier) {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
})
|
||||
return
|
||||
}
|
||||
// check if granted for this application
|
||||
if authorizationCode.Grant.ApplicationID != app.ID {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeInvalidGrant,
|
||||
ErrorDescription: "invalid grant",
|
||||
})
|
||||
return
|
||||
}
|
||||
// remove token from database to deny duplicate usage
|
||||
if err := authorizationCode.Invalidate(); err != nil {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeInvalidRequest,
|
||||
ErrorDescription: "cannot proceed your request",
|
||||
})
|
||||
}
|
||||
resp, tokenErr := newAccessTokenResponse(authorizationCode.Grant)
|
||||
if tokenErr != nil {
|
||||
handleAccessTokenError(ctx, *tokenErr)
|
||||
return
|
||||
}
|
||||
// send successful response
|
||||
ctx.JSON(200, resp)
|
||||
}
|
||||
|
||||
func handleAccessTokenError(ctx *context.Context, acErr AccessTokenError) {
|
||||
ctx.JSON(400, acErr)
|
||||
}
|
||||
|
||||
func handleServerError(ctx *context.Context, state string, redirectURI string) {
|
||||
handleAuthorizeError(ctx, AuthorizeError{
|
||||
ErrorCode: ErrorCodeServerError,
|
||||
ErrorDescription: "A server error occurred",
|
||||
State: state,
|
||||
}, redirectURI)
|
||||
}
|
||||
|
||||
func handleAuthorizeError(ctx *context.Context, authErr AuthorizeError, redirectURI string) {
|
||||
if redirectURI == "" {
|
||||
log.Warn("Authorization failed: %v", authErr.ErrorDescription)
|
||||
ctx.Data["Error"] = authErr
|
||||
ctx.HTML(400, tplGrantError)
|
||||
return
|
||||
}
|
||||
redirect, err := url.Parse(redirectURI)
|
||||
if err != nil {
|
||||
ctx.ServerError("url.Parse", err)
|
||||
return
|
||||
}
|
||||
q := redirect.Query()
|
||||
q.Set("error", string(authErr.ErrorCode))
|
||||
q.Set("error_description", authErr.ErrorDescription)
|
||||
q.Set("state", authErr.State)
|
||||
redirect.RawQuery = q.Encode()
|
||||
ctx.Redirect(redirect.String(), 302)
|
||||
}
|
|
@ -74,4 +74,12 @@ func loadApplicationsData(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
ctx.Data["Tokens"] = tokens
|
||||
ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
|
||||
if setting.OAuth2.Enable {
|
||||
ctx.Data["Applications"], err = models.GetOAuth2ApplicationsByUserID(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
112
routers/user/setting/oauth2.go
Normal file
112
routers/user/setting/oauth2.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
// 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 (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/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"
|
||||
)
|
||||
|
||||
const (
|
||||
tplSettingsOAuthApplications base.TplName = "user/settings/applications_oauth2_edit"
|
||||
)
|
||||
|
||||
// OAuthApplicationsPost response for adding a oauth2 application
|
||||
func OAuthApplicationsPost(ctx *context.Context, form auth.EditOAuth2ApplicationForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsSettingsApplications"] = true
|
||||
|
||||
if ctx.HasError() {
|
||||
loadApplicationsData(ctx)
|
||||
|
||||
ctx.HTML(200, tplSettingsApplications)
|
||||
return
|
||||
}
|
||||
// TODO validate redirect URI
|
||||
app, err := models.CreateOAuth2Application(models.CreateOAuth2ApplicationOptions{
|
||||
Name: form.Name,
|
||||
RedirectURIs: []string{form.RedirectURI},
|
||||
UserID: ctx.User.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(200, tplSettingsOAuthApplications)
|
||||
}
|
||||
|
||||
// OAuthApplicationsEdit response for editing oauth2 application
|
||||
func OAuthApplicationsEdit(ctx *context.Context, form auth.EditOAuth2ApplicationForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsSettingsApplications"] = true
|
||||
|
||||
if ctx.HasError() {
|
||||
loadApplicationsData(ctx)
|
||||
|
||||
ctx.HTML(200, tplSettingsApplications)
|
||||
return
|
||||
}
|
||||
// TODO validate redirect URI
|
||||
if err := models.UpdateOAuth2Application(models.UpdateOAuth2ApplicationOptions{
|
||||
ID: ctx.ParamsInt64("id"),
|
||||
Name: form.Name,
|
||||
RedirectURIs: []string{form.RedirectURI},
|
||||
UserID: ctx.User.ID,
|
||||
}); err != nil {
|
||||
ctx.ServerError("UpdateOAuth2Application", err)
|
||||
return
|
||||
}
|
||||
var err error
|
||||
if ctx.Data["App"], err = models.GetOAuth2ApplicationByID(ctx.ParamsInt64("id")); err != nil {
|
||||
ctx.ServerError("GetOAuth2ApplicationByID", err)
|
||||
return
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success"))
|
||||
ctx.HTML(200, tplSettingsOAuthApplications)
|
||||
}
|
||||
|
||||
// OAuth2ApplicationShow displays the given application
|
||||
func OAuth2ApplicationShow(ctx *context.Context) {
|
||||
app, err := models.GetOAuth2ApplicationByID(ctx.ParamsInt64("id"))
|
||||
if err != nil {
|
||||
if models.IsErrOAuthApplicationNotFound(err) {
|
||||
ctx.NotFound("Application not found", err)
|
||||
return
|
||||
}
|
||||
ctx.ServerError("GetOAuth2ApplicationByID", err)
|
||||
return
|
||||
}
|
||||
if app.UID != ctx.User.ID {
|
||||
ctx.NotFound("Application not found", nil)
|
||||
return
|
||||
}
|
||||
ctx.Data["App"] = app
|
||||
ctx.HTML(200, tplSettingsOAuthApplications)
|
||||
}
|
||||
|
||||
// DeleteOAuth2Application deletes the given oauth2 application
|
||||
func DeleteOAuth2Application(ctx *context.Context) {
|
||||
if err := models.DeleteOAuth2Application(ctx.QueryInt64("id"), ctx.User.ID); err != nil {
|
||||
ctx.ServerError("DeleteOAuth2Application", err)
|
||||
return
|
||||
}
|
||||
log.Trace("OAuth2 Application deleted: %s", ctx.User.Name)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("settings.remove_oauth2_application_success"))
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"redirect": setting.AppSubURL + "/user/settings/applications",
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue