diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 644bfa4e2..3b125b55b 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -6,7 +6,7 @@ info: description: These APIs provide services for manipulating Harbor project. version: "0.3.0" # the domain of the service -host: localhost +host: localshot # array of all schemes that your API supports schemes: - http @@ -1347,7 +1347,30 @@ paths: 404: description: Not found the default root certificate. 500: - description: Unexpected internal errors. + description: Unexpected internal errors. + /ldap/ping: + post: + summary: Ping available ldap service. + description: | + This endpoint ping the available ldap service for test related configuration parameters. + parameters: + - name: ldapconf + in: body + description: ldap configuration. + required: true + schema: + $ref: '#/definitions/LdapConf' + tags: + - Products + responses: + 200: + description: Ping ldap service successfully. + 401: + description: Only admin has this authority. + 403: + description: Inviald ldap configuration parameters. + 500: + description: Unexpected internal errors. definitions: Search: type: object @@ -1798,3 +1821,32 @@ definitions: description: The storage of system. items: $ref: '#/definitions/Storage' + LdapConf: + type: object + properties: + ldap_url: + type: string + description: The url of ldap service. + ldap_searchdn: + type: string + description: The search dn of ldap service. + ldap_search_pwd: + type: string + description: The search password of ldap service. + ldap_basedn: + type: string + description: The base dn of ldap service. + ldap_filter: + type: string + description: The serach filter of ldap service. + ldap_uid: + type: string + description: The serach uid of ldap service. + ldap_scope: + type: integer + format: int64 + description: The serach scope of ldap service. + ldap_connect_timeout: + type: integer + format: int64 + description: The connect timeout of ldap service(second). diff --git a/src/common/models/ldap.go b/src/common/models/ldap.go new file mode 100644 index 000000000..f642d1c84 --- /dev/null +++ b/src/common/models/ldap.go @@ -0,0 +1,28 @@ +/* + Copyright (c) 2016 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 models + +// LdapConf holds information about repository that accessed most +type LdapConf struct { + LdapURL string `json:"ldap_url"` + LdapSearchDn string `json:"ldap_searchdn"` + LdapSearchPwd string `json:"ldap_search_pwd"` + LdapBaseDn string `json:"ldap_basedn"` + LdapFilter string `json:"ldap_filter"` + LdapUID string `json:"ldap_uid"` + LdapScope int `json:"ldap_scope"` + LdapConnectTimeout int `json:"ldap_connect_timeout"` +} diff --git a/src/ui/api/ldap.go b/src/ui/api/ldap.go new file mode 100644 index 000000000..0976ac672 --- /dev/null +++ b/src/ui/api/ldap.go @@ -0,0 +1,159 @@ +/* + Copyright (c) 2016 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" + "net/http" + "strconv" + "strings" + "time" + + "crypto/tls" + + "github.com/vmware/harbor/src/common/api" + "github.com/vmware/harbor/src/common/dao" + "github.com/vmware/harbor/src/common/models" + "github.com/vmware/harbor/src/common/utils/log" + + goldap "gopkg.in/ldap.v2" +) + +// LdapAPI handles requesst to /api/ldap/ping /api/ldap/search +type LdapAPI struct { + api.BaseAPI +} + +var ldapConfs models.LdapConf + +// Prepare ... +func (l *LdapAPI) Prepare() { + + userID := l.ValidateUser() + isSysAdmin, err := dao.IsAdminRole(userID) + if err != nil { + log.Errorf("error occurred in IsAdminRole: %v", err) + l.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + if !isSysAdmin { + l.CustomAbort(http.StatusForbidden, http.StatusText(http.StatusForbidden)) + } +} + +// Ping ... +func (l *LdapAPI) Ping() { + l.DecodeJSONReqAndValidate(&ldapConfs) + + err := validateLdapReq(ldapConfs) + if err != nil { + log.Errorf("Invalid ldap request, error: %v", err) + l.RenderError(http.StatusBadRequest, fmt.Sprintf("invalid ldap request: %v", err)) + return + } + + err = connectTest(ldapConfs) + if err != nil { + log.Errorf("Ldap connect fail, error: %v", err) + l.RenderError(http.StatusBadRequest, fmt.Sprintf("ldap connect fail: %v", err)) + return + } +} + +func validateLdapReq(ldapConfs models.LdapConf) error { + ldapURL := ldapConfs.LdapURL + if ldapURL == "" { + return fmt.Errorf("can not get any available LDAP_URL") + } + log.Debug("ldapURL:", ldapURL) + + ldapConnectTimeout := ldapConfs.LdapConnectTimeout + log.Debug("ldapConnectTimeout:", ldapConnectTimeout) + + return nil + +} + +func connectTest(ldapConfs models.LdapConf) error { + + var ldap *goldap.Conn + var protocol, hostport string + var host, port string + var err error + + ldapURL := ldapConfs.LdapURL + + // This routine keeps compability with the old format used on harbor.cfg + + if strings.Contains(ldapURL, "://") { + splitLdapURL := strings.Split(ldapURL, "://") + protocol, hostport = splitLdapURL[0], splitLdapURL[1] + if !((protocol == "ldap") || (protocol == "ldaps")) { + return fmt.Errorf("unknown ldap protocl") + } + } else { + hostport = ldapURL + protocol = "ldap" + } + + // This tries to detect the used port, if not defined + if strings.Contains(hostport, ":") { + splitHostPort := strings.Split(hostport, ":") + host, port = splitHostPort[0], splitHostPort[1] + _, error := strconv.Atoi(splitHostPort[1]) + if error != nil { + return fmt.Errorf("illegal url format") + } + } else { + host = hostport + switch protocol { + case "ldap": + port = "389" + case "ldaps": + port = "636" + } + } + + // Sets a Dial Timeout for LDAP + connectTimeout := ldapConfs.LdapConnectTimeout + goldap.DefaultTimeout = time.Duration(connectTimeout) * time.Second + + switch protocol { + case "ldap": + ldap, err = goldap.Dial("tcp", fmt.Sprintf("%s:%s", host, port)) + case "ldaps": + ldap, err = goldap.DialTLS("tcp", fmt.Sprintf("%s:%s", host, port), &tls.Config{InsecureSkipVerify: true}) + } + + if err != nil { + return err + } + defer ldap.Close() + + ldapSearchDn := ldapConfs.LdapSearchDn + if ldapSearchDn != "" { + log.Debug("Search DN: ", ldapSearchDn) + ldapSearchPwd := ldapConfs.LdapSearchPwd + err = ldap.Bind(ldapSearchDn, ldapSearchPwd) + if err != nil { + log.Debug("Bind search dn error", err) + return err + } + } + + return nil + +} diff --git a/src/ui/router.go b/src/ui/router.go index 537748b03..55c3f8a73 100644 --- a/src/ui/router.go +++ b/src/ui/router.go @@ -87,6 +87,8 @@ func initRouters() { beego.Router("/api/systeminfo/volumes", &api.SystemInfoAPI{}, "get:GetVolumeInfo") beego.Router("/api/systeminfo/getcert", &api.SystemInfoAPI{}, "get:GetCert") + beego.Router("/api/ldap/ping", &api.LdapAPI{}, "post:Ping") + //external service that hosted on harbor process: beego.Router("/service/notifications", &service.NotificationHandler{}) beego.Router("/service/token", &token.Handler{})