harbor/src/ui/api/ldap.go

232 lines
5.8 KiB
Go

// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api
import (
"fmt"
"github.com/vmware/harbor/src/common/models"
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 (
pingErrorMessage = "LDAP connection test failed"
loadSystemErrorMessage = "Can't load system configuration!"
canNotOpenLdapSession = "Can't open LDAP session!"
searchLdapFailMessage = "LDAP search failed!"
importUserError = "Found internal error when importing LDAP user!"
)
// Prepare ...
func (l *LdapAPI) Prepare() {
l.BaseController.Prepare()
if !l.SecurityCtx.IsAuthenticated() {
l.HandleUnauthorized()
return
}
if !l.SecurityCtx.IsSysAdmin() {
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 ...
func (l *LdapAPI) Ping() {
var ldapConfs = models.LdapConf{
LdapConnectionTimeout: 5,
}
var err error
l.Ctx.Input.CopyBody(1 << 32)
if string(l.Ctx.Input.RequestBody) == "" {
ldapSession := *l.ldapConfig
err = ldapSession.ConnectionTest()
} else {
l.DecodeJSONReqAndValidate(&ldapConfs)
err = ldapUtils.ConnectionTestWithConfig(ldapConfs)
}
if err != nil {
l.HandleInternalServerError(fmt.Sprintf("LDAP connect fail, error: %v", err))
return
}
}
// Search ...
func (l *LdapAPI) Search() {
var err error
var ldapUsers []models.LdapUser
ldapSession := *l.ldapConfig
if err = ldapSession.Open(); err != nil {
l.HandleInternalServerError(fmt.Sprintf("Can't Open LDAP session, error: %v", err))
return
}
defer ldapSession.Close()
searchName := l.GetString("username")
ldapUsers, err = ldapSession.SearchUser(searchName)
if err != nil {
l.HandleInternalServerError(fmt.Sprintf("LDAP search fail, error: %v", err))
return
}
l.Data["json"] = ldapUsers
l.ServeJSON()
}
// ImportUser ...
func (l *LdapAPI) ImportUser() {
var ldapImportUsers models.LdapImportUser
var ldapFailedImportUsers []models.LdapFailedImportUser
l.DecodeJSONReqAndValidate(&ldapImportUsers)
ldapFailedImportUsers, err := importUsers(ldapImportUsers.LdapUIDList, l.ldapConfig)
if err != nil {
l.HandleInternalServerError(fmt.Sprintf("LDAP import user fail, error: %v", err))
return
}
if len(ldapFailedImportUsers) > 0 {
//Some user require json format response.
l.HandleNotFound("")
l.Data["json"] = ldapFailedImportUsers
l.ServeJSON()
return
}
}
func importUsers(ldapImportUsers []string, ldapConfig *ldapUtils.Session) ([]models.LdapFailedImportUser, error) {
var failedImportUser []models.LdapFailedImportUser
var u models.LdapFailedImportUser
ldapSession := *ldapConfig
if err := ldapSession.Open(); err != nil {
log.Errorf("Can't connect to LDAP, error: %v", err)
}
defer ldapSession.Close()
for _, tempUID := range ldapImportUsers {
u.UID = tempUID
u.Error = ""
if u.UID == "" {
u.Error = "empty_uid"
failedImportUser = append(failedImportUser, u)
continue
}
if u.Error != "" {
failedImportUser = append(failedImportUser, u)
continue
}
ldapUsers, err := ldapSession.SearchUser(u.UID)
if err != nil {
u.UID = tempUID
u.Error = "failed_search_user"
failedImportUser = append(failedImportUser, u)
log.Errorf("Invalid LDAP search request for %s, error: %v", tempUID, err)
continue
}
if ldapUsers == nil || len(ldapUsers) <= 0 {
u.UID = tempUID
u.Error = "unknown_user"
failedImportUser = append(failedImportUser, u)
continue
}
var user models.User
user.Username = ldapUsers[0].Username
user.Realname = ldapUsers[0].Realname
user.Email = ldapUsers[0].Email
err = auth.OnBoardUser(&user)
if err != nil || user.UserID <= 0 {
u.UID = tempUID
u.Error = err.Error()
failedImportUser = append(failedImportUser, u)
log.Errorf("Can't import user %s, error: %s", tempUID, u.Error)
}
}
return failedImportUser, nil
}
// SearchGroup ... Search LDAP by groupname
func (l *LdapAPI) SearchGroup() {
var ldapGroups []models.LdapGroup
var err error
searchName := l.GetString("groupname")
groupDN := l.GetString("groupdn")
ldapSession := *l.ldapConfig
ldapSession.Open()
defer ldapSession.Close()
//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()
}