mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-09-10 20:22:14 +02:00
refactor auth interface to return error when verify failure (#22119)
This PR changed the Auth interface signature from `Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User` to `Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error)`. There is a new return argument `error` which means the verification condition matched but verify process failed, we should stop the auth process. Before this PR, when return a `nil` user, we don't know the reason why it returned `nil`. If the match condition is not satisfied or it verified failure? For these two different results, we should have different handler. If the match condition is not satisfied, we should try next auth method and if there is no more auth method, it's an anonymous user. If the condition matched but verify failed, the auth process should be stop and return immediately. This will fix #20563 Co-authored-by: KN4CK3R <admin@oldschoolhack.me> Co-authored-by: Jason Song <i@wolfogre.com>
This commit is contained in:
parent
7cc7db73b9
commit
ca67c5a8a7
15 changed files with 111 additions and 79 deletions
|
@ -219,7 +219,13 @@ func (ctx *APIContext) CheckForOTP() {
|
||||||
func APIAuth(authMethod auth_service.Method) func(*APIContext) {
|
func APIAuth(authMethod auth_service.Method) func(*APIContext) {
|
||||||
return func(ctx *APIContext) {
|
return func(ctx *APIContext) {
|
||||||
// Get user from session if logged in.
|
// Get user from session if logged in.
|
||||||
ctx.Doer = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
var err error
|
||||||
|
ctx.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusUnauthorized, "APIAuth", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.Doer != nil {
|
if ctx.Doer != nil {
|
||||||
if ctx.Locale.Language() != ctx.Doer.Language {
|
if ctx.Locale.Language() != ctx.Doer.Language {
|
||||||
ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
|
ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
|
||||||
|
|
|
@ -662,7 +662,13 @@ func getCsrfOpts() CsrfOptions {
|
||||||
// Auth converts auth.Auth as a middleware
|
// Auth converts auth.Auth as a middleware
|
||||||
func Auth(authMethod auth.Method) func(*Context) {
|
func Auth(authMethod auth.Method) func(*Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
ctx.Doer = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
var err error
|
||||||
|
ctx.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to verify user %v: %v", ctx.Req.RemoteAddr, err)
|
||||||
|
ctx.Error(http.StatusUnauthorized, "Verify")
|
||||||
|
return
|
||||||
|
}
|
||||||
if ctx.Doer != nil {
|
if ctx.Doer != nil {
|
||||||
if ctx.Locale.Language() != ctx.Doer.Language {
|
if ctx.Locale.Language() != ctx.Doer.Language {
|
||||||
ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
|
ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/packages/composer"
|
"code.gitea.io/gitea/routers/api/packages/composer"
|
||||||
|
@ -58,7 +59,13 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
|
||||||
|
|
||||||
authGroup := auth.NewGroup(authMethods...)
|
authGroup := auth.NewGroup(authMethods...)
|
||||||
r.Use(func(ctx *context.Context) {
|
r.Use(func(ctx *context.Context) {
|
||||||
ctx.Doer = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
var err error
|
||||||
|
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Verify: %v", err)
|
||||||
|
ctx.Error(http.StatusUnauthorized, "authGroup.Verify")
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.IsSigned = ctx.Doer != nil
|
ctx.IsSigned = ctx.Doer != nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -321,7 +328,13 @@ func ContainerRoutes(ctx gocontext.Context) *web.Route {
|
||||||
|
|
||||||
authGroup := auth.NewGroup(authMethods...)
|
authGroup := auth.NewGroup(authMethods...)
|
||||||
r.Use(func(ctx *context.Context) {
|
r.Use(func(ctx *context.Context) {
|
||||||
ctx.Doer = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
var err error
|
||||||
|
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to verify user: %v", err)
|
||||||
|
ctx.Error(http.StatusUnauthorized, "Verify")
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.IsSigned = ctx.Doer != nil
|
ctx.IsSigned = ctx.Doer != nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -19,22 +19,22 @@ func (a *Auth) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify extracts the user from the Bearer token
|
// Verify extracts the user from the Bearer token
|
||||||
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) *user_model.User {
|
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
|
||||||
uid, err := packages.ParseAuthorizationToken(req)
|
uid, err := packages.ParseAuthorizationToken(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace("ParseAuthorizationToken: %v", err)
|
log.Trace("ParseAuthorizationToken: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if uid == 0 {
|
if uid == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := user_model.GetUserByID(req.Context(), uid)
|
u, err := user_model.GetUserByID(req.Context(), uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetUserByID: %v", err)
|
log.Error("GetUserByID: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return u
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,25 +20,25 @@ func (a *Auth) Name() string {
|
||||||
|
|
||||||
// Verify extracts the user from the Bearer token
|
// Verify extracts the user from the Bearer token
|
||||||
// If it's an anonymous session a ghost user is returned
|
// If it's an anonymous session a ghost user is returned
|
||||||
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) *user_model.User {
|
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
|
||||||
uid, err := packages.ParseAuthorizationToken(req)
|
uid, err := packages.ParseAuthorizationToken(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace("ParseAuthorizationToken: %v", err)
|
log.Trace("ParseAuthorizationToken: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if uid == 0 {
|
if uid == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if uid == -1 {
|
if uid == -1 {
|
||||||
return user_model.NewGhostUser()
|
return user_model.NewGhostUser(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := user_model.GetUserByID(req.Context(), uid)
|
u, err := user_model.GetUserByID(req.Context(), uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetUserByID: %v", err)
|
log.Error("GetUserByID: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return u
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,19 +20,20 @@ func (a *Auth) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#request-parameters
|
// https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#request-parameters
|
||||||
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) *user_model.User {
|
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
|
||||||
token, err := auth_model.GetAccessTokenBySHA(req.Header.Get("X-NuGet-ApiKey"))
|
token, err := auth_model.GetAccessTokenBySHA(req.Header.Get("X-NuGet-ApiKey"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !(auth_model.IsErrAccessTokenNotExist(err) || auth_model.IsErrAccessTokenEmpty(err)) {
|
if !(auth_model.IsErrAccessTokenNotExist(err) || auth_model.IsErrAccessTokenEmpty(err)) {
|
||||||
log.Error("GetAccessTokenBySHA: %v", err)
|
log.Error("GetAccessTokenBySHA: %v", err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := user_model.GetUserByID(req.Context(), token.UID)
|
u, err := user_model.GetUserByID(req.Context(), token.UID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetUserByID: %v", err)
|
log.Error("GetUserByID: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
token.UpdatedUnix = timeutil.TimeStampNow()
|
token.UpdatedUnix = timeutil.TimeStampNow()
|
||||||
|
@ -40,5 +41,5 @@ func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataS
|
||||||
log.Error("UpdateAccessToken: %v", err)
|
log.Error("UpdateAccessToken: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return u
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,20 +40,20 @@ func (b *Basic) Name() string {
|
||||||
// "Authorization" header of the request and returns the corresponding user object for that
|
// "Authorization" header of the request and returns the corresponding user object for that
|
||||||
// name/token on successful validation.
|
// name/token on successful validation.
|
||||||
// Returns nil if header is empty or validation fails.
|
// Returns nil if header is empty or validation fails.
|
||||||
func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
|
func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
// Basic authentication should only fire on API, Download or on Git or LFSPaths
|
// Basic authentication should only fire on API, Download or on Git or LFSPaths
|
||||||
if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {
|
if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
baHead := req.Header.Get("Authorization")
|
baHead := req.Header.Get("Authorization")
|
||||||
if len(baHead) == 0 {
|
if len(baHead) == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
auths := strings.SplitN(baHead, " ", 2)
|
auths := strings.SplitN(baHead, " ", 2)
|
||||||
if len(auths) != 2 || (strings.ToLower(auths[0]) != "basic") {
|
if len(auths) != 2 || (strings.ToLower(auths[0]) != "basic") {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
uname, passwd, _ := base.BasicAuthDecode(auths[1])
|
uname, passwd, _ := base.BasicAuthDecode(auths[1])
|
||||||
|
@ -77,11 +77,11 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||||
u, err := user_model.GetUserByID(req.Context(), uid)
|
u, err := user_model.GetUserByID(req.Context(), uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetUserByID: %v", err)
|
log.Error("GetUserByID: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
store.GetData()["IsApiToken"] = true
|
store.GetData()["IsApiToken"] = true
|
||||||
return u
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := auth_model.GetAccessTokenBySHA(authToken)
|
token, err := auth_model.GetAccessTokenBySHA(authToken)
|
||||||
|
@ -90,7 +90,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||||
u, err := user_model.GetUserByID(req.Context(), token.UID)
|
u, err := user_model.GetUserByID(req.Context(), token.UID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetUserByID: %v", err)
|
log.Error("GetUserByID: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
token.UpdatedUnix = timeutil.TimeStampNow()
|
token.UpdatedUnix = timeutil.TimeStampNow()
|
||||||
|
@ -99,13 +99,13 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
store.GetData()["IsApiToken"] = true
|
store.GetData()["IsApiToken"] = true
|
||||||
return u
|
return u, nil
|
||||||
} else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
|
} else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
|
||||||
log.Error("GetAccessTokenBySha: %v", err)
|
log.Error("GetAccessTokenBySha: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !setting.Service.EnableBasicAuth {
|
if !setting.Service.EnableBasicAuth {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Basic Authorization: Attempting SignIn for %s", uname)
|
log.Trace("Basic Authorization: Attempting SignIn for %s", uname)
|
||||||
|
@ -114,7 +114,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||||
if !user_model.IsErrUserNotExist(err) {
|
if !user_model.IsErrUserNotExist(err) {
|
||||||
log.Error("UserSignIn: %v", err)
|
log.Error("UserSignIn: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipper, ok := source.Cfg.(LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() {
|
if skipper, ok := source.Cfg.(LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() {
|
||||||
|
@ -123,5 +123,5 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
|
||||||
|
|
||||||
log.Trace("Basic Authorization: Logged in user %-v", u)
|
log.Trace("Basic Authorization: Logged in user %-v", u)
|
||||||
|
|
||||||
return u
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,23 +79,23 @@ func (b *Group) Free() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify extracts and validates
|
// Verify extracts and validates
|
||||||
func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
|
func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
if !db.HasEngine {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to sign in with each of the enabled plugins
|
// Try to sign in with each of the enabled plugins
|
||||||
for _, ssoMethod := range b.methods {
|
for _, ssoMethod := range b.methods {
|
||||||
user := ssoMethod.Verify(req, w, store, sess)
|
user, err := ssoMethod.Verify(req, w, store, sess)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if user != nil {
|
if user != nil {
|
||||||
if store.GetData()["AuthedMethod"] == nil {
|
if store.GetData()["AuthedMethod"] == nil {
|
||||||
if named, ok := ssoMethod.(Named); ok {
|
if named, ok := ssoMethod.(Named); ok {
|
||||||
store.GetData()["AuthedMethod"] = named.Name()
|
store.GetData()["AuthedMethod"] = named.Name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return user
|
return user, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,10 @@ func (h *HTTPSign) Name() string {
|
||||||
// Verify extracts and validates HTTPsign from the Signature header of the request and returns
|
// Verify extracts and validates HTTPsign from the Signature header of the request and returns
|
||||||
// the corresponding user object on successful validation.
|
// the corresponding user object on successful validation.
|
||||||
// Returns nil if header is empty or validation fails.
|
// Returns nil if header is empty or validation fails.
|
||||||
func (h *HTTPSign) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
|
func (h *HTTPSign) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
sigHead := req.Header.Get("Signature")
|
sigHead := req.Header.Get("Signature")
|
||||||
if len(sigHead) == 0 {
|
if len(sigHead) == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -53,14 +53,14 @@ func (h *HTTPSign) Verify(req *http.Request, w http.ResponseWriter, store DataSt
|
||||||
if len(req.Header.Get("X-Ssh-Certificate")) != 0 {
|
if len(req.Header.Get("X-Ssh-Certificate")) != 0 {
|
||||||
// Handle Signature signed by SSH certificates
|
// Handle Signature signed by SSH certificates
|
||||||
if len(setting.SSH.TrustedUserCAKeys) == 0 {
|
if len(setting.SSH.TrustedUserCAKeys) == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
publicKey, err = VerifyCert(req)
|
publicKey, err = VerifyCert(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("VerifyCert on request from %s: failed: %v", req.RemoteAddr, err)
|
log.Debug("VerifyCert on request from %s: failed: %v", req.RemoteAddr, err)
|
||||||
log.Warn("Failed authentication attempt from %s", req.RemoteAddr)
|
log.Warn("Failed authentication attempt from %s", req.RemoteAddr)
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle Signature signed by Public Key
|
// Handle Signature signed by Public Key
|
||||||
|
@ -68,21 +68,21 @@ func (h *HTTPSign) Verify(req *http.Request, w http.ResponseWriter, store DataSt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("VerifyPubKey on request from %s: failed: %v", req.RemoteAddr, err)
|
log.Debug("VerifyPubKey on request from %s: failed: %v", req.RemoteAddr, err)
|
||||||
log.Warn("Failed authentication attempt from %s", req.RemoteAddr)
|
log.Warn("Failed authentication attempt from %s", req.RemoteAddr)
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := user_model.GetUserByID(req.Context(), publicKey.OwnerID)
|
u, err := user_model.GetUserByID(req.Context(), publicKey.OwnerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetUserByID: %v", err)
|
log.Error("GetUserByID: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
store.GetData()["IsApiToken"] = true
|
store.GetData()["IsApiToken"] = true
|
||||||
|
|
||||||
log.Trace("HTTP Sign: Logged in user %-v", u)
|
log.Trace("HTTP Sign: Logged in user %-v", u)
|
||||||
|
|
||||||
return u
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyPubKey(r *http.Request) (*asymkey_model.PublicKey, error) {
|
func VerifyPubKey(r *http.Request) (*asymkey_model.PublicKey, error) {
|
||||||
|
|
|
@ -24,8 +24,9 @@ type Method interface {
|
||||||
// If verification is successful returns either an existing user object (with id > 0)
|
// If verification is successful returns either an existing user object (with id > 0)
|
||||||
// or a new user object (with id = 0) populated with the information that was found
|
// or a new user object (with id = 0) populated with the information that was found
|
||||||
// in the authentication data (username or email).
|
// in the authentication data (username or email).
|
||||||
// Returns nil if verification fails.
|
// Second argument returns err if verification fails, otherwise
|
||||||
Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User
|
// First return argument returns nil if no matched verification condition
|
||||||
|
Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializable represents a structure that requires initialization
|
// Initializable represents a structure that requires initialization
|
||||||
|
|
|
@ -108,18 +108,14 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
|
||||||
// or the "Authorization" header and returns the corresponding user object for that ID.
|
// or the "Authorization" header and returns the corresponding user object for that ID.
|
||||||
// If verification is successful returns an existing user object.
|
// If verification is successful returns an existing user object.
|
||||||
// Returns nil if verification fails.
|
// Returns nil if verification fails.
|
||||||
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
|
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
if !db.HasEngine {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) {
|
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
id := o.userIDFromToken(req, store)
|
id := o.userIDFromToken(req, store)
|
||||||
if id <= 0 {
|
if id <= 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
log.Trace("OAuth2 Authorization: Found token for user[%d]", id)
|
log.Trace("OAuth2 Authorization: Found token for user[%d]", id)
|
||||||
|
|
||||||
|
@ -128,11 +124,11 @@ func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStor
|
||||||
if !user_model.IsErrUserNotExist(err) {
|
if !user_model.IsErrUserNotExist(err) {
|
||||||
log.Error("GetUserByName: %v", err)
|
log.Error("GetUserByName: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("OAuth2 Authorization: Logged in user %-v", user)
|
log.Trace("OAuth2 Authorization: Logged in user %-v", user)
|
||||||
return user
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAuthenticatedTokenRequest(req *http.Request) bool {
|
func isAuthenticatedTokenRequest(req *http.Request) bool {
|
||||||
|
|
|
@ -51,10 +51,10 @@ func (r *ReverseProxy) Name() string {
|
||||||
// If a username is available in the "setting.ReverseProxyAuthUser" header an existing
|
// If a username is available in the "setting.ReverseProxyAuthUser" header an existing
|
||||||
// user object is returned (populated with username or email found in header).
|
// user object is returned (populated with username or email found in header).
|
||||||
// Returns nil if header is empty.
|
// Returns nil if header is empty.
|
||||||
func (r *ReverseProxy) getUserFromAuthUser(req *http.Request) *user_model.User {
|
func (r *ReverseProxy) getUserFromAuthUser(req *http.Request) (*user_model.User, error) {
|
||||||
username := r.getUserName(req)
|
username := r.getUserName(req)
|
||||||
if len(username) == 0 {
|
if len(username) == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
log.Trace("ReverseProxy Authorization: Found username: %s", username)
|
log.Trace("ReverseProxy Authorization: Found username: %s", username)
|
||||||
|
|
||||||
|
@ -62,11 +62,11 @@ func (r *ReverseProxy) getUserFromAuthUser(req *http.Request) *user_model.User {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !user_model.IsErrUserNotExist(err) || !r.isAutoRegisterAllowed() {
|
if !user_model.IsErrUserNotExist(err) || !r.isAutoRegisterAllowed() {
|
||||||
log.Error("GetUserByName: %v", err)
|
log.Error("GetUserByName: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
user = r.newUser(req)
|
user = r.newUser(req)
|
||||||
}
|
}
|
||||||
return user
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getEmail extracts the email from the "setting.ReverseProxyAuthEmail" header
|
// getEmail extracts the email from the "setting.ReverseProxyAuthEmail" header
|
||||||
|
@ -106,12 +106,15 @@ func (r *ReverseProxy) getUserFromAuthEmail(req *http.Request) *user_model.User
|
||||||
// First it will attempt to load it based on the username (see docs for getUserFromAuthUser),
|
// First it will attempt to load it based on the username (see docs for getUserFromAuthUser),
|
||||||
// and failing that it will attempt to load it based on the email (see docs for getUserFromAuthEmail).
|
// and failing that it will attempt to load it based on the email (see docs for getUserFromAuthEmail).
|
||||||
// Returns nil if the headers are empty or the user is not found.
|
// Returns nil if the headers are empty or the user is not found.
|
||||||
func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
|
func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
user := r.getUserFromAuthUser(req)
|
user, err := r.getUserFromAuthUser(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
user = r.getUserFromAuthEmail(req)
|
user = r.getUserFromAuthEmail(req)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +127,7 @@ func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store Da
|
||||||
store.GetData()["IsReverseProxy"] = true
|
store.GetData()["IsReverseProxy"] = true
|
||||||
|
|
||||||
log.Trace("ReverseProxy Authorization: Logged in user %-v", user)
|
log.Trace("ReverseProxy Authorization: Logged in user %-v", user)
|
||||||
return user
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isAutoRegisterAllowed checks if EnableReverseProxyAutoRegister setting is true
|
// isAutoRegisterAllowed checks if EnableReverseProxyAutoRegister setting is true
|
||||||
|
|
|
@ -29,12 +29,12 @@ func (s *Session) Name() string {
|
||||||
// Verify checks if there is a user uid stored in the session and returns the user
|
// Verify checks if there is a user uid stored in the session and returns the user
|
||||||
// object for that uid.
|
// object for that uid.
|
||||||
// Returns nil if there is no user uid stored in the session.
|
// Returns nil if there is no user uid stored in the session.
|
||||||
func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
|
func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
user := SessionUser(sess)
|
user := SessionUser(sess)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
return user
|
return user, nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionUser returns the user object corresponding to the "uid" session variable.
|
// SessionUser returns the user object corresponding to the "uid" session variable.
|
||||||
|
|
|
@ -77,15 +77,15 @@ func (s *SSPI) Free() error {
|
||||||
// If authentication is successful, returns the corresponding user object.
|
// If authentication is successful, returns the corresponding user object.
|
||||||
// If negotiation should continue or authentication fails, immediately returns a 401 HTTP
|
// If negotiation should continue or authentication fails, immediately returns a 401 HTTP
|
||||||
// response code, as required by the SPNEGO protocol.
|
// response code, as required by the SPNEGO protocol.
|
||||||
func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
|
func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
if !s.shouldAuthenticate(req) {
|
if !s.shouldAuthenticate(req) {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := s.getConfig()
|
cfg, err := s.getConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("could not get SSPI config: %v", err)
|
log.Error("could not get SSPI config: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("SSPI Authorization: Attempting to authenticate")
|
log.Trace("SSPI Authorization: Attempting to authenticate")
|
||||||
|
@ -108,7 +108,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
|
||||||
log.Error("%v", err)
|
log.Error("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
if outToken != "" {
|
if outToken != "" {
|
||||||
sspiAuth.AppendAuthenticateHeader(w, outToken)
|
sspiAuth.AppendAuthenticateHeader(w, outToken)
|
||||||
|
@ -116,7 +116,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
|
||||||
|
|
||||||
username := sanitizeUsername(userInfo.Username, cfg)
|
username := sanitizeUsername(userInfo.Username, cfg)
|
||||||
if len(username) == 0 {
|
if len(username) == 0 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
log.Info("Authenticated as %s\n", username)
|
log.Info("Authenticated as %s\n", username)
|
||||||
|
|
||||||
|
@ -124,16 +124,16 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !user_model.IsErrUserNotExist(err) {
|
if !user_model.IsErrUserNotExist(err) {
|
||||||
log.Error("GetUserByName: %v", err)
|
log.Error("GetUserByName: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
if !cfg.AutoCreateUsers {
|
if !cfg.AutoCreateUsers {
|
||||||
log.Error("User '%s' not found", username)
|
log.Error("User '%s' not found", username)
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
user, err = s.newUser(username, cfg)
|
user, err = s.newUser(username, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("CreateUser: %v", err)
|
log.Error("CreateUser: %v", err)
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("SSPI Authorization: Logged in user %-v", user)
|
log.Trace("SSPI Authorization: Logged in user %-v", user)
|
||||||
return user
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getConfig retrieves the SSPI configuration from login sources
|
// getConfig retrieves the SSPI configuration from login sources
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
@ -41,9 +42,15 @@ func CreateAuthorizationToken(u *user_model.User) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseAuthorizationToken(req *http.Request) (int64, error) {
|
func ParseAuthorizationToken(req *http.Request) (int64, error) {
|
||||||
parts := strings.SplitN(req.Header.Get("Authorization"), " ", 2)
|
h := req.Header.Get("Authorization")
|
||||||
|
if h == "" {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(h, " ", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return 0, fmt.Errorf("no token")
|
log.Error("split token failed: %s", h)
|
||||||
|
return 0, fmt.Errorf("split token failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (interface{}, error) {
|
token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (interface{}, error) {
|
||||||
|
|
Loading…
Reference in a new issue