From dd136858f1ea40ad3c94191d647487fa4f31926c 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.0. Signed-off-by: Daniel Baumann --- modules/auth/common.go | 22 ++++ modules/auth/openid/discovery_cache.go | 57 +++++++++ modules/auth/openid/discovery_cache_test.go | 49 +++++++ modules/auth/openid/openid.go | 37 ++++++ modules/auth/pam/pam.go | 43 +++++++ modules/auth/pam/pam_stub.go | 22 ++++ modules/auth/pam/pam_test.go | 20 +++ modules/auth/password/hash/argon2.go | 80 ++++++++++++ modules/auth/password/hash/bcrypt.go | 54 ++++++++ modules/auth/password/hash/common.go | 28 ++++ modules/auth/password/hash/dummy.go | 33 +++++ modules/auth/password/hash/dummy_test.go | 26 ++++ modules/auth/password/hash/hash.go | 189 +++++++++++++++++++++++++++ modules/auth/password/hash/hash_test.go | 191 ++++++++++++++++++++++++++++ modules/auth/password/hash/pbkdf2.go | 67 ++++++++++ modules/auth/password/hash/scrypt.go | 67 ++++++++++ modules/auth/password/hash/setting.go | 76 +++++++++++ modules/auth/password/hash/setting_test.go | 38 ++++++ modules/auth/password/password.go | 136 ++++++++++++++++++++ modules/auth/password/password_test.go | 77 +++++++++++ modules/auth/password/pwn.go | 52 ++++++++ modules/auth/password/pwn/pwn.go | 118 +++++++++++++++++ modules/auth/password/pwn/pwn_test.go | 51 ++++++++ modules/auth/webauthn/webauthn.go | 77 +++++++++++ modules/auth/webauthn/webauthn_test.go | 25 ++++ 25 files changed, 1635 insertions(+) create mode 100644 modules/auth/common.go create mode 100644 modules/auth/openid/discovery_cache.go create mode 100644 modules/auth/openid/discovery_cache_test.go create mode 100644 modules/auth/openid/openid.go create mode 100644 modules/auth/pam/pam.go create mode 100644 modules/auth/pam/pam_stub.go create mode 100644 modules/auth/pam/pam_test.go create mode 100644 modules/auth/password/hash/argon2.go create mode 100644 modules/auth/password/hash/bcrypt.go create mode 100644 modules/auth/password/hash/common.go create mode 100644 modules/auth/password/hash/dummy.go create mode 100644 modules/auth/password/hash/dummy_test.go create mode 100644 modules/auth/password/hash/hash.go create mode 100644 modules/auth/password/hash/hash_test.go create mode 100644 modules/auth/password/hash/pbkdf2.go create mode 100644 modules/auth/password/hash/scrypt.go create mode 100644 modules/auth/password/hash/setting.go create mode 100644 modules/auth/password/hash/setting_test.go create mode 100644 modules/auth/password/password.go create mode 100644 modules/auth/password/password_test.go create mode 100644 modules/auth/password/pwn.go create mode 100644 modules/auth/password/pwn/pwn.go create mode 100644 modules/auth/password/pwn/pwn_test.go create mode 100644 modules/auth/webauthn/webauthn.go create mode 100644 modules/auth/webauthn/webauthn_test.go (limited to 'modules/auth') diff --git a/modules/auth/common.go b/modules/auth/common.go new file mode 100644 index 0000000..77361f6 --- /dev/null +++ b/modules/auth/common.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/log" +) + +func UnmarshalGroupTeamMapping(raw string) (map[string]map[string][]string, error) { + groupTeamMapping := make(map[string]map[string][]string) + if raw == "" { + return groupTeamMapping, nil + } + err := json.Unmarshal([]byte(raw), &groupTeamMapping) + if err != nil { + log.Error("Failed to unmarshal group team mapping: %v", err) + return nil, err + } + return groupTeamMapping, nil +} diff --git a/modules/auth/openid/discovery_cache.go b/modules/auth/openid/discovery_cache.go new file mode 100644 index 0000000..3a8d119 --- /dev/null +++ b/modules/auth/openid/discovery_cache.go @@ -0,0 +1,57 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openid + +import ( + "sync" + "time" + + "github.com/yohcop/openid-go" +) + +type timedDiscoveredInfo struct { + info openid.DiscoveredInfo + time time.Time +} + +type timedDiscoveryCache struct { + cache map[string]timedDiscoveredInfo + ttl time.Duration + mutex *sync.Mutex +} + +func newTimedDiscoveryCache(ttl time.Duration) *timedDiscoveryCache { + return &timedDiscoveryCache{cache: map[string]timedDiscoveredInfo{}, ttl: ttl, mutex: &sync.Mutex{}} +} + +func (s *timedDiscoveryCache) Put(id string, info openid.DiscoveredInfo) { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.cache[id] = timedDiscoveredInfo{info: info, time: time.Now()} +} + +// Delete timed-out cache entries +func (s *timedDiscoveryCache) cleanTimedOut() { + now := time.Now() + for k, e := range s.cache { + diff := now.Sub(e.time) + if diff > s.ttl { + delete(s.cache, k) + } + } +} + +func (s *timedDiscoveryCache) Get(id string) openid.DiscoveredInfo { + s.mutex.Lock() + defer s.mutex.Unlock() + + // Delete old cached while we are at it. + s.cleanTimedOut() + + if info, has := s.cache[id]; has { + return info.info + } + return nil +} diff --git a/modules/auth/openid/discovery_cache_test.go b/modules/auth/openid/discovery_cache_test.go new file mode 100644 index 0000000..5a7f450 --- /dev/null +++ b/modules/auth/openid/discovery_cache_test.go @@ -0,0 +1,49 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openid + +import ( + "testing" + "time" +) + +type testDiscoveredInfo struct{} + +func (s *testDiscoveredInfo) ClaimedID() string { + return "claimedID" +} + +func (s *testDiscoveredInfo) OpEndpoint() string { + return "opEndpoint" +} + +func (s *testDiscoveredInfo) OpLocalID() string { + return "opLocalID" +} + +func TestTimedDiscoveryCache(t *testing.T) { + dc := newTimedDiscoveryCache(1 * time.Second) + + // Put some initial values + dc.Put("foo", &testDiscoveredInfo{}) // openid.opEndpoint: "a", openid.opLocalID: "b", openid.claimedID: "c"}) + + // Make sure we can retrieve them + if di := dc.Get("foo"); di == nil { + t.Errorf("Expected a result, got nil") + } else if di.OpEndpoint() != "opEndpoint" || di.OpLocalID() != "opLocalID" || di.ClaimedID() != "claimedID" { + t.Errorf("Expected opEndpoint opLocalID claimedID, got %v %v %v", di.OpEndpoint(), di.OpLocalID(), di.ClaimedID()) + } + + // Attempt to get a non-existent value + if di := dc.Get("bar"); di != nil { + t.Errorf("Expected nil, got %v", di) + } + + // Sleep one second and try retrieve again + time.Sleep(1 * time.Second) + + if di := dc.Get("foo"); di != nil { + t.Errorf("Expected a nil, got a result") + } +} diff --git a/modules/auth/openid/openid.go b/modules/auth/openid/openid.go new file mode 100644 index 0000000..249ce02 --- /dev/null +++ b/modules/auth/openid/openid.go @@ -0,0 +1,37 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openid + +import ( + "time" + + "github.com/yohcop/openid-go" +) + +// For the demo, we use in-memory infinite storage nonce and discovery +// cache. In your app, do not use this as it will eat up memory and +// never +// free it. Use your own implementation, on a better database system. +// If you have multiple servers for example, you may need to share at +// least +// the nonceStore between them. +var ( + nonceStore = openid.NewSimpleNonceStore() + discoveryCache = newTimedDiscoveryCache(24 * time.Hour) +) + +// Verify handles response from OpenID provider +func Verify(fullURL string) (id string, err error) { + return openid.Verify(fullURL, discoveryCache, nonceStore) +} + +// Normalize normalizes an OpenID URI +func Normalize(url string) (id string, err error) { + return openid.Normalize(url) +} + +// RedirectURL redirects browser +func RedirectURL(id, callbackURL, realm string) (string, error) { + return openid.RedirectURL(id, callbackURL, realm) +} diff --git a/modules/auth/pam/pam.go b/modules/auth/pam/pam.go new file mode 100644 index 0000000..cca1482 --- /dev/null +++ b/modules/auth/pam/pam.go @@ -0,0 +1,43 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build pam + +package pam + +import ( + "errors" + + "github.com/msteinert/pam" +) + +// Supported is true when built with PAM +var Supported = true + +// Auth pam auth service +func Auth(serviceName, userName, passwd string) (string, error) { + t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) { + switch s { + case pam.PromptEchoOff: + return passwd, nil + case pam.PromptEchoOn, pam.ErrorMsg, pam.TextInfo: + return "", nil + } + return "", errors.New("Unrecognized PAM message style") + }) + if err != nil { + return "", err + } + + if err = t.Authenticate(0); err != nil { + return "", err + } + + if err = t.AcctMgmt(0); err != nil { + return "", err + } + + // PAM login names might suffer transformations in the PAM stack. + // We should take whatever the PAM stack returns for it. + return t.GetItem(pam.User) +} diff --git a/modules/auth/pam/pam_stub.go b/modules/auth/pam/pam_stub.go new file mode 100644 index 0000000..3631eee --- /dev/null +++ b/modules/auth/pam/pam_stub.go @@ -0,0 +1,22 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build !pam + +package pam + +import ( + "errors" +) + +// Supported is false when built without PAM +var Supported = false + +// Auth not supported lack of pam tag +func Auth(serviceName, userName, passwd string) (string, error) { + // bypass the lint on callers: SA4023: this comparison is always true (staticcheck) + if !Supported { + return "", errors.New("PAM not supported") + } + return "", nil +} diff --git a/modules/auth/pam/pam_test.go b/modules/auth/pam/pam_test.go new file mode 100644 index 0000000..e9b844e --- /dev/null +++ b/modules/auth/pam/pam_test.go @@ -0,0 +1,20 @@ +//go:build pam + +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pam + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPamAuth(t *testing.T) { + result, err := Auth("gitea", "user1", "false-pwd") + require.Error(t, err) + assert.EqualError(t, err, "Authentication failure") + assert.Len(t, result, 0) +} diff --git a/modules/auth/password/hash/argon2.go b/modules/auth/password/hash/argon2.go new file mode 100644 index 0000000..0cd6472 --- /dev/null +++ b/modules/auth/password/hash/argon2.go @@ -0,0 +1,80 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package hash + +import ( + "encoding/hex" + "strings" + + "code.gitea.io/gitea/modules/log" + + "golang.org/x/crypto/argon2" +) + +func init() { + MustRegister("argon2", NewArgon2Hasher) +} + +// Argon2Hasher implements PasswordHasher +// and uses the Argon2 key derivation function, hybrant variant +type Argon2Hasher struct { + time uint32 + memory uint32 + threads uint8 + keyLen uint32 +} + +// HashWithSaltBytes a provided password and salt +func (hasher *Argon2Hasher) HashWithSaltBytes(password string, salt []byte) string { + if hasher == nil { + return "" + } + return hex.EncodeToString(argon2.IDKey([]byte(password), salt, hasher.time, hasher.memory, hasher.threads, hasher.keyLen)) +} + +// NewArgon2Hasher is a factory method to create an Argon2Hasher +// The provided config should be either empty or of the form: +// "