2019-11-23 02:33:31 +03:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// 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 sso
import (
2021-01-05 16:05:40 +03:00
"net/http"
2019-11-23 02:33:31 +03:00
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
2021-01-30 11:55:53 +03:00
"code.gitea.io/gitea/modules/web/middleware"
2019-11-23 02:33:31 +03:00
)
// Ensure the struct implements the interface.
var (
_ SingleSignOn = & OAuth2 { }
)
// CheckOAuthAccessToken returns uid of user from oauth token
func CheckOAuthAccessToken ( accessToken string ) int64 {
// JWT tokens require a "."
if ! strings . Contains ( accessToken , "." ) {
return 0
}
token , err := models . ParseOAuth2Token ( accessToken )
if err != nil {
log . Trace ( "ParseOAuth2Token: %v" , err )
return 0
}
var grant * models . OAuth2Grant
if grant , err = models . GetOAuth2GrantByID ( token . GrantID ) ; err != nil || grant == nil {
return 0
}
if token . Type != models . TypeAccessToken {
return 0
}
if token . ExpiresAt < time . Now ( ) . Unix ( ) || token . IssuedAt > time . Now ( ) . Unix ( ) {
return 0
}
return grant . UserID
}
// OAuth2 implements the SingleSignOn interface and authenticates requests
// (API requests only) by looking for an OAuth token in query parameters or the
// "Authorization" header.
type OAuth2 struct {
}
// Init does nothing as the OAuth2 implementation does not need to allocate any resources
func ( o * OAuth2 ) Init ( ) error {
return nil
}
// Free does nothing as the OAuth2 implementation does not have to release any resources
func ( o * OAuth2 ) Free ( ) error {
return nil
}
// userIDFromToken returns the user id corresponding to the OAuth token.
2021-01-05 16:05:40 +03:00
func ( o * OAuth2 ) userIDFromToken ( req * http . Request , store DataStore ) int64 {
2021-01-26 18:36:53 +03:00
_ = req . ParseForm ( )
2019-11-23 02:33:31 +03:00
// Check access token.
2021-01-05 16:05:40 +03:00
tokenSHA := req . Form . Get ( "token" )
2019-11-23 02:33:31 +03:00
if len ( tokenSHA ) == 0 {
2021-01-05 16:05:40 +03:00
tokenSHA = req . Form . Get ( "access_token" )
2019-11-23 02:33:31 +03:00
}
if len ( tokenSHA ) == 0 {
// Well, check with header again.
2021-01-05 16:05:40 +03:00
auHead := req . Header . Get ( "Authorization" )
2019-11-23 02:33:31 +03:00
if len ( auHead ) > 0 {
auths := strings . Fields ( auHead )
if len ( auths ) == 2 && ( auths [ 0 ] == "token" || strings . ToLower ( auths [ 0 ] ) == "bearer" ) {
tokenSHA = auths [ 1 ]
}
}
}
if len ( tokenSHA ) == 0 {
return 0
}
// Let's see if token is valid.
if strings . Contains ( tokenSHA , "." ) {
uid := CheckOAuthAccessToken ( tokenSHA )
if uid != 0 {
2021-01-05 16:05:40 +03:00
store . GetData ( ) [ "IsApiToken" ] = true
2019-11-23 02:33:31 +03:00
}
return uid
}
t , err := models . GetAccessTokenBySHA ( tokenSHA )
if err != nil {
2020-08-01 17:45:26 +03:00
if ! models . IsErrAccessTokenNotExist ( err ) && ! models . IsErrAccessTokenEmpty ( err ) {
2019-11-23 02:33:31 +03:00
log . Error ( "GetAccessTokenBySHA: %v" , err )
}
return 0
}
t . UpdatedUnix = timeutil . TimeStampNow ( )
if err = models . UpdateAccessToken ( t ) ; err != nil {
log . Error ( "UpdateAccessToken: %v" , err )
}
2021-01-05 16:05:40 +03:00
store . GetData ( ) [ "IsApiToken" ] = true
2019-11-23 02:33:31 +03:00
return t . UID
}
// IsEnabled returns true as this plugin is enabled by default and its not possible
// to disable it from settings.
func ( o * OAuth2 ) IsEnabled ( ) bool {
return true
}
// VerifyAuthData extracts the user ID from the OAuth token in the query parameters
// or the "Authorization" header and returns the corresponding user object for that ID.
// If verification is successful returns an existing user object.
// Returns nil if verification fails.
2021-01-06 04:38:00 +03:00
func ( o * OAuth2 ) VerifyAuthData ( req * http . Request , w http . ResponseWriter , store DataStore , sess SessionStore ) * models . User {
2019-11-23 02:33:31 +03:00
if ! models . HasEngine {
return nil
}
2021-01-30 11:55:53 +03:00
if middleware . IsInternalPath ( req ) || ! middleware . IsAPIPath ( req ) && ! isAttachmentDownload ( req ) {
2019-11-23 02:33:31 +03:00
return nil
}
2021-01-05 16:05:40 +03:00
id := o . userIDFromToken ( req , store )
2019-11-23 02:33:31 +03:00
if id <= 0 {
return nil
}
2021-05-09 19:04:53 +03:00
log . Trace ( "OAuth2 Authorization: Found token for user[%d]" , id )
2019-11-23 02:33:31 +03:00
user , err := models . GetUserByID ( id )
if err != nil {
if ! models . IsErrUserNotExist ( err ) {
log . Error ( "GetUserByName: %v" , err )
}
return nil
}
2021-05-09 19:04:53 +03:00
log . Trace ( "OAuth2 Authorization: Logged in user %-v" , user )
2019-11-23 02:33:31 +03:00
return user
}