Skip to content

Commit cdb9478

Browse files
dnmgnslafriks
authored andcommitted
LDAP Public SSH Keys synchronization (#1844)
* Add LDAP Key Synchronization feature Signed-off-by: Magnus Lindvall <[email protected]> * Add migration: add login source id column for public_key table * Only update keys if needed * Add function to only list pubkey synchronized from ldap * Only list pub ssh keys synchronized from ldap. Do not sort strings as ExistsInSlice does it. * Only get keys belonging to current login source id * Set default login source id to 0 * Some minor cleanup. Add integration tests (updete dep testify)
1 parent b908ac9 commit cdb9478

File tree

25 files changed

+620
-436
lines changed

25 files changed

+620
-436
lines changed

Gopkg.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integrations/auth_ldap_test.go

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ var gitLDAPUsers = []ldapUser{
4040
Password: "hermes",
4141
FullName: "Conrad Hermes",
4242
43-
IsAdmin: true,
43+
SSHKeys: []string{
44+
"SHA256:qLY06smKfHoW/92yXySpnxFR10QFrLdRjf/GNPvwcW8",
45+
"SHA256:QlVTuM5OssDatqidn2ffY+Lc4YA5Fs78U+0KOHI51jQ",
46+
},
47+
IsAdmin: true,
4448
},
4549
{
4650
UserName: "fry",
@@ -89,26 +93,27 @@ func getLDAPServerHost() string {
8993
return host
9094
}
9195

92-
func addAuthSourceLDAP(t *testing.T) {
96+
func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) {
9397
session := loginUser(t, "user1")
9498
csrf := GetCSRF(t, session, "/admin/auths/new")
9599
req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{
96-
"_csrf": csrf,
97-
"type": "2",
98-
"name": "ldap",
99-
"host": getLDAPServerHost(),
100-
"port": "389",
101-
"bind_dn": "uid=gitea,ou=service,dc=planetexpress,dc=com",
102-
"bind_password": "password",
103-
"user_base": "ou=people,dc=planetexpress,dc=com",
104-
"filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
105-
"admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
106-
"attribute_username": "uid",
107-
"attribute_name": "givenName",
108-
"attribute_surname": "sn",
109-
"attribute_mail": "mail",
110-
"is_sync_enabled": "on",
111-
"is_active": "on",
100+
"_csrf": csrf,
101+
"type": "2",
102+
"name": "ldap",
103+
"host": getLDAPServerHost(),
104+
"port": "389",
105+
"bind_dn": "uid=gitea,ou=service,dc=planetexpress,dc=com",
106+
"bind_password": "password",
107+
"user_base": "ou=people,dc=planetexpress,dc=com",
108+
"filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
109+
"admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
110+
"attribute_username": "uid",
111+
"attribute_name": "givenName",
112+
"attribute_surname": "sn",
113+
"attribute_mail": "mail",
114+
"attribute_ssh_public_key": sshKeyAttribute,
115+
"is_sync_enabled": "on",
116+
"is_active": "on",
112117
})
113118
session.MakeRequest(t, req, http.StatusFound)
114119
}
@@ -119,7 +124,7 @@ func TestLDAPUserSignin(t *testing.T) {
119124
return
120125
}
121126
prepareTestEnv(t)
122-
addAuthSourceLDAP(t)
127+
addAuthSourceLDAP(t, "")
123128

124129
u := gitLDAPUsers[0]
125130

@@ -140,7 +145,7 @@ func TestLDAPUserSync(t *testing.T) {
140145
return
141146
}
142147
prepareTestEnv(t)
143-
addAuthSourceLDAP(t)
148+
addAuthSourceLDAP(t, "")
144149
models.SyncExternalUsers()
145150

146151
session := loginUser(t, "user1")
@@ -186,9 +191,41 @@ func TestLDAPUserSigninFailed(t *testing.T) {
186191
return
187192
}
188193
prepareTestEnv(t)
189-
addAuthSourceLDAP(t)
194+
addAuthSourceLDAP(t, "")
190195

191196
u := otherLDAPUsers[0]
192197

193198
testLoginFailed(t, u.UserName, u.Password, i18n.Tr("en", "form.username_password_incorrect"))
194199
}
200+
201+
func TestLDAPUserSSHKeySync(t *testing.T) {
202+
if skipLDAPTests() {
203+
t.Skip()
204+
return
205+
}
206+
prepareTestEnv(t)
207+
addAuthSourceLDAP(t, "sshPublicKey")
208+
models.SyncExternalUsers()
209+
210+
// Check if users has SSH keys synced
211+
for _, u := range gitLDAPUsers {
212+
if len(u.SSHKeys) == 0 {
213+
continue
214+
}
215+
session := loginUserWithPassword(t, u.UserName, u.Password)
216+
217+
req := NewRequest(t, "GET", "/user/settings/keys")
218+
resp := session.MakeRequest(t, req, http.StatusOK)
219+
220+
htmlDoc := NewHTMLParser(t, resp.Body)
221+
222+
divs := htmlDoc.doc.Find(".key.list .print.meta")
223+
224+
syncedKeys := make([]string, divs.Length())
225+
for i := 0; i < divs.Length(); i++ {
226+
syncedKeys[i] = strings.TrimSpace(divs.Eq(i).Text())
227+
}
228+
229+
assert.ElementsMatch(t, u.SSHKeys, syncedKeys)
230+
}
231+
}

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ var migrations = []Migration{
184184
NewMigration("add multiple assignees", addMultipleAssignees),
185185
// v65 -> v66
186186
NewMigration("add u2f", addU2FReg),
187+
// v66 -> v67
188+
NewMigration("add login source id column for public_key table", addLoginSourceIDToPublicKeyTable),
187189
}
188190

