This commit is contained in:
@@ -9,13 +9,13 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/webauthn"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/auth/webauthn"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/session"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
user_service "gitea.dev/services/user"
|
||||
)
|
||||
|
||||
type ErrUserAuthMessage string
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// Based on https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#secure-remember-me-cookies
|
||||
@@ -57,6 +57,10 @@ func CheckAuthToken(ctx context.Context, value string) (*auth_model.AuthToken, e
|
||||
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(hex.EncodeToString(hashedToken[:]))) == 0 {
|
||||
// If an attacker steals a token and uses the token to create a new session the hash gets updated.
|
||||
// When the victim uses the old token the hashes don't match anymore and the victim should be notified about the compromised token.
|
||||
// Revoke the token so the attacker's rotated token (which shares this ID) can no longer be used.
|
||||
if err := auth_model.DeleteAuthTokenByID(ctx, t.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, ErrAuthTokenInvalidHash
|
||||
}
|
||||
|
||||
@@ -64,10 +68,7 @@ func CheckAuthToken(ctx context.Context, value string) (*auth_model.AuthToken, e
|
||||
}
|
||||
|
||||
func RegenerateAuthToken(ctx context.Context, t *auth_model.AuthToken) (*auth_model.AuthToken, string, error) {
|
||||
token, hash, err := generateTokenAndHash()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
token, hash := generateTokenAndHash()
|
||||
|
||||
newToken := &auth_model.AuthToken{
|
||||
ID: t.ID,
|
||||
@@ -89,16 +90,9 @@ func CreateAuthTokenForUserID(ctx context.Context, userID int64) (*auth_model.Au
|
||||
ExpiresUnix: timeutil.TimeStampNow().AddDuration(time.Duration(setting.LogInRememberDays*24) * time.Hour),
|
||||
}
|
||||
|
||||
var err error
|
||||
t.ID, err = util.CryptoRandomString(10)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
t.ID = util.CryptoRandomString(10)
|
||||
|
||||
token, hash, err := generateTokenAndHash()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
token, hash := generateTokenAndHash()
|
||||
|
||||
t.TokenHash = hash
|
||||
|
||||
@@ -109,15 +103,12 @@ func CreateAuthTokenForUserID(ctx context.Context, userID int64) (*auth_model.Au
|
||||
return t, token, nil
|
||||
}
|
||||
|
||||
func generateTokenAndHash() (string, string, error) {
|
||||
buf, err := util.CryptoRandomBytes(32)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
func generateTokenAndHash() (string, string) {
|
||||
buf := util.CryptoRandomBytes(32)
|
||||
|
||||
token := hex.EncodeToString(buf)
|
||||
|
||||
hashedToken := sha256.Sum256([]byte(token))
|
||||
|
||||
return token, hex.EncodeToString(hashedToken[:]), nil
|
||||
return token, hex.EncodeToString(hashedToken[:])
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -62,7 +63,9 @@ func TestCheckAuthToken(t *testing.T) {
|
||||
assert.ErrorIs(t, err, ErrAuthTokenInvalidHash)
|
||||
assert.Nil(t, at2)
|
||||
|
||||
assert.NoError(t, auth_model.DeleteAuthTokenByID(t.Context(), at.ID))
|
||||
// a hash mismatch signals a compromised token, which must be revoked
|
||||
_, err = auth_model.GetAuthTokenByID(t.Context(), at.ID)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
})
|
||||
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/httpauth"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
actions_model "gitea.dev/models/actions"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/auth/httpauth"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// Ensure the struct implements the interface.
|
||||
@@ -68,7 +69,7 @@ func (b *Basic) parseAuthBasic(req *http.Request) (ret struct{ authToken, uname,
|
||||
|
||||
// VerifyAuthToken only the access token provided as parameter, used by other auth methods that want to reuse access token verification logic
|
||||
func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore, authToken string) (*user_model.User, error) {
|
||||
// get oauth2 token's user's ID and access scope
|
||||
// get oauth2 token's user's ID
|
||||
accessTokenScope, uid := GetOAuthAccessTokenScopeAndUserID(req.Context(), authToken)
|
||||
if uid != 0 {
|
||||
log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid)
|
||||
@@ -104,8 +105,8 @@ func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store
|
||||
store.GetData()["IsApiToken"] = true
|
||||
store.GetData()["ApiTokenScope"] = token.Scope
|
||||
return u, nil
|
||||
} else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
|
||||
log.Error("GetAccessTokenBySha: %v", err)
|
||||
} else if !errors.Is(err, util.ErrNotExist) {
|
||||
log.Error("GetAccessTokenBySHA: %v", err)
|
||||
}
|
||||
|
||||
// check task token
|
||||
@@ -176,7 +177,8 @@ func validateTOTP(req *http.Request, u *user_model.User) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
if ok, err := twofa.ValidateTOTP(req.Header.Get("X-Gitea-OTP")); err != nil {
|
||||
// Consume the passcode atomically so a captured OTP cannot be replayed within its validity window.
|
||||
if ok, err := twofa.ValidateAndConsumeTOTP(req.Context(), req.Header.Get("X-Gitea-OTP")); err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return util.NewInvalidArgumentErrorf("invalid provided OTP")
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
user_model "gitea.dev/models/user"
|
||||
)
|
||||
|
||||
// Ensure the struct implements the interface.
|
||||
|
||||
@@ -11,11 +11,11 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
asymkey_model "gitea.dev/models/asymkey"
|
||||
"gitea.dev/models/db"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/42wim/httpsig"
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -78,8 +78,6 @@ func (h *HTTPSign) Verify(req *http.Request, w http.ResponseWriter, store DataSt
|
||||
return nil, err
|
||||
}
|
||||
|
||||
store.GetData()["IsApiToken"] = true
|
||||
|
||||
log.Trace("HTTP Sign: Logged in user %-v", u)
|
||||
|
||||
return u, nil
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/session"
|
||||
)
|
||||
|
||||
type DataStore = reqctx.ContextDataProvider
|
||||
|
||||
@@ -6,7 +6,7 @@ package auth
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"gitea.dev/models/unittest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
||||
@@ -11,16 +11,16 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/httpauth"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/actions"
|
||||
"code.gitea.io/gitea/services/oauth2_provider"
|
||||
actions_model "gitea.dev/models/actions"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/auth/httpauth"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/services/actions"
|
||||
"gitea.dev/services/oauth2_provider"
|
||||
)
|
||||
|
||||
var _ Method = &OAuth2{}
|
||||
@@ -128,7 +128,7 @@ func (o *OAuth2) userFromToken(ctx context.Context, tokenSHA string, store DataS
|
||||
}
|
||||
t, err := auth_model.GetAccessTokenBySHA(ctx, tokenSHA)
|
||||
if err != nil {
|
||||
if auth_model.IsErrAccessTokenNotExist(err) {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
// check task token
|
||||
if task, err := actions_model.GetRunningTaskByToken(ctx, tokenSHA); err == nil {
|
||||
log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
|
||||
|
||||
@@ -6,10 +6,10 @@ package auth
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/services/actions"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/services/actions"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
gouuid "github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ package auth
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
)
|
||||
|
||||
// Ensure the struct implements the interface.
|
||||
|
||||
@@ -7,18 +7,20 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/services/auth/source/oauth2"
|
||||
"gitea.dev/services/auth/source/smtp"
|
||||
|
||||
_ "code.gitea.io/gitea/services/auth/source/db" // register the sources (and below)
|
||||
_ "code.gitea.io/gitea/services/auth/source/ldap" // register the ldap source
|
||||
_ "code.gitea.io/gitea/services/auth/source/pam" // register the pam source
|
||||
_ "code.gitea.io/gitea/services/auth/source/sspi" // register the sspi source
|
||||
_ "gitea.dev/services/auth/source/db" // register the sources (and below)
|
||||
_ "gitea.dev/services/auth/source/ldap" // register the ldap source
|
||||
_ "gitea.dev/services/auth/source/pam" // register the pam source
|
||||
_ "gitea.dev/services/auth/source/sspi" // register the sspi source
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// UserSignIn validates user name and password.
|
||||
@@ -27,9 +29,8 @@ func UserSignIn(ctx context.Context, username, password string) (*user_model.Use
|
||||
isEmail := false
|
||||
if strings.Contains(username, "@") {
|
||||
isEmail = true
|
||||
emailAddress := user_model.EmailAddress{LowerEmail: strings.ToLower(strings.TrimSpace(username))}
|
||||
// check same email
|
||||
has, err := db.GetEngine(ctx).Get(&emailAddress)
|
||||
emailAddress, has, err := db.Get[user_model.EmailAddress](ctx, builder.Eq{"lower_email": strings.ToLower(strings.TrimSpace(username))})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -51,9 +52,23 @@ func UserSignIn(ctx context.Context, username, password string) (*user_model.Use
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
hasUser, err := user_model.GetIndividualUser(ctx, user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
var hasUser bool
|
||||
var err error
|
||||
if user.ID > 0 {
|
||||
user, err = user_model.GetUserByID(ctx, user.ID)
|
||||
if err != nil && !user_model.IsErrUserNotExist(err) {
|
||||
return nil, nil, err
|
||||
}
|
||||
if user != nil && user.Type != user_model.UserTypeIndividual {
|
||||
return nil, nil, user_model.ErrUserNotExist{Name: username}
|
||||
}
|
||||
hasUser = user != nil
|
||||
} else if user.LowerName != "" {
|
||||
user, err = user_model.GetIndividualUserByName(ctx, user.LowerName)
|
||||
if err != nil && !user_model.IsErrUserNotExist(err) {
|
||||
return nil, nil, err
|
||||
}
|
||||
hasUser = user != nil
|
||||
}
|
||||
|
||||
if hasUser {
|
||||
|
||||
@@ -6,9 +6,9 @@ package auth
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
user_model "gitea.dev/models/user"
|
||||
)
|
||||
|
||||
// DeleteSource deletes a AuthSource record in DB.
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
package db_test
|
||||
|
||||
import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/db"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/services/auth"
|
||||
"gitea.dev/services/auth/source/db"
|
||||
)
|
||||
|
||||
// This test file exists to assert that our Source exposes the interfaces that we expect
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// ErrUserPasswordNotSet represents a "ErrUserPasswordNotSet" kind of error.
|
||||
|
||||
@@ -6,8 +6,8 @@ package db
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"gitea.dev/models/auth"
|
||||
user_model "gitea.dev/models/user"
|
||||
)
|
||||
|
||||
// Source is a password authentication service
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
package ldap_test
|
||||
|
||||
import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/services/auth"
|
||||
"gitea.dev/services/auth/source/ldap"
|
||||
)
|
||||
|
||||
// This test file exists to assert that our Source exposes the interfaces that we expect
|
||||
|
||||
@@ -6,11 +6,11 @@ package ldap
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/secret"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/secret"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
// .____ ________ _____ __________
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
auth_module "code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
source_service "code.gitea.io/gitea/services/auth/source"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
asymkey_model "gitea.dev/models/asymkey"
|
||||
"gitea.dev/models/auth"
|
||||
user_model "gitea.dev/models/user"
|
||||
auth_module "gitea.dev/modules/auth"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
asymkey_service "gitea.dev/services/asymkey"
|
||||
source_service "gitea.dev/services/auth/source"
|
||||
user_service "gitea.dev/services/user"
|
||||
)
|
||||
|
||||
// Authenticate queries if login/password is valid against the LDAP directory pool,
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/log"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
@@ -116,11 +116,12 @@ func dial(source *Source) (*ldap.Conn, error) {
|
||||
InsecureSkipVerify: source.SkipVerify,
|
||||
}
|
||||
|
||||
hostPort := net.JoinHostPort(source.Host, strconv.Itoa(source.Port))
|
||||
if source.SecurityProtocol == SecurityProtocolLDAPS {
|
||||
return ldap.DialTLS("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)), tlsConfig) //nolint:staticcheck // DialTLS is deprecated
|
||||
return ldap.DialURL("ldaps://"+hostPort, ldap.DialWithTLSConfig(tlsConfig))
|
||||
}
|
||||
|
||||
conn, err := ldap.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port))) //nolint:staticcheck // Dial is deprecated
|
||||
conn, err := ldap.DialURL("ldap://" + hostPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error during Dial: %w", err)
|
||||
}
|
||||
|
||||
@@ -7,17 +7,17 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
auth_module "code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
source_service "code.gitea.io/gitea/services/auth/source"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
asymkey_model "gitea.dev/models/asymkey"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/organization"
|
||||
user_model "gitea.dev/models/user"
|
||||
auth_module "gitea.dev/modules/auth"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
asymkey_service "gitea.dev/services/asymkey"
|
||||
source_service "gitea.dev/services/auth/source"
|
||||
user_service "gitea.dev/services/user"
|
||||
)
|
||||
|
||||
// Sync causes this ldap source to synchronize its users with the db
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
package oauth2_test
|
||||
|
||||
import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/services/auth"
|
||||
"gitea.dev/services/auth/source/oauth2"
|
||||
)
|
||||
|
||||
// This test file exists to assert that our Source exposes the interfaces that we expect
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/sessions"
|
||||
@@ -72,7 +72,7 @@ func initOAuth2Sources(ctx context.Context) error {
|
||||
}
|
||||
err := oauth2Source.RegisterSource()
|
||||
if err != nil {
|
||||
log.Critical("Unable to register source: %s due to Error: %v.", source.Name, err)
|
||||
log.Error("Unable to register source: %s due to Error: %v.", source.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -6,7 +6,7 @@ package oauth2
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"gitea.dev/models/unittest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
||||
@@ -13,11 +13,11 @@ import (
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/openidConnect"
|
||||
|
||||
@@ -6,8 +6,8 @@ package oauth2
|
||||
import (
|
||||
"html/template"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/svg"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/svg"
|
||||
)
|
||||
|
||||
// BaseProvider represents a common base for Provider
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/azureadv2"
|
||||
@@ -120,4 +120,25 @@ func init() {
|
||||
}), nil
|
||||
},
|
||||
))
|
||||
|
||||
RegisterGothProvider(&AwsCognitoProvider{})
|
||||
}
|
||||
|
||||
const ProviderNameAwsCognito = "aws-cognito"
|
||||
|
||||
// AwsCognitoProvider is a GothProvider for AWS Cognito (based on OpenID Connect)
|
||||
type AwsCognitoProvider struct {
|
||||
OpenIDProvider
|
||||
}
|
||||
|
||||
// Name provides the technical name for this provider
|
||||
func (c *AwsCognitoProvider) Name() string {
|
||||
return ProviderNameAwsCognito
|
||||
}
|
||||
|
||||
// DisplayName returns the friendly name for this provider
|
||||
func (c *AwsCognitoProvider) DisplayName() string {
|
||||
return "AWS Cognito"
|
||||
}
|
||||
|
||||
var _ GothProvider = &AwsCognitoProvider{}
|
||||
|
||||
@@ -6,9 +6,9 @@ package oauth2
|
||||
import (
|
||||
"html/template"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/svg"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/svg"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/openidConnect"
|
||||
@@ -46,8 +46,14 @@ func (o *OpenIDProvider) CreateGothProvider(providerName, callbackURL string, so
|
||||
provider, err := openidConnect.New(source.ClientID, source.ClientSecret, callbackURL, source.OpenIDConnectAutoDiscoveryURL, scopes...)
|
||||
if err != nil {
|
||||
log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, source.OpenIDConnectAutoDiscoveryURL, err)
|
||||
return nil, err
|
||||
}
|
||||
return provider, err
|
||||
if source.ExternalIDClaim != "" {
|
||||
// UserIdClaims is a fallback list; goth returns the first non-empty matching claim.
|
||||
// A single entry is sufficient because the admin explicitly chooses one claim (e.g. "oid" for Azure AD).
|
||||
provider.UserIdClaims = []string{source.ExternalIDClaim}
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// CustomURLSettings returns the custom url settings for this provider
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/azuread"
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/modules/json"
|
||||
)
|
||||
|
||||
// Source holds configuration for the OAuth2 login source.
|
||||
@@ -30,6 +30,7 @@ type Source struct {
|
||||
|
||||
SSHPublicKeyClaimName string
|
||||
FullNameClaimName string
|
||||
ExternalIDClaim string
|
||||
}
|
||||
|
||||
// FromDB fills up an OAuth2Config from serialized format.
|
||||
|
||||
@@ -6,8 +6,8 @@ package oauth2
|
||||
import (
|
||||
"context"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/services/auth/source/db"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/services/auth/source/db"
|
||||
)
|
||||
|
||||
// Authenticate falls back to the db authenticator
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -61,13 +61,7 @@ func (source *Source) refresh(ctx context.Context, provider goth.Provider, u *us
|
||||
}
|
||||
}
|
||||
|
||||
user := &user_model.User{
|
||||
LoginName: u.ExternalID,
|
||||
LoginType: auth.OAuth2,
|
||||
LoginSource: u.LoginSourceID,
|
||||
}
|
||||
|
||||
hasUser, err := user_model.GetIndividualUser(ctx, user)
|
||||
user, hasUser, err := user_model.GetIndividualUserByLoginSource(ctx, auth.OAuth2, u.LoginSourceID, u.ExternalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -77,19 +71,17 @@ func (source *Source) refresh(ctx context.Context, provider goth.Provider, u *us
|
||||
// recognizes them as a valid user, they will be able to login
|
||||
// via their provider and reactivate their account.
|
||||
if shouldDisable {
|
||||
log.Info("SyncExternalUsers[%s] disabling user %d", source.AuthSource.Name, user.ID)
|
||||
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if hasUser {
|
||||
log.Info("SyncExternalUsers[%s] disabling user %d", source.AuthSource.Name, user.ID)
|
||||
user.IsActive = false
|
||||
err := user_model.UpdateUserCols(ctx, user, "is_active")
|
||||
if err != nil {
|
||||
if err := user_model.UpdateUserCols(ctx, user, "is_active"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete stored tokens, since they are invalid. This
|
||||
// also provents us from checking this in subsequent runs.
|
||||
// HINT: OAUTH-AUTO-SYNC-USER-ACTIVATION
|
||||
// Delete stored tokens, since they are invalid. This also prevents us from checking this in subsequent runs.
|
||||
u.AccessToken = ""
|
||||
u.RefreshToken = ""
|
||||
u.ExpiresAt = time.Time{}
|
||||
|
||||
@@ -6,9 +6,9 @@ package oauth2
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -57,12 +57,7 @@ func TestSource(t *testing.T) {
|
||||
err := source.refresh(t.Context(), provider, e)
|
||||
assert.NoError(t, err)
|
||||
|
||||
e := &user_model.ExternalLoginUser{
|
||||
ExternalID: e.ExternalID,
|
||||
LoginSourceID: e.LoginSourceID,
|
||||
}
|
||||
|
||||
ok, err := user_model.GetExternalLogin(t.Context(), e)
|
||||
e, ok, err := user_model.GetExternalLogin(t.Context(), e.LoginSourceID, e.ExternalID)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "refresh", e.RefreshToken)
|
||||
@@ -82,12 +77,7 @@ func TestSource(t *testing.T) {
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
e := &user_model.ExternalLoginUser{
|
||||
ExternalID: e.ExternalID,
|
||||
LoginSourceID: e.LoginSourceID,
|
||||
}
|
||||
|
||||
ok, err := user_model.GetExternalLogin(t.Context(), e)
|
||||
e, ok, err := user_model.GetExternalLogin(t.Context(), e.LoginSourceID, e.ExternalID)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, ok)
|
||||
assert.Empty(t, e.RefreshToken)
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
session_module "code.gitea.io/gitea/modules/session"
|
||||
"gitea.dev/modules/log"
|
||||
session_module "gitea.dev/modules/session"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
package pam_test
|
||||
|
||||
import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/pam"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/services/auth"
|
||||
"gitea.dev/services/auth/source/pam"
|
||||
)
|
||||
|
||||
// This test file exists to assert that our Source exposes the interfaces that we expect
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
package pam
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/modules/json"
|
||||
)
|
||||
|
||||
// __________ _____ _____
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/pam"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gitea.dev/models/auth"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/auth/pam"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
package smtp_test
|
||||
|
||||
import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/services/auth"
|
||||
"gitea.dev/services/auth/source/smtp"
|
||||
)
|
||||
|
||||
// This test file exists to assert that our Source exposes the interfaces that we expect
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/modules/json"
|
||||
)
|
||||
|
||||
// _________ __________________________
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
auth_model "gitea.dev/models/auth"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// Authenticate queries if the provided login/password is authenticates against the SMTP server
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
org_service "code.gitea.io/gitea/services/org"
|
||||
"gitea.dev/models/organization"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/log"
|
||||
org_service "gitea.dev/services/org"
|
||||
)
|
||||
|
||||
type syncType int
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
package sspi_test
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/sspi"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/services/auth/source/sspi"
|
||||
)
|
||||
|
||||
// This test file exists to assert that our Source exposes the interfaces that we expect
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
package sspi
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/modules/json"
|
||||
)
|
||||
|
||||
// _________ ___________________.___
|
||||
|
||||
@@ -10,15 +10,15 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/services/auth/source/sspi"
|
||||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/services/auth/source/sspi"
|
||||
gitea_context "gitea.dev/services/context"
|
||||
|
||||
gouuid "github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -6,9 +6,9 @@ package auth
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/log"
|
||||
)
|
||||
|
||||
// SyncExternalUsers is used to synchronize users with external authorization source
|
||||
|
||||
Reference in New Issue
Block a user