summaryrefslogtreecommitdiffstats
path: root/services/auth/source/pam
diff options
context:
space:
mode:
Diffstat (limited to 'services/auth/source/pam')
-rw-r--r--services/auth/source/pam/assert_interface_test.go22
-rw-r--r--services/auth/source/pam/source.go47
-rw-r--r--services/auth/source/pam/source_authenticate.go62
3 files changed, 131 insertions, 0 deletions
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 0000000000..a0bebdf9c6
--- /dev/null
+++ b/services/auth/source/pam/assert_interface_test.go
@@ -0,0 +1,22 @@
+// Copyright 2021 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 pam_test
+
+import (
+ "code.gitea.io/gitea/models"
+ "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
+ models.LoginConfig
+ models.LoginSourceSettable
+}
+
+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 0000000000..b717ee6fe8
--- /dev/null
+++ b/services/auth/source/pam/source.go
@@ -0,0 +1,47 @@
+// Copyright 2021 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 pam
+
+import (
+ "code.gitea.io/gitea/models"
+
+ jsoniter "github.com/json-iterator/go"
+)
+
+// __________ _____ _____
+// \______ \/ _ \ / \
+// | ___/ /_\ \ / \ / \
+// | | / | \/ Y \
+// |____| \____|__ /\____|__ /
+// \/ \/
+
+// Source holds configuration for the PAM login source.
+type Source struct {
+ ServiceName string // pam service (e.g. system-auth)
+ EmailDomain string
+
+ // reference to the loginSource
+ loginSource *models.LoginSource
+}
+
+// FromDB fills up a PAMConfig from serialized format.
+func (source *Source) FromDB(bs []byte) error {
+ return models.JSONUnmarshalHandleDoubleEncode(bs, &source)
+}
+
+// ToDB exports a PAMConfig to a serialized format.
+func (source *Source) ToDB() ([]byte, error) {
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ return json.Marshal(source)
+}
+
+// SetLoginSource sets the related LoginSource
+func (source *Source) SetLoginSource(loginSource *models.LoginSource) {
+ source.loginSource = loginSource
+}
+
+func init() {
+ models.RegisterLoginTypeConfig(models.LoginPAM, &Source{})
+}
diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go
new file mode 100644
index 0000000000..6ca0642904
--- /dev/null
+++ b/services/auth/source/pam/source_authenticate.go
@@ -0,0 +1,62 @@
+// Copyright 2021 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 pam
+
+import (
+ "fmt"
+ "strings"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/auth/pam"
+ "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(user *models.User, login, password string) (*models.User, error) {
+ pamLogin, err := pam.Auth(source.ServiceName, login, password)
+ if err != nil {
+ if strings.Contains(err.Error(), "Authentication failure") {
+ return nil, models.ErrUserNotExist{Name: login}
+ }
+ 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 models.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 models.ValidateEmail(email) != nil {
+ email = uuid.New().String() + "@localhost"
+ }
+ }
+
+ user = &models.User{
+ LowerName: strings.ToLower(username),
+ Name: username,
+ Email: email,
+ Passwd: password,
+ LoginType: models.LoginPAM,
+ LoginSource: source.loginSource.ID,
+ LoginName: login, // This is what the user typed in
+ IsActive: true,
+ }
+ return user, models.CreateUser(user)
+}