189191
// Migrate database to current version

models/migrations/v66.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"fmt"
9+
10+
"github.com/go-xorm/xorm"
11+
)
12+
13+
func addLoginSourceIDToPublicKeyTable(x *xorm.Engine) error {
14+
type PublicKey struct {
15+
LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
16+
}
17+
18+
if err := x.Sync2(new(PublicKey)); err != nil {
19+
return fmt.Errorf("Sync2: %v", err)
20+
}
21+
return nil
22+
}

models/ssh_key.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,14 @@ const (
4747

4848
// PublicKey represents a user or deploy SSH public key.
4949
type PublicKey struct {
50-
ID int64 `xorm:"pk autoincr"`
51-
OwnerID int64 `xorm:"INDEX NOT NULL"`
52-
Name string `xorm:"NOT NULL"`
53-
Fingerprint string `xorm:"NOT NULL"`
54-
Content string `xorm:"TEXT NOT NULL"`
55-
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
56-
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
50+
ID int64 `xorm:"pk autoincr"`
51+
OwnerID int64 `xorm:"INDEX NOT NULL"`
52+
Name string `xorm:"NOT NULL"`
53+
Fingerprint string `xorm:"NOT NULL"`
54+
Content string `xorm:"TEXT NOT NULL"`
55+
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
56+
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
57+
LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
5758

5859
CreatedUnix util.TimeStamp `xorm:"created"`
5960
UpdatedUnix util.TimeStamp `xorm:"updated"`
@@ -391,7 +392,7 @@ func addKey(e Engine, key *PublicKey) (err error) {
391392
}
392393

393394
// AddPublicKey adds new public key to database and authorized_keys file.
394-
func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
395+
func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*PublicKey, error) {
395396
log.Trace(content)
396397

397398
fingerprint, err := calcFingerprint(content)
@@ -420,12 +421,13 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
420421
}
421422

422423
key := &PublicKey{
423-
OwnerID: ownerID,
424-
Name: name,
425-
Fingerprint: fingerprint,
426-
Content: content,
427-
Mode: AccessModeWrite,
428-
Type: KeyTypeUser,
424+
OwnerID: ownerID,
425+
Name: name,
426+
Fingerprint: fingerprint,
427+
Content: content,
428+
Mode: AccessModeWrite,
429+
Type: KeyTypeUser,
430+
LoginSourceID: LoginSourceID,
429431
}
430432
if err = addKey(sess, key); err != nil {
431433
return nil, fmt.Errorf("addKey: %v", err)
@@ -471,6 +473,14 @@ func ListPublicKeys(uid int64) ([]*PublicKey, error) {
471473
Find(&keys)
472474
}
473475

476+
// ListPublicLdapSSHKeys returns a list of synchronized public ldap ssh keys belongs to given user and login source.
477+
func ListPublicLdapSSHKeys(uid int64, LoginSourceID int64) ([]*PublicKey, error) {
478+
keys := make([]*PublicKey, 0, 5)
479+
return keys, x.
480+
Where("owner_id = ? AND login_source_id = ?", uid, LoginSourceID).
481+
Find(&keys)
482+
}
483+
474484
// UpdatePublicKeyUpdated updates public key use time.
475485
func UpdatePublicKeyUpdated(id int64) error {
476486
// Check if key exists before update as affected rows count is unreliable

0 commit comments

Comments
 (0)