diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index 069ad6600c7e8..c9be5abb379b0 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -94,6 +94,10 @@ func commonLdapCLIFlags() []cli.Flag { Name: "public-ssh-key-attribute", Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.", }, + &cli.BoolFlag{ + Name: "ssh-keys-are-verified", + Usage: "Set to true to automatically flag SSH keys in LDAP as verified.", + }, &cli.BoolFlag{ Name: "skip-local-2fa", Usage: "Set to true to skip local 2fa for users authenticated by this source", @@ -294,6 +298,9 @@ func parseLdapConfig(c *cli.Command, config *ldap.Source) error { if c.IsSet("public-ssh-key-attribute") { config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute") } + if c.IsSet("ssh-keys-are-verified") { + config.SSHKeysAreVerified = c.Bool("ssh-keys-are-verified") + } if c.IsSet("avatar-attribute") { config.AttributeAvatar = c.String("avatar-attribute") } diff --git a/models/asymkey/ssh_key.go b/models/asymkey/ssh_key.go index d77b5d46a76b1..98784b36bd3fe 100644 --- a/models/asymkey/ssh_key.go +++ b/models/asymkey/ssh_key.go @@ -84,7 +84,7 @@ func addKey(ctx context.Context, key *PublicKey) (err error) { } // AddPublicKey adds new public key to database and authorized_keys file. -func AddPublicKey(ctx context.Context, ownerID int64, name, content string, authSourceID int64) (*PublicKey, error) { +func AddPublicKey(ctx context.Context, ownerID int64, name, content string, authSourceID int64, verified bool) (*PublicKey, error) { log.Trace(content) fingerprint, err := CalcFingerprint(content) @@ -115,6 +115,7 @@ func AddPublicKey(ctx context.Context, ownerID int64, name, content string, auth Mode: perm.AccessModeWrite, Type: KeyTypeUser, LoginSourceID: authSourceID, + Verified: verified, } if err = addKey(ctx, key); err != nil { return nil, fmt.Errorf("addKey: %w", err) @@ -298,7 +299,7 @@ func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, erro } // AddPublicKeysBySource add a users public keys. Returns true if there are changes. -func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool { +func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string, verified bool) bool { var sshKeysNeedUpdate bool for _, sshKey := range sshPublicKeys { var err error @@ -317,7 +318,7 @@ func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.So marshalled = marshalled[:len(marshalled)-1] sshKeyName := fmt.Sprintf("%s-%s", s.Name, ssh.FingerprintSHA256(out)) - if _, err := AddPublicKey(ctx, usr.ID, sshKeyName, marshalled, s.ID); err != nil { + if _, err := AddPublicKey(ctx, usr.ID, sshKeyName, marshalled, s.ID, verified); err != nil { if IsErrKeyAlreadyExist(err) { log.Trace("AddPublicKeysBySource[%s]: Public SSH Key %s already exists for user", sshKeyName, usr.Name) } else { @@ -336,7 +337,7 @@ func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.So } // SynchronizePublicKeys updates a user's public keys. Returns true if there are changes. -func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool { +func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string, verified bool) bool { var sshKeysNeedUpdate bool log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name) @@ -381,7 +382,7 @@ func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.So newKeys = append(newKeys, key) } } - if AddPublicKeysBySource(ctx, usr, s, newKeys) { + if AddPublicKeysBySource(ctx, usr, s, newKeys, verified) { sshKeysNeedUpdate = true } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6712250924361..74b7f44b93b25 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3230,6 +3230,7 @@ auths.attribute_surname = Surname Attribute auths.attribute_mail = Email Attribute auths.attribute_ssh_public_key = Public SSH Key Attribute auths.attribute_avatar = Avatar Attribute +auths.ssh_keys_are_verified = SSH keys in LDAP are considered as verified auths.attributes_in_bind = Fetch Attributes in Bind DN Context auths.allow_deactivate_all = Allow an empty search result to deactivate all users auths.use_paged_search = Use Paged Search diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index aa69245e4999d..08aa182ca19ec 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -211,7 +211,7 @@ func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid return } - key, err := asymkey_model.AddPublicKey(ctx, uid, form.Title, content, 0) + key, err := asymkey_model.AddPublicKey(ctx, uid, form.Title, content, 0, false) if err != nil { repo.HandleAddKeyError(ctx, err) return diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index fb1a7d95240c2..3407789f2f951 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -136,6 +136,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source { AttributesInBind: form.AttributesInBind, AttributeSSHPublicKey: form.AttributeSSHPublicKey, AttributeAvatar: form.AttributeAvatar, + SSHKeysAreVerified: form.SSHKeysAreVerified, SearchPageSize: pageSize, Filter: form.Filter, GroupsEnabled: form.GroupsEnabled, diff --git a/routers/web/auth/oauth_signin_sync.go b/routers/web/auth/oauth_signin_sync.go index 86d196602452a..2f7a8eab58090 100644 --- a/routers/web/auth/oauth_signin_sync.go +++ b/routers/web/auth/oauth_signin_sync.go @@ -86,7 +86,7 @@ func oauth2UpdateSSHPubIfNeed(ctx *context.Context, authSource *auth.Source, got if err != nil { return err } - if !asymkey_model.SynchronizePublicKeys(ctx, user, authSource, sshKeys) { + if !asymkey_model.SynchronizePublicKeys(ctx, user, authSource, sshKeys, false) { return nil } return asymkey_service.RewriteAllPublicKeys(ctx) diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 6b5a7a2e2a0bd..ef7ef47c51957 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -187,7 +187,7 @@ func KeysPost(ctx *context.Context) { return } - if _, err = asymkey_model.AddPublicKey(ctx, ctx.Doer.ID, form.Title, content, 0); err != nil { + if _, err = asymkey_model.AddPublicKey(ctx, ctx.Doer.ID, form.Title, content, 0, false); err != nil { ctx.Data["HasSSHError"] = true switch { case asymkey_model.IsErrKeyAlreadyExist(err): diff --git a/services/asymkey/commit_test.go b/services/asymkey/commit_test.go index 6edba1e90aff3..2dd08b5dd4cff 100644 --- a/services/asymkey/commit_test.go +++ b/services/asymkey/commit_test.go @@ -31,7 +31,7 @@ func TestParseCommitWithSSHSignature(t *testing.T) { // AAAEDWqPHTH51xb4hy1y1f1VeWL/2A9Q0b6atOyv5fx8x5prpPrMXSg9qTx04jPNPWRcHs // utyxWjThIpzcaO68yWVnAAAAEXVzZXIyQGV4YW1wbGUuY29tAQIDBA== // -----END OPENSSH PRIVATE KEY----- - sshPubKey, err := asymkey_model.AddPublicKey(t.Context(), 999, "user-ssh-key-any-name", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILpPrMXSg9qTx04jPNPWRcHsutyxWjThIpzcaO68yWVn", 0) + sshPubKey, err := asymkey_model.AddPublicKey(t.Context(), 999, "user-ssh-key-any-name", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILpPrMXSg9qTx04jPNPWRcHsutyxWjThIpzcaO68yWVn", 0, false) require.NoError(t, err) _, err = db.GetEngine(t.Context()).ID(sshPubKey.ID).Cols("verified").Update(&asymkey_model.PublicKey{Verified: true}) require.NoError(t, err) diff --git a/services/asymkey/ssh_key_test.go b/services/asymkey/ssh_key_test.go index 3605bd1e64c82..b052050dc6b18 100644 --- a/services/asymkey/ssh_key_test.go +++ b/services/asymkey/ssh_key_test.go @@ -66,7 +66,7 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib for i, kase := range testCases { s.ID = int64(i) + 20 - asymkey_model.AddPublicKeysBySource(t.Context(), user, s, []string{kase.keyString}) + asymkey_model.AddPublicKeysBySource(t.Context(), user, s, []string{kase.keyString}, false) keys, err := db.Find[asymkey_model.PublicKey](t.Context(), asymkey_model.FindPublicKeyOptions{ OwnerID: user.ID, LoginSourceID: s.ID, diff --git a/services/auth/source/ldap/source.go b/services/auth/source/ldap/source.go index 2362cad8aae7a..81d4b5446bd4a 100644 --- a/services/auth/source/ldap/source.go +++ b/services/auth/source/ldap/source.go @@ -44,6 +44,7 @@ type Source struct { AttributesInBind bool // fetch attributes in bind context (not user) AttributeSSHPublicKey string // LDAP SSH Public Key attribute AttributeAvatar string + SSHKeysAreVerified bool // true if SSH keys in LDAP are verified SearchPageSize uint32 // Search with paging page size Filter string // Query filter to validate entry AdminFilter string // Query filter to check if user is admin diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go index 4463bcc05446c..582841aebec5f 100644 --- a/services/auth/source/ldap/source_authenticate.go +++ b/services/auth/source/ldap/source_authenticate.go @@ -73,7 +73,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u } if user != nil { - if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, user, source.AuthSource, sr.SSHPublicKey) { + if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, user, source.AuthSource, sr.SSHPublicKey, source.SSHKeysAreVerified) { if err := asymkey_service.RewriteAllPublicKeys(ctx); err != nil { return user, err } @@ -99,7 +99,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u return user, err } - if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(ctx, user, source.AuthSource, sr.SSHPublicKey) { + if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(ctx, user, source.AuthSource, sr.SSHPublicKey, source.SSHKeysAreVerified) { if err := asymkey_service.RewriteAllPublicKeys(ctx); err != nil { return user, err } diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index 7b401c5c96b92..0c5fdac674a24 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -135,7 +135,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { if err == nil && isAttributeSSHPublicKeySet { log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.AuthSource.Name, usr.Name) - if asymkey_model.AddPublicKeysBySource(ctx, usr, source.AuthSource, su.SSHPublicKey) { + if asymkey_model.AddPublicKeysBySource(ctx, usr, source.AuthSource, su.SSHPublicKey, source.SSHKeysAreVerified) { sshKeysNeedUpdate = true } } @@ -145,7 +145,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { } } else if updateExisting { // Synchronize SSH Public Key if that attribute is set - if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, usr, source.AuthSource, su.SSHPublicKey) { + if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, usr, source.AuthSource, su.SSHPublicKey, source.SSHKeysAreVerified) { sshKeysNeedUpdate = true } diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 886110236c29b..95965b5f29a94 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -34,6 +34,7 @@ type AuthenticationForm struct { AttributeMail string AttributeSSHPublicKey string AttributeAvatar string + SSHKeysAreVerified bool AttributesInBind bool UsePagedSearch bool SearchPageSize int diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 7b96b4e94fd2b..c5cd2e3e290ed 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -113,6 +113,12 @@ +