From e68b9d00a6e05b3a941f63ffb696f91e554ac5ec Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.3. Signed-off-by: Daniel Baumann --- services/auth/source/pam/assert_interface_test.go | 21 +++++++ services/auth/source/pam/source.go | 45 ++++++++++++++ services/auth/source/pam/source_authenticate.go | 76 +++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 services/auth/source/pam/assert_interface_test.go create mode 100644 services/auth/source/pam/source.go create mode 100644 services/auth/source/pam/source_authenticate.go (limited to 'services/auth/source/pam') diff --git a/services/auth/source/pam/assert_interface_test.go b/services/auth/source/pam/assert_interface_test.go new file mode 100644 index 0000000..8e7648b --- /dev/null +++ b/services/auth/source/pam/assert_interface_test.go @@ -0,0 +1,21 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +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" +) + +// This test file exists to assert that our Source exposes the interfaces that we expect +// It tightly binds the interfaces and implementation without breaking go import cycles + +type sourceInterface interface { + auth.PasswordAuthenticator + auth_model.Config + auth_model.SourceSettable +} + +var _ (sourceInterface) = &pam.Source{} diff --git a/services/auth/source/pam/source.go b/services/auth/source/pam/source.go new file mode 100644 index 0000000..96b182e --- /dev/null +++ b/services/auth/source/pam/source.go @@ -0,0 +1,45 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pam + +import ( + "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/modules/json" +) + +// __________ _____ _____ +// \______ \/ _ \ / \ +// | ___/ /_\ \ / \ / \ +// | | / | \/ Y \ +// |____| \____|__ /\____|__ / +// \/ \/ + +// Source holds configuration for the PAM login source. +type Source struct { + ServiceName string // pam service (e.g. system-auth) + EmailDomain string + SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source + + // reference to the authSource + authSource *auth.Source +} + +// FromDB fills up a PAMConfig from serialized format. +func (source *Source) FromDB(bs []byte) error { + return json.UnmarshalHandleDoubleEncode(bs, &source) +} + +// ToDB exports a PAMConfig to a serialized format. +func (source *Source) ToDB() ([]byte, error) { + return json.Marshal(source) +} + +// SetAuthSource sets the related AuthSource +func (source *Source) SetAuthSource(authSource *auth.Source) { + source.authSource = authSource +} + +func init() { + auth.RegisterTypeConfig(auth.PAM, &Source{}) +} diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go new file mode 100644 index 0000000..addd1bd --- /dev/null +++ b/services/auth/source/pam/source_authenticate.go @@ -0,0 +1,76 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pam + +import ( + "context" + "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" + + "github.com/google/uuid" +) + +// Authenticate queries if login/password is valid against the PAM, +// and create a local user if success when enabled. +func (source *Source) Authenticate(ctx context.Context, user *user_model.User, userName, password string) (*user_model.User, error) { + pamLogin, err := pam.Auth(source.ServiceName, userName, password) + if err != nil { + if strings.Contains(err.Error(), "Authentication failure") { + return nil, user_model.ErrUserNotExist{Name: userName} + } + return nil, err + } + + if user != nil { + return user, nil + } + + // Allow PAM sources with `@` in their name, like from Active Directory + username := pamLogin + email := pamLogin + idx := strings.Index(pamLogin, "@") + if idx > -1 { + username = pamLogin[:idx] + } + if user_model.ValidateEmail(email) != nil { + if source.EmailDomain != "" { + email = fmt.Sprintf("%s@%s", username, source.EmailDomain) + } else { + email = fmt.Sprintf("%s@%s", username, setting.Service.NoReplyAddress) + } + if user_model.ValidateEmail(email) != nil { + email = uuid.New().String() + "@localhost" + } + } + + user = &user_model.User{ + LowerName: strings.ToLower(username), + Name: username, + Email: email, + Passwd: password, + LoginType: auth.PAM, + LoginSource: source.authSource.ID, + LoginName: userName, // This is what the user typed in + } + overwriteDefault := &user_model.CreateUserOverwriteOptions{ + IsActive: optional.Some(true), + } + + if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { + return user, err + } + + return user, nil +} + +// IsSkipLocalTwoFA returns if this source should skip local 2fa for password authentication +func (source *Source) IsSkipLocalTwoFA() bool { + return source.SkipLocalTwoFA +} -- cgit v1.2.3