Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/admin_auth_ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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")
}
Expand Down
11 changes: 6 additions & 5 deletions models/asymkey/ssh_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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)
Expand Down Expand Up @@ -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
}

Expand Down
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion routers/api/v1/user/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions routers/web/admin/auths.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion routers/web/auth/oauth_signin_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion routers/web/user/setting/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion services/asymkey/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion services/asymkey/ssh_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions services/auth/source/ldap/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions services/auth/source/ldap/source_authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions services/auth/source/ldap/source_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand All @@ -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
}

Expand Down
1 change: 1 addition & 0 deletions services/forms/auth_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type AuthenticationForm struct {
AttributeMail string
AttributeSSHPublicKey string
AttributeAvatar string
SSHKeysAreVerified bool
AttributesInBind bool
UsePagedSearch bool
SearchPageSize int
Expand Down
6 changes: 6 additions & 0 deletions templates/admin/auth/edit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@
<input id="attribute_avatar" name="attribute_avatar" value="{{$cfg.AttributeAvatar}}" placeholder="jpegPhoto">
</div>

<div class="inline field">
<div class="ui checkbox">
<label for="ssh_keys_are_verified"><strong>{{ctx.Locale.Tr "admin.auths.ssh_keys_are_verified"}}</strong></label>
<input id="ssh_keys_are_verified" name="ssh_keys_are_verified" type="checkbox" {{if $cfg.SSHKeysAreVerified}}checked{{end}}>
</div>
</div>
<!-- ldap group begin -->
<div class="inline field">
<div class="ui checkbox">
Expand Down
6 changes: 6 additions & 0 deletions templates/admin/auth/source/ldap.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@
<input id="attribute_avatar" name="attribute_avatar" value="{{.attribute_avatar}}" placeholder="jpegPhoto">
</div>

<div class="inline field">
<div class="ui checkbox">
<label for="ssh_keys_are_verified"><strong>{{ctx.Locale.Tr "admin.auths.ssh_keys_are_verified"}}</strong></label>
<input id="ssh_keys_are_verified" name="ssh_keys_are_verified" type="checkbox" {{if .ssh_keys_are_verified}}checked{{end}}>
</div>
</div>
<!-- ldap group begin -->
<div class="inline field">
<div class="ui checkbox">
Expand Down