mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-24 19:25:19 +01:00
Merge pull request #5200 from stonezdj/add_group_search_api
Add REST API to search LDAP by Group DN
This commit is contained in:
commit
ad7a85da51
@ -2325,14 +2325,18 @@ paths:
|
||||
summary: Search available ldap groups.
|
||||
description: >
|
||||
This endpoint searches the available ldap groups based on related
|
||||
configuration parameters. Support searched by input ladp configuration,
|
||||
load configuration from the system and specific filter.
|
||||
configuration parameters. support to search by groupname or groupdn.
|
||||
parameters:
|
||||
- name: groupname
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: Ldap group name
|
||||
- name: groupdn
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The LDAP group DN
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
@ -2342,6 +2346,10 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/UserGroup'
|
||||
"400":
|
||||
description: The Ldap group DN is invalid.
|
||||
'404':
|
||||
description: No ldap group found.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/ldap/users/search:
|
||||
@ -2372,6 +2380,7 @@ paths:
|
||||
description: Only admin has this authority.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
|
||||
/ldap/users/import:
|
||||
post:
|
||||
summary: Import selected available ldap users.
|
||||
@ -3653,6 +3662,7 @@ definitions:
|
||||
ldap_group_dn:
|
||||
type: string
|
||||
description: The DN of the LDAP group if group type is 1 (LDAP group).
|
||||
|
||||
Resource:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils"
|
||||
ldapUtils "github.com/vmware/harbor/src/common/utils/ldap"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
"github.com/vmware/harbor/src/ui/filter"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
@ -77,6 +78,25 @@ type usrInfo struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
ldapConfig := models.LdapConf{
|
||||
LdapURL: "ldap://127.0.0.1:389",
|
||||
LdapSearchDn: "cn=admin,dc=example,dc=com",
|
||||
LdapSearchPassword: "admin",
|
||||
LdapBaseDn: "dc=example,dc=com",
|
||||
LdapUID: "cn",
|
||||
LdapScope: 2,
|
||||
LdapConnectionTimeout: 5,
|
||||
}
|
||||
ldapGroupConfig := models.LdapGroupConf{
|
||||
LdapGroupBaseDN: "ou=groups,dc=example,dc=com",
|
||||
LdapGroupFilter: "objectclass=groupOfNames",
|
||||
LdapGroupSearchScope: 2,
|
||||
LdapGroupNameAttribute: "cn",
|
||||
}
|
||||
ldapTestConfig, err := ldapUtils.CreateWithAllConfig(ldapConfig, ldapGroupConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize configurations: %v", err)
|
||||
}
|
||||
if err := config.Init(); err != nil {
|
||||
log.Fatalf("failed to initialize configurations: %v", err)
|
||||
}
|
||||
@ -134,7 +154,10 @@ func init() {
|
||||
beego.Router("/api/systeminfo", &SystemInfoAPI{}, "get:GetGeneralInfo")
|
||||
beego.Router("/api/systeminfo/volumes", &SystemInfoAPI{}, "get:GetVolumeInfo")
|
||||
beego.Router("/api/systeminfo/getcert", &SystemInfoAPI{}, "get:GetCert")
|
||||
beego.Router("/api/ldap/ping", &LdapAPI{}, "post:Ping")
|
||||
beego.Router("/api/ldap/ping", &LdapAPI{ldapConfig: ldapTestConfig, useTestConfig: true}, "post:Ping")
|
||||
beego.Router("/api/ldap/users/search", &LdapAPI{ldapConfig: ldapTestConfig, useTestConfig: true}, "get:Search")
|
||||
beego.Router("/api/ldap/groups/search", &LdapAPI{ldapConfig: ldapTestConfig, useTestConfig: true}, "get:SearchGroup")
|
||||
beego.Router("/api/ldap/users/import", &LdapAPI{ldapConfig: ldapTestConfig, useTestConfig: true}, "post:ImportUser")
|
||||
beego.Router("/api/configurations", &ConfigAPI{})
|
||||
beego.Router("/api/configurations/reset", &ConfigAPI{}, "post:Reset")
|
||||
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
|
||||
|
@ -21,11 +21,15 @@ import (
|
||||
ldapUtils "github.com/vmware/harbor/src/common/utils/ldap"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/ui/auth"
|
||||
|
||||
goldap "gopkg.in/ldap.v2"
|
||||
)
|
||||
|
||||
// LdapAPI handles requesst to /api/ldap/ping /api/ldap/user/search /api/ldap/user/import
|
||||
type LdapAPI struct {
|
||||
BaseController
|
||||
ldapConfig *ldapUtils.Session
|
||||
useTestConfig bool // Only used for unit test
|
||||
}
|
||||
|
||||
const (
|
||||
@ -47,6 +51,14 @@ func (l *LdapAPI) Prepare() {
|
||||
l.HandleForbidden(l.SecurityCtx.GetUsername())
|
||||
return
|
||||
}
|
||||
if l.useTestConfig {
|
||||
ldapCfg, err := ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't load system configuration, error: %v", err))
|
||||
return
|
||||
}
|
||||
l.ldapConfig = ldapCfg
|
||||
}
|
||||
}
|
||||
|
||||
// Ping ...
|
||||
@ -55,16 +67,11 @@ func (l *LdapAPI) Ping() {
|
||||
LdapConnectionTimeout: 5,
|
||||
}
|
||||
var err error
|
||||
var ldapSession *ldapUtils.Session
|
||||
|
||||
l.Ctx.Input.CopyBody(1 << 32)
|
||||
|
||||
if string(l.Ctx.Input.RequestBody) == "" {
|
||||
ldapSession, err = ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't load system configuration, error: %v", err))
|
||||
return
|
||||
}
|
||||
ldapSession := *l.ldapConfig
|
||||
err = ldapSession.ConnectionTest()
|
||||
} else {
|
||||
l.DecodeJSONReqAndValidate(&ldapConfs)
|
||||
@ -81,7 +88,7 @@ func (l *LdapAPI) Ping() {
|
||||
func (l *LdapAPI) Search() {
|
||||
var err error
|
||||
var ldapUsers []models.LdapUser
|
||||
ldapSession, err := ldapUtils.LoadSystemLdapConfig()
|
||||
ldapSession := *l.ldapConfig
|
||||
if err = ldapSession.Open(); err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't Open LDAP session, error: %v", err))
|
||||
return
|
||||
@ -106,11 +113,10 @@ func (l *LdapAPI) Search() {
|
||||
func (l *LdapAPI) ImportUser() {
|
||||
var ldapImportUsers models.LdapImportUser
|
||||
var ldapFailedImportUsers []models.LdapFailedImportUser
|
||||
var ldapConfs models.LdapConf
|
||||
|
||||
l.DecodeJSONReqAndValidate(&ldapImportUsers)
|
||||
|
||||
ldapFailedImportUsers, err := importUsers(ldapConfs, ldapImportUsers.LdapUIDList)
|
||||
ldapFailedImportUsers, err := importUsers(ldapImportUsers.LdapUIDList, l.ldapConfig)
|
||||
|
||||
if err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("LDAP import user fail, error: %v", err))
|
||||
@ -127,17 +133,12 @@ func (l *LdapAPI) ImportUser() {
|
||||
|
||||
}
|
||||
|
||||
func importUsers(ldapConfs models.LdapConf, ldapImportUsers []string) ([]models.LdapFailedImportUser, error) {
|
||||
func importUsers(ldapImportUsers []string, ldapConfig *ldapUtils.Session) ([]models.LdapFailedImportUser, error) {
|
||||
var failedImportUser []models.LdapFailedImportUser
|
||||
var u models.LdapFailedImportUser
|
||||
|
||||
ldapSession, err := ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
log.Errorf("Can't load system configuration, error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = ldapSession.Open(); err != nil {
|
||||
ldapSession := *ldapConfig
|
||||
if err := ldapSession.Open(); err != nil {
|
||||
log.Errorf("Can't connect to LDAP, error: %v", err)
|
||||
}
|
||||
defer ldapSession.Close()
|
||||
@ -194,19 +195,37 @@ func importUsers(ldapConfs models.LdapConf, ldapImportUsers []string) ([]models.
|
||||
|
||||
// SearchGroup ... Search LDAP by groupname
|
||||
func (l *LdapAPI) SearchGroup() {
|
||||
var ldapGroups []models.LdapGroup
|
||||
var err error
|
||||
searchName := l.GetString("groupname")
|
||||
ldapSession, err := ldapUtils.LoadSystemLdapConfig()
|
||||
if err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't get LDAP system config, error: %v", err))
|
||||
return
|
||||
}
|
||||
groupDN := l.GetString("groupdn")
|
||||
ldapSession := *l.ldapConfig
|
||||
ldapSession.Open()
|
||||
defer ldapSession.Close()
|
||||
ldapGroups, err := ldapSession.SearchGroupByName(searchName)
|
||||
|
||||
//Search LDAP group by groupName or group DN
|
||||
if len(searchName) > 0 {
|
||||
ldapGroups, err = ldapSession.SearchGroupByName(searchName)
|
||||
if err != nil {
|
||||
l.HandleInternalServerError(fmt.Sprintf("Can't search LDAP group by name, error: %v", err))
|
||||
return
|
||||
}
|
||||
} else if len(groupDN) > 0 {
|
||||
if _, err := goldap.ParseDN(groupDN); err != nil {
|
||||
l.HandleBadRequest(fmt.Sprintf("Invalid DN: %v", err))
|
||||
return
|
||||
}
|
||||
ldapGroups, err = ldapSession.SearchGroupByDN(groupDN)
|
||||
if err != nil {
|
||||
//OpenLDAP usually return an error if DN is not found
|
||||
l.HandleNotFound(fmt.Sprintf("Search LDAP group fail, error: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(ldapGroups) == 0 {
|
||||
l.HandleNotFound("No ldap group found")
|
||||
return
|
||||
}
|
||||
l.Data["json"] = ldapGroups
|
||||
l.ServeJSON()
|
||||
}
|
||||
|
136
src/ui/api/ldap_test.go
Normal file
136
src/ui/api/ldap_test.go
Normal file
@ -0,0 +1,136 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
func TestLDAPPing(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/ldap/ping",
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/ldap/ping",
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/ldap/ping",
|
||||
bodyJSON: &models.LdapConf{
|
||||
LdapURL: "ldap://127.0.0.1:389",
|
||||
LdapSearchDn: "cn=admin,dc=example,dc=com",
|
||||
LdapSearchPassword: "admin",
|
||||
LdapBaseDn: "dc=example,dc=com",
|
||||
LdapUID: "cn",
|
||||
LdapScope: 2,
|
||||
LdapConnectionTimeout: 5,
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestLDAPUserSearch(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/ldap/users/search?username=mike",
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/ldap/users/search?username=mike",
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestLDAPGroupSearch(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/ldap/groups/search?groupname=harbor_users",
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/ldap/groups/search?groupname=harbor_users",
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestLDAPGroupSearchWithDN(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/ldap/groups/search?groupdn=cn=harbor_users,ou=groups,dc=example,dc=com",
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/ldap/groups/search?groupname=cn=harbor_users,ou=groups,dc=example,dc=com",
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestLDAPImportUser(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/ldap/users/import",
|
||||
bodyJSON: &models.LdapImportUser{
|
||||
LdapUIDList: []string{"mike", "mike02"},
|
||||
},
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
&codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/ldap/users/import",
|
||||
bodyJSON: &models.LdapImportUser{
|
||||
LdapUIDList: []string{"mike", "mike02"},
|
||||
},
|
||||
credential: admin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
Loading…
Reference in New Issue
Block a user