From 226ab5a5f7cc2e315545678f75f094d9263cab00 Mon Sep 17 00:00:00 2001 From: kun wang Date: Thu, 29 Dec 2016 13:23:59 +0800 Subject: [PATCH 1/6] Add debounce to input fields. (#1273) LGTM --- src/ui/views/sign-up.htm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/views/sign-up.htm b/src/ui/views/sign-up.htm index 841c4d8d7..eb513eaec 100644 --- a/src/ui/views/sign-up.htm +++ b/src/ui/views/sign-up.htm @@ -30,7 +30,7 @@
- +
// 'username_is_required' | tr // // 'username_is_too_long' | tr // @@ -45,7 +45,7 @@
- +
// 'email_is_required' | tr // // 'email_content_illegal' | tr // From 160e22f0fed8fdf72f01ede94cdc0eec0c61916f Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Fri, 30 Dec 2016 06:03:30 -0200 Subject: [PATCH 2/6] Changes LDAP Library and other LDAP improvements (#1277) * Changes LDAP library to go-ldap and creates new ldap timeout directive * Add support for connection on LDAP using TLS --- make/common/templates/ui/env | 1 + make/dev/ui/Dockerfile | 2 +- make/harbor.cfg | 3 + make/prepare | 2 + make/ubuntu/ui/Dockerfile | 2 +- src/ui/auth/ldap/ldap.go | 108 +++- src/ui/config/config.go | 32 +- src/ui/config/config_test.go | 3 + .../github.com/mqu/openldap/LICENCE.txt | 24 - src/vendor/github.com/mqu/openldap/README.md | 82 --- .../mqu/openldap/add-modify-delete.go | 230 -------- src/vendor/github.com/mqu/openldap/defines.go | 350 ------------ .../github.com/mqu/openldap/openldap.go | 454 ---------------- .../github.com/mqu/openldap/options-errors.go | 118 ---- src/vendor/github.com/mqu/openldap/results.go | 306 ----------- src/vendor/github.com/mqu/openldap/types.go | 76 --- src/vendor/gopkg.in/asn1-ber.v1/LICENSE | 27 + src/vendor/gopkg.in/asn1-ber.v1/README.md | 24 + src/vendor/gopkg.in/asn1-ber.v1/ber.go | 504 ++++++++++++++++++ .../gopkg.in/asn1-ber.v1/content_int.go | 25 + src/vendor/gopkg.in/asn1-ber.v1/header.go | 29 + src/vendor/gopkg.in/asn1-ber.v1/identifier.go | 103 ++++ src/vendor/gopkg.in/asn1-ber.v1/length.go | 71 +++ src/vendor/gopkg.in/asn1-ber.v1/util.go | 24 + src/vendor/gopkg.in/ldap.v2/LICENSE | 22 + src/vendor/gopkg.in/ldap.v2/Makefile | 42 ++ src/vendor/gopkg.in/ldap.v2/README.md | 53 ++ src/vendor/gopkg.in/ldap.v2/add.go | 113 ++++ src/vendor/gopkg.in/ldap.v2/bind.go | 143 +++++ src/vendor/gopkg.in/ldap.v2/client.go | 27 + src/vendor/gopkg.in/ldap.v2/compare.go | 85 +++ src/vendor/gopkg.in/ldap.v2/conn.go | 467 ++++++++++++++++ src/vendor/gopkg.in/ldap.v2/control.go | 420 +++++++++++++++ src/vendor/gopkg.in/ldap.v2/debug.go | 24 + src/vendor/gopkg.in/ldap.v2/del.go | 84 +++ src/vendor/gopkg.in/ldap.v2/dn.go | 244 +++++++++ src/vendor/gopkg.in/ldap.v2/doc.go | 4 + src/vendor/gopkg.in/ldap.v2/error.go | 148 +++++ src/vendor/gopkg.in/ldap.v2/filter.go | 466 ++++++++++++++++ src/vendor/gopkg.in/ldap.v2/ldap.go | 320 +++++++++++ src/vendor/gopkg.in/ldap.v2/modify.go | 170 ++++++ src/vendor/gopkg.in/ldap.v2/passwdmodify.go | 148 +++++ src/vendor/gopkg.in/ldap.v2/search.go | 450 ++++++++++++++++ src/vendor/vendor.json | 18 +- 44 files changed, 4365 insertions(+), 1683 deletions(-) delete mode 100644 src/vendor/github.com/mqu/openldap/LICENCE.txt delete mode 100644 src/vendor/github.com/mqu/openldap/README.md delete mode 100644 src/vendor/github.com/mqu/openldap/add-modify-delete.go delete mode 100644 src/vendor/github.com/mqu/openldap/defines.go delete mode 100644 src/vendor/github.com/mqu/openldap/openldap.go delete mode 100644 src/vendor/github.com/mqu/openldap/options-errors.go delete mode 100644 src/vendor/github.com/mqu/openldap/results.go delete mode 100644 src/vendor/github.com/mqu/openldap/types.go create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/LICENSE create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/README.md create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/ber.go create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/content_int.go create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/header.go create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/identifier.go create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/length.go create mode 100644 src/vendor/gopkg.in/asn1-ber.v1/util.go create mode 100644 src/vendor/gopkg.in/ldap.v2/LICENSE create mode 100644 src/vendor/gopkg.in/ldap.v2/Makefile create mode 100644 src/vendor/gopkg.in/ldap.v2/README.md create mode 100644 src/vendor/gopkg.in/ldap.v2/add.go create mode 100644 src/vendor/gopkg.in/ldap.v2/bind.go create mode 100644 src/vendor/gopkg.in/ldap.v2/client.go create mode 100644 src/vendor/gopkg.in/ldap.v2/compare.go create mode 100644 src/vendor/gopkg.in/ldap.v2/conn.go create mode 100644 src/vendor/gopkg.in/ldap.v2/control.go create mode 100644 src/vendor/gopkg.in/ldap.v2/debug.go create mode 100644 src/vendor/gopkg.in/ldap.v2/del.go create mode 100644 src/vendor/gopkg.in/ldap.v2/dn.go create mode 100644 src/vendor/gopkg.in/ldap.v2/doc.go create mode 100644 src/vendor/gopkg.in/ldap.v2/error.go create mode 100644 src/vendor/gopkg.in/ldap.v2/filter.go create mode 100644 src/vendor/gopkg.in/ldap.v2/ldap.go create mode 100644 src/vendor/gopkg.in/ldap.v2/modify.go create mode 100644 src/vendor/gopkg.in/ldap.v2/passwdmodify.go create mode 100644 src/vendor/gopkg.in/ldap.v2/search.go diff --git a/make/common/templates/ui/env b/make/common/templates/ui/env index e4feedc2e..a15f323b7 100644 --- a/make/common/templates/ui/env +++ b/make/common/templates/ui/env @@ -16,6 +16,7 @@ LDAP_BASE_DN=$ldap_basedn LDAP_FILTER=$ldap_filter LDAP_UID=$ldap_uid LDAP_SCOPE=$ldap_scope +LDAP_CONNECT_TIMEOUT=$ldap_connect_timeout UI_SECRET=$ui_secret SECRET_KEY=$secret_key SELF_REGISTRATION=$self_registration diff --git a/make/dev/ui/Dockerfile b/make/dev/ui/Dockerfile index 822797d76..1f7fb2fb2 100644 --- a/make/dev/ui/Dockerfile +++ b/make/dev/ui/Dockerfile @@ -21,7 +21,7 @@ COPY src/favicon.ico /go/bin/favicon.ico COPY make/jsminify.sh /tmp/jsminify.sh RUN chmod u+x /go/bin/harbor_ui \ - && sed -i 's/TLS_CACERT/#TLS_CAERT/g' /etc/ldap/ldap.conf \ + && sed -i 's/TLS_CACERT/#TLS_CACERT/g' /etc/ldap/ldap.conf \ && sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \ && timestamp=`date '+%s'` \ && /tmp/jsminify.sh /go/bin/views/sections/script-include.htm /go/bin/static/resources/js/harbor.app.min.$timestamp.js /go/bin/ \ diff --git a/make/harbor.cfg b/make/harbor.cfg index 8b8c6cd37..69118dbf3 100644 --- a/make/harbor.cfg +++ b/make/harbor.cfg @@ -52,6 +52,9 @@ ldap_uid = uid #the scope to search for users, 1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE ldap_scope = 3 +#Timeout (in seconds) when connecting to an LDAP Server. The default value (and most reasonable) is 5 seconds. +ldap_connect_timeout = 5 + #The password for the root user of mysql db, change this before any production use. db_password = root123 diff --git a/make/prepare b/make/prepare index 5e1b6b311..2610d5fa2 100755 --- a/make/prepare +++ b/make/prepare @@ -101,6 +101,7 @@ else: ldap_filter = "" ldap_uid = rcp.get("configuration", "ldap_uid") ldap_scope = rcp.get("configuration", "ldap_scope") +ldap_connect_timeout = rcp.get("configuration", "ldap_connect_timeout") db_password = rcp.get("configuration", "db_password") self_registration = rcp.get("configuration", "self_registration") use_compressed_js = rcp.get("configuration", "use_compressed_js") @@ -201,6 +202,7 @@ render(os.path.join(templates_dir, "ui", "env"), ldap_filter=ldap_filter, ldap_uid=ldap_uid, ldap_scope=ldap_scope, + ldap_connect_timeout=ldap_connect_timeout, self_registration=self_registration, use_compressed_js=use_compressed_js, ui_secret=ui_secret, diff --git a/make/ubuntu/ui/Dockerfile b/make/ubuntu/ui/Dockerfile index 067741a4e..d54749741 100644 --- a/make/ubuntu/ui/Dockerfile +++ b/make/ubuntu/ui/Dockerfile @@ -18,7 +18,7 @@ COPY ./src/favicon.ico /harbor/favicon.ico COPY ./make/jsminify.sh /tmp/jsminify.sh RUN chmod u+x /harbor/harbor_ui \ - && sed -i 's/TLS_CACERT/#TLS_CAERT/g' /etc/ldap/ldap.conf \ + && sed -i 's/TLS_CACERT/#TLS_CACERT/g' /etc/ldap/ldap.conf \ && sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \ && timestamp=`date '+%s'` \ && /tmp/jsminify.sh /harbor/views/sections/script-include.htm /harbor/static/resources/js/harbor.app.min.$timestamp.js /harbor/ \ diff --git a/src/ui/auth/ldap/ldap.go b/src/ui/auth/ldap/ldap.go index 46c563cbe..f5230ba6f 100644 --- a/src/ui/auth/ldap/ldap.go +++ b/src/ui/auth/ldap/ldap.go @@ -18,7 +18,11 @@ package ldap import ( "errors" "fmt" + "strconv" "strings" + "time" + + "crypto/tls" "github.com/vmware/harbor/src/common/utils/log" @@ -27,7 +31,7 @@ import ( "github.com/vmware/harbor/src/ui/auth" "github.com/vmware/harbor/src/ui/config" - "github.com/mqu/openldap" + goldap "gopkg.in/ldap.v2" ) // Auth implements Authenticator interface to authenticate against LDAP @@ -35,6 +39,59 @@ type Auth struct{} const metaChars = "&|!=~*<>()" +// Connect checks the LDAP configuration directives, and connects to the LDAP URL +// Returns an LDAP connection +func Connect() (*goldap.Conn, error) { + + ldapURL := config.LDAP().URL + if ldapURL == "" { + return nil, errors.New("can not get any available LDAP_URL") + } + log.Debug("ldapURL:", ldapURL) + + // This routine keeps compability with the old format used on harbor.cfg + splitLdapURL := strings.Split(ldapURL, "://") + protocol, hostport := splitLdapURL[0], splitLdapURL[1] + + var host, port string + + // This tries to detect the used port, if not defined + if strings.Contains(hostport, ":") { + splitHostPort := strings.Split(hostport, ":") + host, port = splitHostPort[0], splitHostPort[1] + } else { + host = hostport + switch protocol { + case "ldap": + port = "389" + case "ldaps": + port = "636" + } + } + + // Sets a Dial Timeout for LDAP + cTimeout := config.LDAP().ConnectTimeout + connectTimeout, _ := strconv.Atoi(cTimeout) + goldap.DefaultTimeout = time.Duration(connectTimeout) * time.Second + + var ldap *goldap.Conn + var err error + + 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 nil, err + } + + return ldap, nil + +} + // Authenticate checks user's credential against LDAP based on basedn template and LDAP URL, // if the check is successful a dummy record will be inserted into DB, such that this user can // be associated to other entities in the system. @@ -46,16 +103,11 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) { return nil, fmt.Errorf("the principal contains meta char: %q", c) } } - ldapURL := config.LDAP().URL - if ldapURL == "" { - return nil, errors.New("can not get any available LDAP_URL") - } - log.Debug("ldapURL:", ldapURL) - ldap, err := openldap.Initialize(ldapURL) + + ldap, err := Connect() if err != nil { return nil, err } - ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3) ldapBaseDn := config.LDAP().BaseDn if ldapBaseDn == "" { @@ -86,27 +138,42 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) { ldapScope := config.LDAP().Scope var scope int if ldapScope == "1" { - scope = openldap.LDAP_SCOPE_BASE + scope = goldap.ScopeBaseObject } else if ldapScope == "2" { - scope = openldap.LDAP_SCOPE_ONELEVEL + scope = goldap.ScopeSingleLevel } else { - scope = openldap.LDAP_SCOPE_SUBTREE + scope = goldap.ScopeWholeSubtree } attributes := []string{"uid", "cn", "mail", "email"} - result, err := ldap.SearchAll(ldapBaseDn, scope, filter, attributes) + + searchRequest := goldap.NewSearchRequest( + ldapBaseDn, + scope, + goldap.NeverDerefAliases, + 0, // Unlimited results. TODO: Limit this (as we expect only one result)? + 0, // Search Timeout. TODO: Limit this (check what is the unit of timeout) and make configurable + false, // Types Only + filter, + attributes, + nil, + ) + + result, err := ldap.Search(searchRequest) if err != nil { return nil, err } - if len(result.Entries()) == 0 { + + if len(result.Entries) == 0 { log.Warningf("Not found an entry.") return nil, nil - } else if len(result.Entries()) != 1 { + } else if len(result.Entries) != 1 { log.Warningf("Found more than one entry.") return nil, nil } - en := result.Entries()[0] - bindDN := en.Dn() - log.Debug("found entry:", en) + + entry := result.Entries[0] + bindDN := entry.DN + log.Debug("found entry:", bindDN) err = ldap.Bind(bindDN, m.Password) if err != nil { log.Debug("Bind user error", err) @@ -115,9 +182,10 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) { defer ldap.Close() u := models.User{} - for _, attr := range en.Attributes() { - val := attr.Values()[0] - switch attr.Name() { + + for _, attr := range entry.Attributes { + val := attr.Values[0] + switch attr.Name { case "uid": u.Realname = val case "cn": diff --git a/src/ui/config/config.go b/src/ui/config/config.go index 783779c4d..dd69504b6 100644 --- a/src/ui/config/config.go +++ b/src/ui/config/config.go @@ -26,13 +26,14 @@ import ( // LDAPSetting wraps the setting of an LDAP server type LDAPSetting struct { - URL string - BaseDn string - SearchDn string - SearchPwd string - UID string - Filter string - Scope string + URL string + BaseDn string + SearchDn string + SearchPwd string + UID string + Filter string + Scope string + ConnectTimeout string } type uiParser struct{} @@ -42,13 +43,14 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{}) mode := raw["AUTH_MODE"] if mode == "ldap_auth" { setting := LDAPSetting{ - URL: raw["LDAP_URL"], - BaseDn: raw["LDAP_BASE_DN"], - SearchDn: raw["LDAP_SEARCH_DN"], - SearchPwd: raw["LDAP_SEARCH_PWD"], - UID: raw["LDAP_UID"], - Filter: raw["LDAP_FILTER"], - Scope: raw["LDAP_SCOPE"], + URL: raw["LDAP_URL"], + BaseDn: raw["LDAP_BASE_DN"], + SearchDn: raw["LDAP_SEARCH_DN"], + SearchPwd: raw["LDAP_SEARCH_PWD"], + UID: raw["LDAP_UID"], + Filter: raw["LDAP_FILTER"], + Scope: raw["LDAP_SCOPE"], + ConnectTimeout: raw["LDAP_CONNECT_TIMEOUT"], } config["ldap"] = setting } @@ -83,7 +85,7 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{}) var uiConfig *commonConfig.Config func init() { - uiKeys := []string{"AUTH_MODE", "LDAP_URL", "LDAP_BASE_DN", "LDAP_SEARCH_DN", "LDAP_SEARCH_PWD", "LDAP_UID", "LDAP_FILTER", "LDAP_SCOPE", "TOKEN_EXPIRATION", "HARBOR_ADMIN_PASSWORD", "EXT_REG_URL", "UI_SECRET", "SECRET_KEY", "SELF_REGISTRATION", "PROJECT_CREATION_RESTRICTION", "REGISTRY_URL", "JOB_SERVICE_URL"} + uiKeys := []string{"AUTH_MODE", "LDAP_URL", "LDAP_BASE_DN", "LDAP_SEARCH_DN", "LDAP_SEARCH_PWD", "LDAP_UID", "LDAP_FILTER", "LDAP_SCOPE", "LDAP_CONNECT_TIMEOUT", "TOKEN_EXPIRATION", "HARBOR_ADMIN_PASSWORD", "EXT_REG_URL", "UI_SECRET", "SECRET_KEY", "SELF_REGISTRATION", "PROJECT_CREATION_RESTRICTION", "REGISTRY_URL", "JOB_SERVICE_URL"} uiConfig = &commonConfig.Config{ Config: make(map[string]interface{}), Loader: &commonConfig.EnvConfigLoader{Keys: uiKeys}, diff --git a/src/ui/config/config_test.go b/src/ui/config/config_test.go index 38df6641b..3f18b40b1 100644 --- a/src/ui/config/config_test.go +++ b/src/ui/config/config_test.go @@ -29,6 +29,7 @@ var ( "cn", "uid", "2", + "5", } tokenExp = "3" tokenExpRes = 3 @@ -52,6 +53,7 @@ func TestMain(m *testing.M) { os.Setenv("LDAP_UID", ldap.UID) os.Setenv("LDAP_SCOPE", ldap.Scope) os.Setenv("LDAP_FILTER", ldap.Filter) + os.Setenv("LDAP_CONNECT_TIMEOUT", ldap.ConnectTimeout) os.Setenv("TOKEN_EXPIRATION", tokenExp) os.Setenv("HARBOR_ADMIN_PASSWORD", adminPassword) os.Setenv("EXT_REG_URL", externalRegURL) @@ -76,6 +78,7 @@ func TestMain(m *testing.M) { os.Unsetenv("LDAP_UID") os.Unsetenv("LDAP_SCOPE") os.Unsetenv("LDAP_FILTER") + os.Unsetenv("LDAP_CONNECT_TIMEOUT") os.Unsetenv("TOKEN_EXPIRATION") os.Unsetenv("HARBOR_ADMIN_PASSWORD") os.Unsetenv("EXT_REG_URL") diff --git a/src/vendor/github.com/mqu/openldap/LICENCE.txt b/src/vendor/github.com/mqu/openldap/LICENCE.txt deleted file mode 100644 index 6d6a608da..000000000 --- a/src/vendor/github.com/mqu/openldap/LICENCE.txt +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (C) 2012 - Marc Quinton. - -Use of this source code is governed by the MIT Licence : - http://opensource.org/licenses/mit-license.php - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/src/vendor/github.com/mqu/openldap/README.md b/src/vendor/github.com/mqu/openldap/README.md deleted file mode 100644 index b32af5eaa..000000000 --- a/src/vendor/github.com/mqu/openldap/README.md +++ /dev/null @@ -1,82 +0,0 @@ -OpenLDAP -==== - -this is Openldap binding in GO language. I don't work any more with golang, so, please fork this project. - - -Installation : ------ - -Installation is easy and very quick, as you can see : - - # install openldap library and devel packages - sudo apt-get install libldap libldap2-dev # debian/ubuntu. - sudo urpmi openldap-devel # fedora, RH, ... - - # install go - go get github.com/mqu/openldap - - # verify you've got it : - (cd $GOPATH ; go list ./...) | grep openldap - -Usage ----- - -- Look a this [exemple](https://github.com/mqu/openldap/blob/master/_examples/test-openldap.go). -- a more complex example making [LDAP search](https://github.com/mqu/openldap/blob/master/_examples/ldapsearch.go) that mimics ldapsearch command, printing out result on console. - -Doc: ---- -- run _go doc openldap_, -- will come soon, complete documentation in this [Wiki](https://github.com/mqu/openldap/wiki). -- look at [_examples/](https://github.com/mqu/openldap/blob/master/_examples/)*.go to see how to use this library. - -Todo : ----- - - - thread-safe test, - - complete LDAP:GetOption() and LDAP:SetOption() method : now, they work only for integer values, - - avoid using deprecated function (see LDAP_DEPRECATED flag and "// DEPRECATED" comments in *.go sources), - - write some tests, - - verify memory leaks (Valgrind), - - support LDIF format (in, out), - - add support for external commands (ldapadd, ldapdelete) - - create an LDAP CLI (command line interface), like lftp, with commands like shell, - - a nice GUI with GTK, - - proxy, server, - - what else ? - - -Links : ----- - - - goc : http://code.google.com/p/go-wiki/wiki/cgo (how to bind native libraries to GO) - - Openldap library (and server) : http://www.openldap.org/ - - Pure Go [LDAP](https://github.com/mmitton/ldap) library, with [ASN1](https://github.com/mmitton/asn1-ber) support. - -Licence : ----- - -Copyright (C) 2012 - Marc Quinton. - -Use of this source code is governed by the MIT Licence : - http://opensource.org/licenses/mit-license.php - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/vendor/github.com/mqu/openldap/add-modify-delete.go b/src/vendor/github.com/mqu/openldap/add-modify-delete.go deleted file mode 100644 index 78675343d..000000000 --- a/src/vendor/github.com/mqu/openldap/add-modify-delete.go +++ /dev/null @@ -1,230 +0,0 @@ -/* - * - * Copyright (C) 2012 - Marc Quinton. - * - * Use of this source code is governed by the MIT Licence : - * http://opensource.org/licenses/mit-license.php - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package openldap - -/* #include -#include -#define LDAP_DEPRECATED 1 -#include - -// goc can not use union on structs ; so create a new type with same attributes and size -// fixme : support binary mods (mod_bvalues) -typedef struct ldapmod_str { - int mod_op; - char *mod_type; - char **mod_vals; -} LDAPModStr; - - -int _ldap_add(LDAP *ld, char* dn, LDAPModStr **attrs){ - - //API: int ldap_add_ext_s(LDAP *ld, char *dn, LDAPMod **attrs, LDAPControl *sctrls, LDAPControl *cctrls ); - // nota : cast (LDAPMod **) is possible because structs have same size - return ldap_add_ext_s(ld, dn, (LDAPMod **)attrs, NULL, NULL); -} - -int _ldap_modify(LDAP *ld, char* dn, LDAPModStr **mods ){ - - // nota : cast (LDAPMod **) is possible because structs have same size - return ldap_modify_ext_s( ld, dn, (LDAPMod **)mods, NULL, NULL); -} - -int _ldap_rename (LDAP *ld, char *dn, char *newrdn, char *newSuperior, int deleteoldrdn){ - //API: int ldap_rename_s( ld, dn, newrdn, newparent, deleteoldrdn, sctrls[], cctrls[]) - - return ldap_rename_s(ld, dn, newrdn, newSuperior, deleteoldrdn, NULL, NULL); -} - -void _ldap_mods_free (LDAPModStr **mods, int freemods){ - //API: void ldap_mods_free(LDAPMod **mods, int freemods); - return ldap_mods_free((LDAPMod **)mods, freemods); -} - - -*/ -// #cgo CFLAGS: -DLDAP_DEPRECATED=1 -// #cgo linux CFLAGS: -DLINUX=1 -// #cgo LDFLAGS: -lldap -llber -import "C" - -import ( - "errors" - "unsafe" - "fmt" -) - - -func (self *Ldap) doModify(dn string, attrs map[string][]string, changeType int, full_add bool) (int){ - - _dn := C.CString(dn) - defer C.free(unsafe.Pointer(_dn)) - - mods := make([]*C.LDAPModStr, len(attrs)+1) - // mods[len] = nil by default - - count:= 0 - for key, values := range attrs { - - // transform []string to C.char** null terminated array (attributes argument) - _values := make([]*C.char, len(values)+1) // default set to nil (NULL in C) - - for i, value := range values { - _values[i] = C.CString(value) - defer C.free(unsafe.Pointer(_values[i])) - } - - var mod C.LDAPModStr - - mod.mod_op = C.int(changeType) - mod.mod_type = C.CString(key) - mod.mod_vals = &_values[0] - - defer C.free(unsafe.Pointer(mod.mod_type)) - - mods[count] = &mod - - count++ - } - - var rv int - - if full_add { - // API: int ldap_add (LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) - rv = int(C._ldap_add(self.conn, _dn, &mods[0])) - } else{ - // API: int ldap_modify (LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) - rv = int(C._ldap_modify(self.conn, _dn, &mods[0])) - } - - // FIXME: need to call ldap_mods_free(&mods) some where. - // C._ldap_mods_free(&mods[0], 1) // not OK. - return rv -} - -func (self *Ldap) Modify(dn string, attrs map[string][]string) (error){ - - changeType := C.LDAP_MOD_REPLACE - full_add := false - rv := self.doModify(dn, attrs, changeType, full_add) - - if rv != LDAP_OPT_SUCCESS { - return errors.New(fmt.Sprintf("LDAP::Modify() error : %d (%s)", rv, ErrorToString(rv))) - } - return nil -} - -func (self *Ldap) ModifyDel(dn string, attrs map[string][]string) (error){ - - changeType := C.LDAP_MOD_DELETE - full_add := false - rv := self.doModify(dn, attrs, changeType, full_add) - - if rv != LDAP_OPT_SUCCESS { - return errors.New(fmt.Sprintf("LDAP::ModifyDel() error : %d (%s)", rv, ErrorToString(rv))) - } - return nil -} - -func (self *Ldap) ModifyAdd(dn string, attrs map[string][]string) (error){ - - changeType := C.LDAP_MOD_ADD - full_add := false - rv := self.doModify(dn, attrs, changeType, full_add) - if rv != LDAP_OPT_SUCCESS { - return errors.New(fmt.Sprintf("LDAP::ModifyAdd() error : %d (%s)", rv, ErrorToString(rv))) - } - return nil -} - -func (self *Ldap) Add(dn string, attrs map[string][]string) (error){ - - changeType := C.LDAP_MOD_ADD - full_add := true - rv := self.doModify(dn, attrs, changeType, full_add) - if rv != LDAP_OPT_SUCCESS { - return errors.New(fmt.Sprintf("LDAP::Add() error : %d (%s)", rv, ErrorToString(rv))) - } - return nil -} - -func (self *Ldap) Delete(dn string) (error){ - - _dn := C.CString(dn) - defer C.free(unsafe.Pointer(_dn)) - - // API: int ldap_delete (LDAP *ld, LDAP_CONST char *dn) - rv := C.ldap_delete_s(self.conn, _dn) - - if rv != LDAP_OPT_SUCCESS { - return errors.New(fmt.Sprintf("LDAP::Delete() error : %d (%s)", rv, ErrorToString(int(rv)))) - } - - return nil -} - -// Rename() to rename LDAP entries. -// -// These routines are used to perform a LDAP rename operation. The function changes the leaf compo- -// nent of an entry's distinguished name and optionally moves the entry to a new parent container. -// The ldap_rename_s performs a rename operation synchronously. The method takes dn, which points to -// the distinguished name of the entry whose attribute is being compared, newparent,the distinguished -// name of the entry's new parent. If this parameter is NULL, only the RDN is changed. The root DN is -// specified by passing a zero length string, "". deleteoldrdn specifies whether the old RDN should -// be retained or deleted. Zero indicates that the old RDN should be retained. If you choose this -// option, the attribute will contain both names (the old and the new). Non-zero indicates that the -// old RDN should be deleted. serverctrls points to an array of LDAPControl structures that list the -// client controls to use with this extended operation. Use NULL to specify no client controls. -// clientctrls points to an array of LDAPControl structures that list the client controls to use with -// the search. -// FIXME: support NULL and "" values for newSuperior parameter. -// -func (self *Ldap) Rename(dn string, newrdn string, newSuperior string, deleteOld bool) (error){ - - _dn := C.CString(dn) - defer C.free(unsafe.Pointer(_dn)) - - _newrdn := C.CString(newrdn) - defer C.free(unsafe.Pointer(_newrdn)) - - _newSuperior := C.CString(newSuperior) - defer C.free(unsafe.Pointer(_newSuperior)) - - var _delete C.int = 0 - if deleteOld { - _delete = 1 - } - - // API: int ldap_rename (LDAP *ld, char *newrdn, char *newSuperior, int deleteoldrdn) - rv := C._ldap_rename(self.conn, _dn, _newrdn, _newSuperior, _delete) - - if rv != LDAP_OPT_SUCCESS { - return errors.New(fmt.Sprintf("LDAP::Rename() error : %d (%s)", rv, ErrorToString(int(rv)))) - } - - return nil -} diff --git a/src/vendor/github.com/mqu/openldap/defines.go b/src/vendor/github.com/mqu/openldap/defines.go deleted file mode 100644 index 576f96a11..000000000 --- a/src/vendor/github.com/mqu/openldap/defines.go +++ /dev/null @@ -1,350 +0,0 @@ -/* - * - * Copyright (C) 2012 - Marc Quinton. - * - * Use of this source code is governed by the MIT Licence : - * http://opensource.org/licenses/mit-license.php - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package openldap - -const ( - // first version for this GO API binding - OPENLDAP_API_BINDING_VERSION = "0.2" -) - -const ( - LDAP_VERSION1 = 1 - LDAP_VERSION2 = 2 - LDAP_VERSION3 = 3 -) - -const ( - LDAP_VERSION_MIN = LDAP_VERSION2 - LDAP_VERSION = LDAP_VERSION2 - LDAP_VERSION_MAX = LDAP_VERSION3 -) - -const ( - LDAP_API_VERSION = 3001 - LDAP_VENDOR_NAME = "OpenLDAP" -) - -const ( - LDAP_PORT = 389 - LDAPS_PORT = 636 -) - -const ( - LDAP_OPT_SUCCESS = 0 - LDAP_OPT_ERROR = -1 -) - -// search scopes -const ( - LDAP_SCOPE_BASE = 0x0000 - LDAP_SCOPE_ONELEVEL = 0x0001 - LDAP_SCOPE_SUBTREE = 0x0002 - LDAP_SCOPE_SUBORDINATE = 0x0003 // OpenLDAP extension - LDAP_SCOPE_DEFAULT = -1 // OpenLDAP extension -) - -const ( - LDAP_SCOPE_BASEOBJECT = LDAP_SCOPE_BASE - LDAP_SCOPE_ONE = LDAP_SCOPE_ONELEVEL - LDAP_SCOPE_SUB = LDAP_SCOPE_SUBTREE - LDAP_SCOPE_CHILDREN = LDAP_SCOPE_SUBORDINATE -) - -const ( - LDAP_RES_ANY = -1 - LDAP_RES_UNSOLICITED = 0 -) - -//const ( -//LDAP_API_FEATURE_THREAD_SAFE = 1 -//LDAP_API_FEATURE_SESSION_THREAD_SAFE = 1 -//LDAP_API_FEATURE_OPERATION_THREAD_SAFE = 1 -//) - -const ( - LDAP_SUCCESS = 0x00 - LDAP_OPERATIONS_ERROR = 0x01 - LDAP_PROTOCOL_ERROR = 0x02 - LDAP_TIMELIMIT_EXCEEDED = 0x03 - LDAP_SIZELIMIT_EXCEEDED = 0x04 - LDAP_COMPARE_FALSE = 0x05 - LDAP_COMPARE_TRUE = 0x06 - LDAP_AUTH_METHOD_NOT_SUPPORTED = 0x07 - LDAP_STRONG_AUTH_REQUIRED = 0x08 - // Not used in LDAPv3 - LDAP_PARTIAL_RESULTS = 0x09 - - // Next 5 new in LDAPv3 - LDAP_REFERRAL = 0x0a - LDAP_ADMINLIMIT_EXCEEDED = 0x0b - LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 0x0c - LDAP_CONFIDENTIALITY_REQUIRED = 0x0d - LDAP_SASL_BIND_INPROGRESS = 0x0e - - LDAP_NO_SUCH_ATTRIBUTE = 0x10 - LDAP_UNDEFINED_TYPE = 0x11 - LDAP_INAPPROPRIATE_MATCHING = 0x12 - LDAP_CONSTRAINT_VIOLATION = 0x13 - LDAP_TYPE_OR_VALUE_EXISTS = 0x14 - LDAP_INVALID_SYNTAX = 0x15 - - LDAP_NO_SUCH_OBJECT = 0x20 /* 32 */ - LDAP_ALIAS_PROBLEM = 0x21 - LDAP_INVALID_DN_SYNTAX = 0x22 - // Next two not used in LDAPv3 - LDAP_IS_LEAF = 0x23 - LDAP_ALIAS_DEREF_PROBLEM = 0x24 - - LDAP_INAPPROPRIATE_AUTH = 0x30 /* 48 */ - LDAP_INVALID_CREDENTIALS = 0x31 /* 49 */ - LDAP_INSUFFICIENT_ACCESS = 0x32 - LDAP_BUSY = 0x33 - LDAP_UNAVAILABLE = 0x34 - LDAP_UNWILLING_TO_PERFORM = 0x35 - LDAP_LOOP_DETECT = 0x36 - - LDAP_SORT_CONTROL_MISSING = 0x3C /* 60 */ - LDAP_INDEX_RANGE_ERROR = 0x3D /* 61 */ - - LDAP_NAMING_VIOLATION = 0x40 - LDAP_OBJECT_CLASS_VIOLATION = 0x41 - LDAP_NOT_ALLOWED_ON_NONLEAF = 0x42 - LDAP_NOT_ALLOWED_ON_RDN = 0x43 - LDAP_ALREADY_EXISTS = 0x44 /* 68 */ - LDAP_NO_OBJECT_CLASS_MODS = 0x45 - LDAP_RESULTS_TOO_LARGE = 0x46 - // Next two for LDAPv3 - LDAP_AFFECTS_MULTIPLE_DSAS = 0x47 - LDAP_OTHER = 0x50 - - // Used by some APIs - LDAP_SERVER_DOWN = 0x51 - LDAP_LOCAL_ERROR = 0x52 - LDAP_ENCODING_ERROR = 0x53 - LDAP_DECODING_ERROR = 0x54 - LDAP_TIMEOUT = 0x55 - LDAP_AUTH_UNKNOWN = 0x56 - LDAP_FILTER_ERROR = 0x57 /* 87 */ - LDAP_USER_CANCELLED = 0x58 - LDAP_PARAM_ERROR = 0x59 - LDAP_NO_MEMORY = 0x5a - - // Preliminary LDAPv3 codes - LDAP_CONNECT_ERROR = 0x5b - LDAP_NOT_SUPPORTED = 0x5c - LDAP_CONTROL_NOT_FOUND = 0x5d - LDAP_NO_RESULTS_RETURNED = 0x5e - LDAP_MORE_RESULTS_TO_RETURN = 0x5f - LDAP_CLIENT_LOOP = 0x60 - LDAP_REFERRAL_LIMIT_EXCEEDED = 0x61 -) - -const ( - LDAP_DEREF_NEVER = 0 - LDAP_DEREF_SEARCHING = 1 - LDAP_DEREF_FINDING = 2 - LDAP_DEREF_ALWAYS = 3 -) - -const ( - LDAP_NO_LIMIT = 0 -) - -const ( - LDAP_MSG_ONE = 0 - LDAP_MSG_ALL = 1 - LDAP_MSG_RECEIVED = 2 -) - -// LDAP_OPTions -// 0x0000 - 0x0fff reserved for api options -// 0x1000 - 0x3fff reserved for api extended options -// 0x4000 - 0x7fff reserved for private and experimental options - -const ( - LDAP_OPT_API_INFO = 0x0000 - LDAP_OPT_DESC = 0x0001 // historic - LDAP_OPT_DEREF = 0x0002 - LDAP_OPT_SIZELIMIT = 0x0003 - LDAP_OPT_TIMELIMIT = 0x0004 - // 0x05 - 0x07 not defined - - LDAP_OPT_REFERRALS = 0x0008 - LDAP_OPT_RESTART = 0x0009 - // 0x0a - 0x10 not defined - - LDAP_OPT_PROTOCOL_VERSION = 0x0011 - LDAP_OPT_SERVER_CONTROLS = 0x0012 - LDAP_OPT_CLIENT_CONTROLS = 0x0013 - // 0x14 not defined - - LDAP_OPT_API_FEATURE_INFO = 0x0015 - // 0x16 - 0x2f not defined - - LDAP_OPT_HOST_NAME = 0x0030 - LDAP_OPT_RESULT_CODE = 0x0031 - LDAP_OPT_ERROR_NUMBER = LDAP_OPT_RESULT_CODE - LDAP_OPT_DIAGNOSTIC_MESSAGE = 0x0032 - LDAP_OPT_ERROR_STRING = LDAP_OPT_DIAGNOSTIC_MESSAGE - LDAP_OPT_MATCHED_DN = 0x0033 - // 0x0034 - 0x3fff not defined - - // 0x0091 used by Microsoft for LDAP_OPT_AUTO_RECONNECT - - LDAP_OPT_SSPI_FLAGS = 0x0092 - // 0x0093 used by Microsoft for LDAP_OPT_SSL_INFO - - // 0x0094 used by Microsoft for LDAP_OPT_REF_DEREF_CONN_PER_MSG - - LDAP_OPT_SIGN = 0x0095 - LDAP_OPT_ENCRYPT = 0x0096 - LDAP_OPT_SASL_METHOD = 0x0097 - // 0x0098 used by Microsoft for LDAP_OPT_AREC_EXCLUSIVE - - LDAP_OPT_SECURITY_CONTEXT = 0x0099 - -// 0x009A used by Microsoft for LDAP_OPT_ROOTDSE_CACHE - -// 0x009B - 0x3fff not defined - -) - -// API Extensions - -const LDAP_OPT_API_EXTENSION_BASE = 0x4000 // API extensions - -// private and experimental options - -// OpenLDAP specific options - -const ( - LDAP_OPT_DEBUG_LEVEL = 0x5001 // debug level - LDAP_OPT_TIMEOUT = 0x5002 // default timeout - LDAP_OPT_REFHOPLIMIT = 0x5003 // ref hop limit - LDAP_OPT_NETWORK_TIMEOUT = 0x5005 // socket level timeout - LDAP_OPT_URI = 0x5006 - LDAP_OPT_REFERRAL_URLS = 0x5007 // Referral URLs - LDAP_OPT_SOCKBUF = 0x5008 // sockbuf - LDAP_OPT_DEFBASE = 0x5009 // searchbase - LDAP_OPT_CONNECT_ASYNC = 0x5010 // create connections asynchronously - LDAP_OPT_CONNECT_CB = 0x5011 // connection callbacks - LDAP_OPT_SESSION_REFCNT = 0x5012 // session reference count -) - -// OpenLDAP TLS options - -const ( - LDAP_OPT_X_TLS = 0x6000 - LDAP_OPT_X_TLS_CTX = 0x6001 // OpenSSL CTX* - LDAP_OPT_X_TLS_CACERTFILE = 0x6002 - LDAP_OPT_X_TLS_CACERTDIR = 0x6003 - LDAP_OPT_X_TLS_CERTFILE = 0x6004 - LDAP_OPT_X_TLS_KEYFILE = 0x6005 - LDAP_OPT_X_TLS_REQUIRE_CERT = 0x6006 - LDAP_OPT_X_TLS_PROTOCOL_MIN = 0x6007 - LDAP_OPT_X_TLS_CIPHER_SUITE = 0x6008 - LDAP_OPT_X_TLS_RANDOM_FILE = 0x6009 - LDAP_OPT_X_TLS_SSL_CTX = 0x600a // OpenSSL SSL* - LDAP_OPT_X_TLS_CRLCHECK = 0x600b - LDAP_OPT_X_TLS_CONNECT_CB = 0x600c - LDAP_OPT_X_TLS_CONNECT_ARG = 0x600d - LDAP_OPT_X_TLS_DHFILE = 0x600e - LDAP_OPT_X_TLS_NEWCTX = 0x600f - LDAP_OPT_X_TLS_CRLFILE = 0x6010 // GNUtls only - LDAP_OPT_X_TLS_PACKAGE = 0x6011 -) - -const ( - LDAP_OPT_X_TLS_NEVER = 0 - LDAP_OPT_X_TLS_HARD = 1 - LDAP_OPT_X_TLS_DEMAND = 2 - LDAP_OPT_X_TLS_ALLOW = 3 - LDAP_OPT_X_TLS_TRY = 4 -) - -const ( - LDAP_OPT_X_TLS_CRL_NONE = 0 - LDAP_OPT_X_TLS_CRL_PEER = 1 - LDAP_OPT_X_TLS_CRL_ALL = 2 -) - -// for LDAP_OPT_X_TLS_PROTOCOL_MIN - -//!!! const ( -//!!! LDAP_OPT_X_TLS_PROTOCOL(maj,min) = (((maj) << 8) + (min)) -//!!! LDAP_OPT_X_TLS_PROTOCOL_SSL2 = (2 << 8) -//!!! LDAP_OPT_X_TLS_PROTOCOL_SSL3 = (3 << 8) -//!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 = ((3 << 8) + 1) -//!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 = ((3 << 8) + 2) -//!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 = ((3 << 8) + 3) -//!!! ) - -// OpenLDAP SASL options - -const ( - LDAP_OPT_X_SASL_MECH = 0x6100 - LDAP_OPT_X_SASL_REALM = 0x6101 - LDAP_OPT_X_SASL_AUTHCID = 0x6102 - LDAP_OPT_X_SASL_AUTHZID = 0x6103 - LDAP_OPT_X_SASL_SSF = 0x6104 // read-only - LDAP_OPT_X_SASL_SSF_EXTERNAL = 0x6105 // write-only - LDAP_OPT_X_SASL_SECPROPS = 0x6106 // write-only - LDAP_OPT_X_SASL_SSF_MIN = 0x6107 - LDAP_OPT_X_SASL_SSF_MAX = 0x6108 - LDAP_OPT_X_SASL_MAXBUFSIZE = 0x6109 - LDAP_OPT_X_SASL_MECHLIST = 0x610a // read-only - LDAP_OPT_X_SASL_NOCANON = 0x610b - LDAP_OPT_X_SASL_USERNAME = 0x610c // read-only - LDAP_OPT_X_SASL_GSS_CREDS = 0x610d -) - -// OpenLDAP GSSAPI options - -const ( - LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT = 0x6200 - LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL = 0x6201 -) - -// -// OpenLDAP per connection tcp-keepalive settings -// (Linux only, ignored where unsupported) -const ( - LDAP_OPT_X_KEEPALIVE_IDLE = 0x6300 - LDAP_OPT_X_KEEPALIVE_PROBES = 0x6301 - LDAP_OPT_X_KEEPALIVE_INTERVAL = 0x6302 -) - -/* authentication methods available */ -const ( - LDAP_AUTH_NONE = 0x00 // no authentication - LDAP_AUTH_SIMPLE = 0x80 // context specific + primitive - LDAP_AUTH_SASL = 0xa3 // context specific + constructed - LDAP_AUTH_KRBV4 = 0xff // means do both of the following - LDAP_AUTH_KRBV41 = 0x81 // context specific + primitive - LDAP_AUTH_KRBV42 = 0x82 // context specific + primitive -) diff --git a/src/vendor/github.com/mqu/openldap/openldap.go b/src/vendor/github.com/mqu/openldap/openldap.go deleted file mode 100644 index 8a45b2787..000000000 --- a/src/vendor/github.com/mqu/openldap/openldap.go +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Openldap (2.4.30) binding in GO - * - * - * link to ldap or ldap_r (for thread-safe binding) - * - * - * Copyright (C) 2012 - Marc Quinton. - * - * Use of this source code is governed by the MIT Licence : - * http://opensource.org/licenses/mit-license.php - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -package openldap - -/* - -#define LDAP_DEPRECATED 1 -#include -#include - -static inline char* to_charptr(const void* s) { return (char*)s; } -static inline LDAPControl** to_ldapctrlptr(const void* s) { - return (LDAPControl**) s; -} -*/ -// #cgo CFLAGS: -DLDAP_DEPRECATED=1 -// #cgo linux CFLAGS: -DLINUX=1 -// #cgo LDFLAGS: -lldap -llber -import "C" - -import ( - "errors" - "fmt" - "unsafe" - "strings" - "strconv" -) - -/* Intialize() open an LDAP connexion ; supported url formats : - * - * ldap://host:389/ - * ldaps://secure-host:636/ - * - * return values : - * - on success : LDAP object, nil - * - on error : nil and error with error description. - */ -func Initialize(url string) (*Ldap, error) { - _url := C.CString(url) - defer C.free(unsafe.Pointer(_url)) - - var ldap *C.LDAP - - // API: int ldap_initialize (LDAP **ldp, LDAP_CONST char *url ) - rv := C.ldap_initialize(&ldap, _url) - - if rv != 0 { - err := errors.New(fmt.Sprintf("LDAP::Initialize() error (%d) : %s", rv, ErrorToString(int(rv)))) - return nil, err - } - - return &Ldap{ldap}, nil -} - -/* - * StartTLS() is used for regular LDAP (not - * LDAPS) connections to establish encryption - * after the session is running. - * - * return value : - * - nil on success, - * - error with error description on error. - */ -func (self *Ldap) StartTLS() error { - var rv int - - // API: int ldap_start_tls_s(LDAP *ld, LDAPControl **serverctrls, LDAPControl **clientctrls); - rv = int(C.ldap_start_tls_s(self.conn, - C.to_ldapctrlptr(unsafe.Pointer(nil)), - C.to_ldapctrlptr(unsafe.Pointer(nil)))) - - if rv == LDAP_OPT_SUCCESS { - return nil - } - - return errors.New(fmt.Sprintf("LDAP::StartTLS() error (%d) : %s", rv, - ErrorToString(rv))) -} - -/* - * Bind() is used for LDAP authentifications - * - * if who is empty this is an anonymous bind - * else this is an authentificated bind - * - * return value : - * - nil on succes, - * - error with error description on error. - * - */ -func (self *Ldap) Bind(who, cred string) error { - var rv int - - authmethod := C.int(LDAP_AUTH_SIMPLE) - - // DEPRECATED - // API: int ldap_bind_s (LDAP *ld, LDAP_CONST char *who, LDAP_CONST char *cred, int authmethod ); - if who == "" { - _who := C.to_charptr(unsafe.Pointer(nil)) - _cred := C.to_charptr(unsafe.Pointer(nil)) - - rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod)) - } else { - _who := C.CString(who) - _cred := C.CString(cred) - defer C.free(unsafe.Pointer(_who)) - rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod)) - } - - if rv == LDAP_OPT_SUCCESS { - return nil - } - - self.conn = nil - return errors.New(fmt.Sprintf("LDAP::Bind() error (%d) : %s", rv, ErrorToString(rv))) -} - -/* - * close LDAP connexion - * - * return value : - * - nil on succes, - * - error with error description on error. - * - */ -func (self *Ldap) Close() error { - - // DEPRECATED - // API: int ldap_unbind(LDAP *ld) - rv := C.ldap_unbind(self.conn) - - if rv == LDAP_OPT_SUCCESS { - return nil - } - - self.conn = nil - return errors.New(fmt.Sprintf("LDAP::Close() error (%d) : %s", int(rv), ErrorToString(int(rv)))) - -} -/* - * Unbind() close LDAP connexion - * - * an alias to Ldap::Close() - * - */ -func (self *Ldap) Unbind() error { - return self.Close() -} - -/* Search() is used to search LDAP server - - base is where search is starting - - scope allows local or deep search. Supported values : - - LDAP_SCOPE_BASE - - LDAP_SCOPE_ONELEVEL - - LDAP_SCOPE_SUBTREE - - filter is an LDAP search expression, - - attributes is an array of string telling with LDAP attribute to get from this request -*/ -func (self *Ldap) Search(base string, scope int, filter string, attributes []string) (*LdapMessage, error) { - - var attrsonly int = 0 // false: returns all, true, returns only attributes without values - - _base := C.CString(base) - defer C.free(unsafe.Pointer(_base)) - - _filter := C.CString(filter) - defer C.free(unsafe.Pointer(_filter)) - - // transform []string to C.char** null terminated array (attributes argument) - _attributes := make([]*C.char, len(attributes)+1) // default set to nil (NULL in C) - - for i, arg := range attributes { - _attributes[i] = C.CString(arg) - defer C.free(unsafe.Pointer(_attributes[i])) - } - - var msg *C.LDAPMessage - - // DEPRECATED - // API: int ldap_search_s (LDAP *ld, char *base, int scope, char *filter, char **attrs, int attrsonly, LdapMessage * ldap_res) - rv := int(C.ldap_search_s(self.conn, _base, C.int(scope), _filter, &_attributes[0], C.int(attrsonly), &msg)) - - if rv == LDAP_OPT_SUCCESS { - _msg := new(LdapMessage) - _msg.ldap = self - _msg.errno = rv - _msg.msg = msg - return _msg, nil - } - - return nil, errors.New(fmt.Sprintf("LDAP::Search() error : %d (%s)", rv, ErrorToString(rv))) -} - -// ------------------------------------- Ldap* method (object oriented) ------------------------------------------------------------------- - -// Create a new LdapAttribute entry with name and values. -func LdapAttributeNew(name string, values []string)(*LdapAttribute){ - a := new(LdapAttribute) - a.values = values - a.name = name - return a -} - -// Append() adds an LdapAttribute to self LdapEntry -func (self *LdapEntry) Append(a LdapAttribute){ - self.values = append(self.values, a) -} - -// String() is used for fmt.Println(self) -// -func (self *LdapAttribute) String() string{ - return self.ToText() -} - -// ToText() returns a text string representation of LdapAttribute -// avoiding displaying binary data. -// -func (self *LdapAttribute) ToText() string{ - - var list []string - - for _, a := range self.Values() { - if (!_isPrint(a)) { - list = append(list, fmt.Sprintf("binary-data[%d]", len(a))) - } else { - list = append(list, a) - } - } - if len(list) > 1 { - return fmt.Sprintf("%s: (%d)[%s]", self.name, len(list), strings.Join(list, ", ")) - } - return fmt.Sprintf("%s: [%s]", self.name, strings.Join(list, ", ")) -} - -// Name() return attribute name -func (self *LdapAttribute) Name() string{ - return self.name -} - -// Values() returns array values for self LdapAttribute -// -func (self *LdapAttribute) Values() []string{ - return self.values -} - -// _isPrint() returns true if str is printable -// -// @private method -func _isPrint(str string) bool{ - for _, c := range str{ - - if !strconv.IsPrint(rune(c)) { - return false - } - } - - return true -} - -// IsPrint() returns true is self LdapAttribute is printable. -func (self *LdapAttribute) IsPrint() bool{ - for _, a := range self.Values() { - if (!_isPrint(a)) { - return false - } - } - return true -} - -// Dn() returns DN (Distinguish Name) for self LdapEntry -func (self *LdapEntry) Dn() string{ - return self.dn -} - -// Attributes() returns an array of LdapAttribute -func (self *LdapEntry) Attributes() []LdapAttribute{ - return self.values -} - -// Print() allow printing self LdapEntry with fmt.Println() -func (self *LdapEntry) String() string { - return self.ToText() -} - -// GetValuesByName() get a list of values for self LdapEntry, using "name" attribute -func (self *LdapEntry) GetValuesByName(attrib string) []string{ - - for _, a := range self.values{ - if a.Name() == attrib { - return a.values - } - } - - return []string{} -} -// GetOneValueByName() ; a quick way to get a single attribute value -func (self *LdapEntry) GetOneValueByName(attrib string) (string, error){ - - for _, a := range self.values{ - if a.Name() == attrib { - return a.values[0], nil - } - } - - return "", errors.New(fmt.Sprintf("LdapEntry::GetOneValueByName() error : attribute %s not found", attrib)) -} - -// ToText() return a string representating self LdapEntry -func (self *LdapEntry) ToText() string{ - - txt := fmt.Sprintf("dn: %s\n", self.dn) - - for _, a := range self.values{ - txt = txt + fmt.Sprintf("%s\n", a.ToText()) - } - - return txt -} - -// Append() add e to LdapSearchResult array -func (self *LdapSearchResult) Append(e LdapEntry){ - self.entries = append(self.entries, e) -} - -// ToText() : a quick way to print an LdapSearchResult -func (self *LdapSearchResult) ToText() string{ - - txt := fmt.Sprintf("# query : %s\n", self.filter) - txt = txt + fmt.Sprintf("# num results : %d\n", self.Count()) - txt = txt + fmt.Sprintf("# search : %s\n", self.Filter()) - txt = txt + fmt.Sprintf("# base : %s\n", self.Base()) - txt = txt + fmt.Sprintf("# attributes : [%s]\n", strings.Join(self.Attributes(), ", ")) - - for _, e := range self.entries{ - txt = txt + fmt.Sprintf("%s\n", e.ToText()) - } - - return txt -} - -// String() : used for fmt.Println(self) -func (self *LdapSearchResult) String() string{ - return self.ToText() -} - -// Entries() : returns an array of LdapEntry for self -func (self *LdapSearchResult) Entries() []LdapEntry{ - return self.entries -} - -// Count() : returns number of results for self search. -func (self *LdapSearchResult) Count() int{ - return len(self.entries) -} - -// Filter() : returns filter for self search -func (self *LdapSearchResult) Filter() string{ - return self.filter -} - -// Filter() : returns base DN for self search -func (self *LdapSearchResult) Base() string{ - return self.base -} - -// Filter() : returns scope for self search -func (self *LdapSearchResult) Scope() int{ - return self.scope -} - -// Filter() : returns an array of attributes used for this actual search -func (self *LdapSearchResult) Attributes() []string{ - return self.attributes -} - -// SearchAll() : a quick way to make search. This method returns an LdapSearchResult with all necessary methods to -// access data. Result is a collection (tree) of []LdapEntry / []LdapAttribute. -// -func (self *Ldap) SearchAll(base string, scope int, filter string, attributes []string) (*LdapSearchResult, error) { - - sr := new(LdapSearchResult) - - sr.ldap = self - sr.base = base - sr.scope = scope - sr.filter = filter - sr.attributes = attributes - - // Search(base string, scope int, filter string, attributes []string) (*LDAPMessage, error) - result, err := self.Search(base, scope, filter, attributes) - - if err != nil { - fmt.Println(err) - return sr, err - } - - // Free LDAP::Result() allocated data - defer result.MsgFree() - - e := result.FirstEntry() - - for e != nil { - _e := new(LdapEntry) - - _e.dn = e.GetDn() - - attr, _ := e.FirstAttribute() - for attr != "" { - - _attr := LdapAttributeNew(attr, e.GetValues(attr)) - _e.Append(*_attr) - - attr, _ = e.NextAttribute() - - } - - sr.Append(*_e) - - e = e.NextEntry() - } - - return sr, nil -} diff --git a/src/vendor/github.com/mqu/openldap/options-errors.go b/src/vendor/github.com/mqu/openldap/options-errors.go deleted file mode 100644 index 04f926843..000000000 --- a/src/vendor/github.com/mqu/openldap/options-errors.go +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * Copyright (C) 2012 - Marc Quinton. - * - * Use of this source code is governed by the MIT Licence : - * http://opensource.org/licenses/mit-license.php - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package openldap - -/* -#include - -static inline char* to_charptr(const void* s) { return (char*)s; } - -*/ -// #cgo CFLAGS: -DLDAP_DEPRECATED=1 -// #cgo linux CFLAGS: -DLINUX=1 -// #cgo LDFLAGS: -lldap -llber -import "C" - -import ( - "errors" - "fmt" - "unsafe" -) - -// FIXME : support all kind of option (int, int*, ...) -func (self *Ldap) SetOption(opt int, val int) error { - - // API: ldap_set_option (LDAP *ld,int option, LDAP_CONST void *invalue)); - rv := C.ldap_set_option(self.conn, C.int(opt), unsafe.Pointer(&val)) - - if rv == LDAP_OPT_SUCCESS { - return nil - } - - return errors.New(fmt.Sprintf("LDAP::SetOption() error (%d) : %s", int(rv), ErrorToString(int(rv)))) -} - -// FIXME : support all kind of option (int, int*, ...) should take care of all return type for ldap_get_option -func (self *Ldap) GetOption(opt int) (val int, err error) { - - // API: int ldap_get_option (LDAP *ld,int option, LDAP_CONST void *invalue)); - rv := C.ldap_get_option(self.conn, C.int(opt), unsafe.Pointer(&val)) - - if rv == LDAP_OPT_SUCCESS { - return val, nil - } - - return 0, errors.New(fmt.Sprintf("LDAP::GetOption() error (%d) : %s", rv, ErrorToString(int(rv)))) -} - -/* -** WORK IN PROGRESS! -** -** OpenLDAP reentrancy/thread-safeness should be dynamically -** checked using ldap_get_option(). -** -** The -lldap implementation is not thread-safe. -** -** The -lldap_r implementation is: -** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety) -** but also be: -** LDAP_API_FEATURE_SESSION_THREAD_SAFE -** LDAP_API_FEATURE_OPERATION_THREAD_SAFE -** -** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE -** can be used to determine if -lldap_r is available at compile -** time. You must define LDAP_THREAD_SAFE if and only if you -** link with -lldap_r. -** -** If you fail to define LDAP_THREAD_SAFE when linking with -** -lldap_r or define LDAP_THREAD_SAFE when linking with -lldap, -** provided header definations and declarations may be incorrect. -** - */ - -func (self *Ldap) IsThreadSafe() bool { - // fmt.Println("IsThreadSafe()") - // opt, err := self.GetOption(LDAP_API_FEATURE_THREAD_SAFE) ; fmt.Println(opt, err) - // opt, err = self.GetOption(LDAP_THREAD_SAFE) ; fmt.Println(opt, err) - // opt, err = self.GetOption(LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE) ; fmt.Println(opt, err) - - //FIXME: need to implement LDAP::GetOption(LDAP_OPT_API_FEATURE_INFO) - return false -} - -func ErrorToString(err int) string { - - // API: char * ldap_err2string (int err ) - result := C.GoString(C.to_charptr(unsafe.Pointer(C.ldap_err2string(C.int(err))))) - return result -} - -func (self *Ldap) Errno() int { - opt, _ := self.GetOption(LDAP_OPT_ERROR_NUMBER) - return opt -} diff --git a/src/vendor/github.com/mqu/openldap/results.go b/src/vendor/github.com/mqu/openldap/results.go deleted file mode 100644 index 14460b848..000000000 --- a/src/vendor/github.com/mqu/openldap/results.go +++ /dev/null @@ -1,306 +0,0 @@ -/* - * - * Copyright (C) 2012 - Marc Quinton. - * - * Use of this source code is governed by the MIT Licence : - * http://opensource.org/licenses/mit-license.php - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package openldap - -/*#include -#include -#include -#include - -int _berval_get_len(struct berval **ber, int i){ - return ber[i]->bv_len; -} - -char* _berval_get_value(struct berval **ber, int i){ - return ber[i]->bv_val; -} - -*/ -// #cgo CFLAGS: -DLDAP_DEPRECATED=1 -// #cgo linux CFLAGS: -DLINUX=1 -// #cgo LDFLAGS: -lldap -llber -import "C" - -import ( - "errors" - "fmt" - "unsafe" -) - -// ------------------------------------------ RESULTS methods --------------------------------------------- -/* - - openldap C API : - - int ldap_count_messages( LDAP *ld, LdapMessage *result ) - LdapMessage *ldap_first_message( LDAP *ld, LdapMessage *result ) - LdapMessage *ldap_next_message ( LDAP *ld, LdapMessage *message ) - - int ldap_count_entries( LDAP *ld, LdapMessage *result ) - LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result ) - LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry ) - - char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr ) - char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber ) - - char **ldap_get_values(LDAP *ld, LdapMessage *entry, char *attr) - struct berval **ldap_get_values_len(LDAP *ld, LdapMessage *entry,char *attr) - - int ldap_count_values(char **vals) - int ldap_count_values_len(struct berval **vals) - void ldap_value_free(char **vals) - void ldap_value_free_len(struct berval **vals) - -*/ - -func (self *LdapMessage) Count() int { - // API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain ) - // err : (count = -1) - count := int(C.ldap_count_messages(self.ldap.conn, self.msg)) - if count == -1 { - panic("LDAP::Count() (ldap_count_messages) error (-1)") - } - return count - -} - -func (self *LdapMessage) FirstMessage() *LdapMessage { - - var msg *C.LDAPMessage - msg = C.ldap_first_message(self.ldap.conn, self.msg) - if msg == nil { - return nil - } - _msg := new(LdapMessage) - _msg.ldap = self.ldap - _msg.errno = 0 - _msg.msg = msg - return _msg -} - -func (self *LdapMessage) NextMessage() *LdapMessage { - var msg *C.LDAPMessage - msg = C.ldap_next_message(self.ldap.conn, self.msg) - - if msg == nil { - return nil - } - _msg := new(LdapMessage) - _msg.ldap = self.ldap - _msg.errno = 0 - _msg.msg = msg - return _msg -} - -/* an alias to ldap_count_message() ? */ -func (self *LdapEntry) CountEntries() int { - // API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain ) - // err : (count = -1) - return int(C.ldap_count_entries(self.ldap.conn, self.entry)) -} - -func (self *LdapMessage) FirstEntry() *LdapEntry { - - var msg *C.LDAPMessage - // API: LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result ) - msg = C.ldap_first_entry(self.ldap.conn, self.msg) - if msg == nil { - return nil - } - _msg := new(LdapEntry) - _msg.ldap = self.ldap - _msg.errno = 0 - _msg.entry = msg - return _msg -} - -func (self *LdapEntry) NextEntry() *LdapEntry { - var msg *C.LDAPMessage - // API: LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry ) - msg = C.ldap_next_entry(self.ldap.conn, self.entry) - - if msg == nil { - return nil - } - _msg := new(LdapEntry) - _msg.ldap = self.ldap - _msg.errno = 0 - _msg.entry = msg - return _msg -} - -func (self *LdapEntry) FirstAttribute() (string, error) { - - var ber *C.BerElement - - // API: char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr ) - rv := C.ldap_first_attribute(self.ldap.conn, self.entry, &ber) - - if rv == nil { - // error - return "", nil - } - self.ber = ber - return C.GoString(rv), nil -} - -func (self *LdapEntry) NextAttribute() (string, error) { - - // API: char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber ) - rv := C.ldap_next_attribute(self.ldap.conn, self.entry, self.ber) - - if rv == nil { - // error - return "", nil - } - return C.GoString(rv), nil -} - -// private func -func sptr(p uintptr) *C.char { - return *(**C.char)(unsafe.Pointer(p)) -} - -// private func used to convert null terminated char*[] to go []string -func cstrings_array(x **C.char) []string { - var s []string - for p := uintptr(unsafe.Pointer(x)); sptr(p) != nil; p += unsafe.Sizeof(uintptr(0)) { - s = append(s, C.GoString(sptr(p))) - } - return s -} - -// GetValues() return an array of string containing values for LDAP attribute "attr". -// Binary data are supported. -func (self *LdapEntry) GetValues(attr string) []string { - var s []string - - _attr := C.CString(attr) - defer C.free(unsafe.Pointer(_attr)) - - var bv **C.struct_berval - - //API: struct berval **ldap_get_values_len(LDAP *ld, LDAPMessage *entry, char *attr) - bv = C.ldap_get_values_len(self.ldap.conn, self.entry, _attr) - - var i int - count := int(C.ldap_count_values_len(bv)) - - for i = 0 ; i < count; i++ { - s = append(s, C.GoStringN(C._berval_get_value(bv, C.int(i)), C._berval_get_len(bv, C.int(i)))) - } - - // free allocated array (bv) - C.ldap_value_free_len(bv) - - return s -} - -// ------------------------------------------------ RESULTS ----------------------------------------------- -/* - int ldap_result ( LDAP *ld, int msgid, int all, struct timeval *timeout, LdapMessage **result ); - int ldap_msgfree( LdapMessage *msg ); - int ldap_msgtype( LdapMessage *msg ); - int ldap_msgid ( LdapMessage *msg ); - -*/ - -// Result() -// take care to free LdapMessage result with MsgFree() -// -func (self *Ldap) Result() (*LdapMessage, error) { - - var msgid int = 1 - var all int = 1 - - var tv C.struct_timeval - tv.tv_sec = 30 - - var msg *C.LDAPMessage - - // API: int ldap_result( LDAP *ld, int msgid, int all, struct timeval *timeout, LDAPMessage **result ); - rv := C.ldap_result(self.conn, C.int(msgid), C.int(all), &tv, &msg) - - if rv != LDAP_OPT_SUCCESS { - return nil, errors.New(fmt.Sprintf("LDAP::Result() error : %d (%s)", rv, ErrorToString(int(rv)))) - } - - _msg := new(LdapMessage) - _msg.ldap = self - _msg.errno = int(rv) - _msg.msg = msg - - return _msg, nil -} - -// MsgFree() is used to free LDAP::Result() allocated data -// -// returns -1 on error. -// -func (self *LdapMessage) MsgFree() int{ - if self.msg != nil { - rv := C.ldap_msgfree(self.msg) - self.msg = nil - return int(rv) - } - return -1 -} - - -// ---------------------------------------- DN Methods --------------------------------------------------- -/* - - char *ldap_get_dn( LDAP *ld, LdapMessage *entry) - int ldap_str2dn( const char *str, LDAPDN *dn, unsigned flags) - void ldap_dnfree( LDAPDN dn) - int ldap_dn2str( LDAPDN dn, char **str, unsigned flags) - - char **ldap_explode_dn( const char *dn, int notypes) - char **ldap_explode_rdn( const char *rdn, int notypes) - - char *ldap_dn2ufn ( const char * dn ) - char *ldap_dn2dcedn( const char * dn ) - char *ldap_dcedn2dn( const char * dn ) - char *ldap_dn2ad_canonical( const char * dn ) - -*/ - -// GetDn() return the DN (Distinguish Name) for self LdapEntry -func (self *LdapEntry) GetDn() string { - // API: char *ldap_get_dn( LDAP *ld, LDAPMessage *entry ) - rv := C.ldap_get_dn(self.ldap.conn, self.entry) - defer C.free(unsafe.Pointer(rv)) - - if rv == nil { - err := self.ldap.Errno() - panic(fmt.Sprintf("LDAP::GetDn() error %d (%s)", err, ErrorToString(err))) - } - - val := C.GoString(rv) - return val -} diff --git a/src/vendor/github.com/mqu/openldap/types.go b/src/vendor/github.com/mqu/openldap/types.go deleted file mode 100644 index d78628e65..000000000 --- a/src/vendor/github.com/mqu/openldap/types.go +++ /dev/null @@ -1,76 +0,0 @@ -/* - * - * Copyright (C) 2012 - Marc Quinton. - * - * Use of this source code is governed by the MIT Licence : - * http://opensource.org/licenses/mit-license.php - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package openldap - -/* -# include - -*/ -// #cgo CFLAGS: -DLDAP_DEPRECATED=1 -// #cgo linux CFLAGS: -DLINUX=1 -// #cgo LDFLAGS: -lldap -llber -import "C" - -type Ldap struct { - conn *C.LDAP -} - -type LdapMessage struct { - ldap *Ldap - // conn *C.LDAP - msg *C.LDAPMessage - errno int -} - -type LdapAttribute struct{ - name string - values []string -} - - -type LdapEntry struct { - ldap *Ldap - // conn *C.LDAP - entry *C.LDAPMessage - errno int - ber *C.BerElement - - dn string - values []LdapAttribute -} - -type LdapSearchResult struct{ - ldap *Ldap - - scope int - filter string - base string - attributes []string - - entries []LdapEntry -} diff --git a/src/vendor/gopkg.in/asn1-ber.v1/LICENSE b/src/vendor/gopkg.in/asn1-ber.v1/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/vendor/gopkg.in/asn1-ber.v1/README.md b/src/vendor/gopkg.in/asn1-ber.v1/README.md new file mode 100644 index 000000000..e3a9560d6 --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/README.md @@ -0,0 +1,24 @@ +[![GoDoc](https://godoc.org/gopkg.in/asn1-ber.v1?status.svg)](https://godoc.org/gopkg.in/asn1-ber.v1) [![Build Status](https://travis-ci.org/go-asn1-ber/asn1-ber.svg)](https://travis-ci.org/go-asn1-ber/asn1-ber) + + +ASN1 BER Encoding / Decoding Library for the GO programming language. +--------------------------------------------------------------------- + +Required libraries: + None + +Working: + Very basic encoding / decoding needed for LDAP protocol + +Tests Implemented: + A few + +TODO: + Fix all encoding / decoding to conform to ASN1 BER spec + Implement Tests / Benchmarks + +--- + +The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) +The design is licensed under the Creative Commons 3.0 Attributions license. +Read this article for more details: http://blog.golang.org/gopher diff --git a/src/vendor/gopkg.in/asn1-ber.v1/ber.go b/src/vendor/gopkg.in/asn1-ber.v1/ber.go new file mode 100644 index 000000000..25cc921be --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/ber.go @@ -0,0 +1,504 @@ +package ber + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "reflect" +) + +type Packet struct { + Identifier + Value interface{} + ByteValue []byte + Data *bytes.Buffer + Children []*Packet + Description string +} + +type Identifier struct { + ClassType Class + TagType Type + Tag Tag +} + +type Tag uint64 + +const ( + TagEOC Tag = 0x00 + TagBoolean Tag = 0x01 + TagInteger Tag = 0x02 + TagBitString Tag = 0x03 + TagOctetString Tag = 0x04 + TagNULL Tag = 0x05 + TagObjectIdentifier Tag = 0x06 + TagObjectDescriptor Tag = 0x07 + TagExternal Tag = 0x08 + TagRealFloat Tag = 0x09 + TagEnumerated Tag = 0x0a + TagEmbeddedPDV Tag = 0x0b + TagUTF8String Tag = 0x0c + TagRelativeOID Tag = 0x0d + TagSequence Tag = 0x10 + TagSet Tag = 0x11 + TagNumericString Tag = 0x12 + TagPrintableString Tag = 0x13 + TagT61String Tag = 0x14 + TagVideotexString Tag = 0x15 + TagIA5String Tag = 0x16 + TagUTCTime Tag = 0x17 + TagGeneralizedTime Tag = 0x18 + TagGraphicString Tag = 0x19 + TagVisibleString Tag = 0x1a + TagGeneralString Tag = 0x1b + TagUniversalString Tag = 0x1c + TagCharacterString Tag = 0x1d + TagBMPString Tag = 0x1e + TagBitmask Tag = 0x1f // xxx11111b + + // HighTag indicates the start of a high-tag byte sequence + HighTag Tag = 0x1f // xxx11111b + // HighTagContinueBitmask indicates the high-tag byte sequence should continue + HighTagContinueBitmask Tag = 0x80 // 10000000b + // HighTagValueBitmask obtains the tag value from a high-tag byte sequence byte + HighTagValueBitmask Tag = 0x7f // 01111111b +) + +const ( + // LengthLongFormBitmask is the mask to apply to the length byte to see if a long-form byte sequence is used + LengthLongFormBitmask = 0x80 + // LengthValueBitmask is the mask to apply to the length byte to get the number of bytes in the long-form byte sequence + LengthValueBitmask = 0x7f + + // LengthIndefinite is returned from readLength to indicate an indefinite length + LengthIndefinite = -1 +) + +var tagMap = map[Tag]string{ + TagEOC: "EOC (End-of-Content)", + TagBoolean: "Boolean", + TagInteger: "Integer", + TagBitString: "Bit String", + TagOctetString: "Octet String", + TagNULL: "NULL", + TagObjectIdentifier: "Object Identifier", + TagObjectDescriptor: "Object Descriptor", + TagExternal: "External", + TagRealFloat: "Real (float)", + TagEnumerated: "Enumerated", + TagEmbeddedPDV: "Embedded PDV", + TagUTF8String: "UTF8 String", + TagRelativeOID: "Relative-OID", + TagSequence: "Sequence and Sequence of", + TagSet: "Set and Set OF", + TagNumericString: "Numeric String", + TagPrintableString: "Printable String", + TagT61String: "T61 String", + TagVideotexString: "Videotex String", + TagIA5String: "IA5 String", + TagUTCTime: "UTC Time", + TagGeneralizedTime: "Generalized Time", + TagGraphicString: "Graphic String", + TagVisibleString: "Visible String", + TagGeneralString: "General String", + TagUniversalString: "Universal String", + TagCharacterString: "Character String", + TagBMPString: "BMP String", +} + +type Class uint8 + +const ( + ClassUniversal Class = 0 // 00xxxxxxb + ClassApplication Class = 64 // 01xxxxxxb + ClassContext Class = 128 // 10xxxxxxb + ClassPrivate Class = 192 // 11xxxxxxb + ClassBitmask Class = 192 // 11xxxxxxb +) + +var ClassMap = map[Class]string{ + ClassUniversal: "Universal", + ClassApplication: "Application", + ClassContext: "Context", + ClassPrivate: "Private", +} + +type Type uint8 + +const ( + TypePrimitive Type = 0 // xx0xxxxxb + TypeConstructed Type = 32 // xx1xxxxxb + TypeBitmask Type = 32 // xx1xxxxxb +) + +var TypeMap = map[Type]string{ + TypePrimitive: "Primitive", + TypeConstructed: "Constructed", +} + +var Debug bool = false + +func PrintBytes(out io.Writer, buf []byte, indent string) { + data_lines := make([]string, (len(buf)/30)+1) + num_lines := make([]string, (len(buf)/30)+1) + + for i, b := range buf { + data_lines[i/30] += fmt.Sprintf("%02x ", b) + num_lines[i/30] += fmt.Sprintf("%02d ", (i+1)%100) + } + + for i := 0; i < len(data_lines); i++ { + out.Write([]byte(indent + data_lines[i] + "\n")) + out.Write([]byte(indent + num_lines[i] + "\n\n")) + } +} + +func PrintPacket(p *Packet) { + printPacket(os.Stdout, p, 0, false) +} + +func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) { + indent_str := "" + + for len(indent_str) != indent { + indent_str += " " + } + + class_str := ClassMap[p.ClassType] + + tagtype_str := TypeMap[p.TagType] + + tag_str := fmt.Sprintf("0x%02X", p.Tag) + + if p.ClassType == ClassUniversal { + tag_str = tagMap[p.Tag] + } + + value := fmt.Sprint(p.Value) + description := "" + + if p.Description != "" { + description = p.Description + ": " + } + + fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indent_str, description, class_str, tagtype_str, tag_str, p.Data.Len(), value) + + if printBytes { + PrintBytes(out, p.Bytes(), indent_str) + } + + for _, child := range p.Children { + printPacket(out, child, indent+1, printBytes) + } +} + +// ReadPacket reads a single Packet from the reader +func ReadPacket(reader io.Reader) (*Packet, error) { + p, _, err := readPacket(reader) + if err != nil { + return nil, err + } + return p, nil +} + +func DecodeString(data []byte) string { + return string(data) +} + +func parseInt64(bytes []byte) (ret int64, err error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = fmt.Errorf("integer too large") + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +func encodeInteger(i int64) []byte { + n := int64Length(i) + out := make([]byte, n) + + var j int + for ; n > 0; n-- { + out[j] = (byte(i >> uint((n-1)*8))) + j++ + } + + return out +} + +func int64Length(i int64) (numBytes int) { + numBytes = 1 + + for i > 127 { + numBytes++ + i >>= 8 + } + + for i < -128 { + numBytes++ + i >>= 8 + } + + return +} + +// DecodePacket decodes the given bytes into a single Packet +// If a decode error is encountered, nil is returned. +func DecodePacket(data []byte) *Packet { + p, _, _ := readPacket(bytes.NewBuffer(data)) + + return p +} + +// DecodePacketErr decodes the given bytes into a single Packet +// If a decode error is encountered, nil is returned +func DecodePacketErr(data []byte) (*Packet, error) { + p, _, err := readPacket(bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + return p, nil +} + +// readPacket reads a single Packet from the reader, returning the number of bytes read +func readPacket(reader io.Reader) (*Packet, int, error) { + identifier, length, read, err := readHeader(reader) + if err != nil { + return nil, read, err + } + + p := &Packet{ + Identifier: identifier, + } + + p.Data = new(bytes.Buffer) + p.Children = make([]*Packet, 0, 2) + p.Value = nil + + if p.TagType == TypeConstructed { + // TODO: if universal, ensure tag type is allowed to be constructed + + // Track how much content we've read + contentRead := 0 + for { + if length != LengthIndefinite { + // End if we've read what we've been told to + if contentRead == length { + break + } + // Detect if a packet boundary didn't fall on the expected length + if contentRead > length { + return nil, read, fmt.Errorf("expected to read %d bytes, read %d", length, contentRead) + } + } + + // Read the next packet + child, r, err := readPacket(reader) + if err != nil { + return nil, read, err + } + contentRead += r + read += r + + // Test is this is the EOC marker for our packet + if isEOCPacket(child) { + if length == LengthIndefinite { + break + } + return nil, read, errors.New("eoc child not allowed with definite length") + } + + // Append and continue + p.AppendChild(child) + } + return p, read, nil + } + + if length == LengthIndefinite { + return nil, read, errors.New("indefinite length used with primitive type") + } + + // Read definite-length content + content := make([]byte, length, length) + if length > 0 { + _, err := io.ReadFull(reader, content) + if err != nil { + if err == io.EOF { + return nil, read, io.ErrUnexpectedEOF + } + return nil, read, err + } + read += length + } + + if p.ClassType == ClassUniversal { + p.Data.Write(content) + p.ByteValue = content + + switch p.Tag { + case TagEOC: + case TagBoolean: + val, _ := parseInt64(content) + + p.Value = val != 0 + case TagInteger: + p.Value, _ = parseInt64(content) + case TagBitString: + case TagOctetString: + // the actual string encoding is not known here + // (e.g. for LDAP content is already an UTF8-encoded + // string). Return the data without further processing + p.Value = DecodeString(content) + case TagNULL: + case TagObjectIdentifier: + case TagObjectDescriptor: + case TagExternal: + case TagRealFloat: + case TagEnumerated: + p.Value, _ = parseInt64(content) + case TagEmbeddedPDV: + case TagUTF8String: + p.Value = DecodeString(content) + case TagRelativeOID: + case TagSequence: + case TagSet: + case TagNumericString: + case TagPrintableString: + p.Value = DecodeString(content) + case TagT61String: + case TagVideotexString: + case TagIA5String: + case TagUTCTime: + case TagGeneralizedTime: + case TagGraphicString: + case TagVisibleString: + case TagGeneralString: + case TagUniversalString: + case TagCharacterString: + case TagBMPString: + } + } else { + p.Data.Write(content) + } + + return p, read, nil +} + +func (p *Packet) Bytes() []byte { + var out bytes.Buffer + + out.Write(encodeIdentifier(p.Identifier)) + out.Write(encodeLength(p.Data.Len())) + out.Write(p.Data.Bytes()) + + return out.Bytes() +} + +func (p *Packet) AppendChild(child *Packet) { + p.Data.Write(child.Bytes()) + p.Children = append(p.Children, child) +} + +func Encode(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet { + p := new(Packet) + + p.ClassType = ClassType + p.TagType = TagType + p.Tag = Tag + p.Data = new(bytes.Buffer) + + p.Children = make([]*Packet, 0, 2) + + p.Value = Value + p.Description = Description + + if Value != nil { + v := reflect.ValueOf(Value) + + if ClassType == ClassUniversal { + switch Tag { + case TagOctetString: + sv, ok := v.Interface().(string) + + if ok { + p.Data.Write([]byte(sv)) + } + } + } + } + + return p +} + +func NewSequence(Description string) *Packet { + return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, Description) +} + +func NewBoolean(ClassType Class, TagType Type, Tag Tag, Value bool, Description string) *Packet { + intValue := int64(0) + + if Value { + intValue = 1 + } + + p := Encode(ClassType, TagType, Tag, nil, Description) + + p.Value = Value + p.Data.Write(encodeInteger(intValue)) + + return p +} + +func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet { + p := Encode(ClassType, TagType, Tag, nil, Description) + + p.Value = Value + switch v := Value.(type) { + case int: + p.Data.Write(encodeInteger(int64(v))) + case uint: + p.Data.Write(encodeInteger(int64(v))) + case int64: + p.Data.Write(encodeInteger(v)) + case uint64: + // TODO : check range or add encodeUInt... + p.Data.Write(encodeInteger(int64(v))) + case int32: + p.Data.Write(encodeInteger(int64(v))) + case uint32: + p.Data.Write(encodeInteger(int64(v))) + case int16: + p.Data.Write(encodeInteger(int64(v))) + case uint16: + p.Data.Write(encodeInteger(int64(v))) + case int8: + p.Data.Write(encodeInteger(int64(v))) + case uint8: + p.Data.Write(encodeInteger(int64(v))) + default: + // TODO : add support for big.Int ? + panic(fmt.Sprintf("Invalid type %T, expected {u|}int{64|32|16|8}", v)) + } + + return p +} + +func NewString(ClassType Class, TagType Type, Tag Tag, Value, Description string) *Packet { + p := Encode(ClassType, TagType, Tag, nil, Description) + + p.Value = Value + p.Data.Write([]byte(Value)) + + return p +} diff --git a/src/vendor/gopkg.in/asn1-ber.v1/content_int.go b/src/vendor/gopkg.in/asn1-ber.v1/content_int.go new file mode 100644 index 000000000..1858b74b6 --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/content_int.go @@ -0,0 +1,25 @@ +package ber + +func encodeUnsignedInteger(i uint64) []byte { + n := uint64Length(i) + out := make([]byte, n) + + var j int + for ; n > 0; n-- { + out[j] = (byte(i >> uint((n-1)*8))) + j++ + } + + return out +} + +func uint64Length(i uint64) (numBytes int) { + numBytes = 1 + + for i > 255 { + numBytes++ + i >>= 8 + } + + return +} diff --git a/src/vendor/gopkg.in/asn1-ber.v1/header.go b/src/vendor/gopkg.in/asn1-ber.v1/header.go new file mode 100644 index 000000000..123744e9b --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/header.go @@ -0,0 +1,29 @@ +package ber + +import ( + "errors" + "io" +) + +func readHeader(reader io.Reader) (identifier Identifier, length int, read int, err error) { + if i, c, err := readIdentifier(reader); err != nil { + return Identifier{}, 0, read, err + } else { + identifier = i + read += c + } + + if l, c, err := readLength(reader); err != nil { + return Identifier{}, 0, read, err + } else { + length = l + read += c + } + + // Validate length type with identifier (x.600, 8.1.3.2.a) + if length == LengthIndefinite && identifier.TagType == TypePrimitive { + return Identifier{}, 0, read, errors.New("indefinite length used with primitive type") + } + + return identifier, length, read, nil +} diff --git a/src/vendor/gopkg.in/asn1-ber.v1/identifier.go b/src/vendor/gopkg.in/asn1-ber.v1/identifier.go new file mode 100644 index 000000000..f7672a844 --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/identifier.go @@ -0,0 +1,103 @@ +package ber + +import ( + "errors" + "fmt" + "io" + "math" +) + +func readIdentifier(reader io.Reader) (Identifier, int, error) { + identifier := Identifier{} + read := 0 + + // identifier byte + b, err := readByte(reader) + if err != nil { + if Debug { + fmt.Printf("error reading identifier byte: %v\n", err) + } + return Identifier{}, read, err + } + read++ + + identifier.ClassType = Class(b) & ClassBitmask + identifier.TagType = Type(b) & TypeBitmask + + if tag := Tag(b) & TagBitmask; tag != HighTag { + // short-form tag + identifier.Tag = tag + return identifier, read, nil + } + + // high-tag-number tag + tagBytes := 0 + for { + b, err := readByte(reader) + if err != nil { + if Debug { + fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err) + } + return Identifier{}, read, err + } + tagBytes++ + read++ + + // Lowest 7 bits get appended to the tag value (x.690, 8.1.2.4.2.b) + identifier.Tag <<= 7 + identifier.Tag |= Tag(b) & HighTagValueBitmask + + // First byte may not be all zeros (x.690, 8.1.2.4.2.c) + if tagBytes == 1 && identifier.Tag == 0 { + return Identifier{}, read, errors.New("invalid first high-tag-number tag byte") + } + // Overflow of int64 + // TODO: support big int tags? + if tagBytes > 9 { + return Identifier{}, read, errors.New("high-tag-number tag overflow") + } + + // Top bit of 0 means this is the last byte in the high-tag-number tag (x.690, 8.1.2.4.2.a) + if Tag(b)&HighTagContinueBitmask == 0 { + break + } + } + + return identifier, read, nil +} + +func encodeIdentifier(identifier Identifier) []byte { + b := []byte{0x0} + b[0] |= byte(identifier.ClassType) + b[0] |= byte(identifier.TagType) + + if identifier.Tag < HighTag { + // Short-form + b[0] |= byte(identifier.Tag) + } else { + // high-tag-number + b[0] |= byte(HighTag) + + tag := identifier.Tag + + highBit := uint(63) + for { + if tag&(1<= 0; i-- { + offset := uint(i) * 7 + mask := Tag(0x7f) << offset + tagByte := (tag & mask) >> offset + if i != 0 { + tagByte |= 0x80 + } + b = append(b, byte(tagByte)) + } + } + return b +} diff --git a/src/vendor/gopkg.in/asn1-ber.v1/length.go b/src/vendor/gopkg.in/asn1-ber.v1/length.go new file mode 100644 index 000000000..8e2ae4ddd --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/length.go @@ -0,0 +1,71 @@ +package ber + +import ( + "errors" + "fmt" + "io" +) + +func readLength(reader io.Reader) (length int, read int, err error) { + // length byte + b, err := readByte(reader) + if err != nil { + if Debug { + fmt.Printf("error reading length byte: %v\n", err) + } + return 0, 0, err + } + read++ + + switch { + case b == 0xFF: + // Invalid 0xFF (x.600, 8.1.3.5.c) + return 0, read, errors.New("invalid length byte 0xff") + + case b == LengthLongFormBitmask: + // Indefinite form, we have to decode packets until we encounter an EOC packet (x.600, 8.1.3.6) + length = LengthIndefinite + + case b&LengthLongFormBitmask == 0: + // Short definite form, extract the length from the bottom 7 bits (x.600, 8.1.3.4) + length = int(b) & LengthValueBitmask + + case b&LengthLongFormBitmask != 0: + // Long definite form, extract the number of length bytes to follow from the bottom 7 bits (x.600, 8.1.3.5.b) + lengthBytes := int(b) & LengthValueBitmask + // Protect against overflow + // TODO: support big int length? + if lengthBytes > 8 { + return 0, read, errors.New("long-form length overflow") + } + for i := 0; i < lengthBytes; i++ { + b, err = readByte(reader) + if err != nil { + if Debug { + fmt.Printf("error reading long-form length byte %d: %v\n", i, err) + } + return 0, read, err + } + read++ + + // x.600, 8.1.3.5 + length <<= 8 + length |= int(b) + } + + default: + return 0, read, errors.New("invalid length byte") + } + + return length, read, nil +} + +func encodeLength(length int) []byte { + length_bytes := encodeUnsignedInteger(uint64(length)) + if length > 127 || len(length_bytes) > 1 { + longFormBytes := []byte{(LengthLongFormBitmask | byte(len(length_bytes)))} + longFormBytes = append(longFormBytes, length_bytes...) + length_bytes = longFormBytes + } + return length_bytes +} diff --git a/src/vendor/gopkg.in/asn1-ber.v1/util.go b/src/vendor/gopkg.in/asn1-ber.v1/util.go new file mode 100644 index 000000000..3e56b66c8 --- /dev/null +++ b/src/vendor/gopkg.in/asn1-ber.v1/util.go @@ -0,0 +1,24 @@ +package ber + +import "io" + +func readByte(reader io.Reader) (byte, error) { + bytes := make([]byte, 1, 1) + _, err := io.ReadFull(reader, bytes) + if err != nil { + if err == io.EOF { + return 0, io.ErrUnexpectedEOF + } + return 0, err + } + return bytes[0], nil +} + +func isEOCPacket(p *Packet) bool { + return p != nil && + p.Tag == TagEOC && + p.ClassType == ClassUniversal && + p.TagType == TypePrimitive && + len(p.ByteValue) == 0 && + len(p.Children) == 0 +} diff --git a/src/vendor/gopkg.in/ldap.v2/LICENSE b/src/vendor/gopkg.in/ldap.v2/LICENSE new file mode 100644 index 000000000..6c0ed4b38 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com) +Portions copyright (c) 2015-2016 go-ldap Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/vendor/gopkg.in/ldap.v2/Makefile b/src/vendor/gopkg.in/ldap.v2/Makefile new file mode 100644 index 000000000..c1fc96657 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/Makefile @@ -0,0 +1,42 @@ +.PHONY: default install build test quicktest fmt vet lint + +default: fmt vet lint build quicktest + +install: + go get -t -v ./... + +build: + go build -v ./... + +test: + go test -v -cover ./... + +quicktest: + go test ./... + +# Capture output and force failure when there is non-empty output +fmt: + @echo gofmt -l . + @OUTPUT=`gofmt -l . 2>&1`; \ + if [ "$$OUTPUT" ]; then \ + echo "gofmt must be run on the following files:"; \ + echo "$$OUTPUT"; \ + exit 1; \ + fi + +# Only run on go1.5+ +vet: + go tool vet -atomic -bool -copylocks -nilfunc -printf -shadow -rangeloops -unreachable -unsafeptr -unusedresult . + +# https://github.com/golang/lint +# go get github.com/golang/lint/golint +# Capture output and force failure when there is non-empty output +# Only run on go1.5+ +lint: + @echo golint ./... + @OUTPUT=`golint ./... 2>&1`; \ + if [ "$$OUTPUT" ]; then \ + echo "golint errors:"; \ + echo "$$OUTPUT"; \ + exit 1; \ + fi diff --git a/src/vendor/gopkg.in/ldap.v2/README.md b/src/vendor/gopkg.in/ldap.v2/README.md new file mode 100644 index 000000000..a26ed2d82 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/README.md @@ -0,0 +1,53 @@ +[![GoDoc](https://godoc.org/gopkg.in/ldap.v2?status.svg)](https://godoc.org/gopkg.in/ldap.v2) +[![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap) + +# Basic LDAP v3 functionality for the GO programming language. + +## Install + +For the latest version use: + + go get gopkg.in/ldap.v2 + +Import the latest version with: + + import "gopkg.in/ldap.v2" + +## Required Libraries: + + - gopkg.in/asn1-ber.v1 + +## Features: + + - Connecting to LDAP server (non-TLS, TLS, STARTTLS) + - Binding to LDAP server + - Searching for entries + - Filter Compile / Decompile + - Paging Search Results + - Modify Requests / Responses + - Add Requests / Responses + - Delete Requests / Responses + +## Examples: + + - search + - modify + +## Contributing: + +Bug reports and pull requests are welcome! + +Before submitting a pull request, please make sure tests and verification scripts pass: +``` +make all +``` + +To set up a pre-push hook to run the tests and verify scripts before pushing: +``` +ln -s ../../.githooks/pre-push .git/hooks/pre-push +``` + +--- +The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) +The design is licensed under the Creative Commons 3.0 Attributions license. +Read this article for more details: http://blog.golang.org/gopher diff --git a/src/vendor/gopkg.in/ldap.v2/add.go b/src/vendor/gopkg.in/ldap.v2/add.go new file mode 100644 index 000000000..0e5f6cdba --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/add.go @@ -0,0 +1,113 @@ +// +// https://tools.ietf.org/html/rfc4511 +// +// AddRequest ::= [APPLICATION 8] SEQUENCE { +// entry LDAPDN, +// attributes AttributeList } +// +// AttributeList ::= SEQUENCE OF attribute Attribute + +package ldap + +import ( + "errors" + "log" + + "gopkg.in/asn1-ber.v1" +) + +// Attribute represents an LDAP attribute +type Attribute struct { + // Type is the name of the LDAP attribute + Type string + // Vals are the LDAP attribute values + Vals []string +} + +func (a *Attribute) encode() *ber.Packet { + seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute") + seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type")) + set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") + for _, value := range a.Vals { + set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) + } + seq.AppendChild(set) + return seq +} + +// AddRequest represents an LDAP AddRequest operation +type AddRequest struct { + // DN identifies the entry being added + DN string + // Attributes list the attributes of the new entry + Attributes []Attribute +} + +func (a AddRequest) encode() *ber.Packet { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request") + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN")) + attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") + for _, attribute := range a.Attributes { + attributes.AppendChild(attribute.encode()) + } + request.AppendChild(attributes) + return request +} + +// Attribute adds an attribute with the given type and values +func (a *AddRequest) Attribute(attrType string, attrVals []string) { + a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals}) +} + +// NewAddRequest returns an AddRequest for the given DN, with no attributes +func NewAddRequest(dn string) *AddRequest { + return &AddRequest{ + DN: dn, + } + +} + +// Add performs the given AddRequest +func (l *Conn) Add(addRequest *AddRequest) error { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + packet.AppendChild(addRequest.encode()) + + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return err + } + ber.PrintPacket(packet) + } + + if packet.Children[1].Tag == ApplicationAddResponse { + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode != 0 { + return NewError(resultCode, errors.New(resultDescription)) + } + } else { + log.Printf("Unexpected Response: %d", packet.Children[1].Tag) + } + + l.Debug.Printf("%d: returning", msgCtx.id) + return nil +} diff --git a/src/vendor/gopkg.in/ldap.v2/bind.go b/src/vendor/gopkg.in/ldap.v2/bind.go new file mode 100644 index 000000000..26b3cc727 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/bind.go @@ -0,0 +1,143 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ldap + +import ( + "errors" + + "gopkg.in/asn1-ber.v1" +) + +// SimpleBindRequest represents a username/password bind operation +type SimpleBindRequest struct { + // Username is the name of the Directory object that the client wishes to bind as + Username string + // Password is the credentials to bind with + Password string + // Controls are optional controls to send with the bind request + Controls []Control +} + +// SimpleBindResult contains the response from the server +type SimpleBindResult struct { + Controls []Control +} + +// NewSimpleBindRequest returns a bind request +func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest { + return &SimpleBindRequest{ + Username: username, + Password: password, + Controls: controls, + } +} + +func (bindRequest *SimpleBindRequest) encode() *ber.Packet { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name")) + request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password")) + + request.AppendChild(encodeControls(bindRequest.Controls)) + + return request +} + +// SimpleBind performs the simple bind operation defined in the given request +func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + encodedBindRequest := simpleBindRequest.encode() + packet.AppendChild(encodedBindRequest) + + if l.Debug { + ber.PrintPacket(packet) + } + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + + packetResponse, ok := <-msgCtx.responses + if !ok { + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return nil, err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return nil, err + } + ber.PrintPacket(packet) + } + + result := &SimpleBindResult{ + Controls: make([]Control, 0), + } + + if len(packet.Children) == 3 { + for _, child := range packet.Children[2].Children { + result.Controls = append(result.Controls, DecodeControl(child)) + } + } + + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode != 0 { + return result, NewError(resultCode, errors.New(resultDescription)) + } + + return result, nil +} + +// Bind performs a bind with the given username and password +func (l *Conn) Bind(username, password string) error { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name")) + bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password")) + packet.AppendChild(bindRequest) + + if l.Debug { + ber.PrintPacket(packet) + } + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + packetResponse, ok := <-msgCtx.responses + if !ok { + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return err + } + ber.PrintPacket(packet) + } + + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode != 0 { + return NewError(resultCode, errors.New(resultDescription)) + } + + return nil +} diff --git a/src/vendor/gopkg.in/ldap.v2/client.go b/src/vendor/gopkg.in/ldap.v2/client.go new file mode 100644 index 000000000..055b27b5f --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/client.go @@ -0,0 +1,27 @@ +package ldap + +import ( + "crypto/tls" + "time" +) + +// Client knows how to interact with an LDAP server +type Client interface { + Start() + StartTLS(config *tls.Config) error + Close() + SetTimeout(time.Duration) + + Bind(username, password string) error + SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) + + Add(addRequest *AddRequest) error + Del(delRequest *DelRequest) error + Modify(modifyRequest *ModifyRequest) error + + Compare(dn, attribute, value string) (bool, error) + PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) + + Search(searchRequest *SearchRequest) (*SearchResult, error) + SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) +} diff --git a/src/vendor/gopkg.in/ldap.v2/compare.go b/src/vendor/gopkg.in/ldap.v2/compare.go new file mode 100644 index 000000000..cc6d2af5e --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/compare.go @@ -0,0 +1,85 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// File contains Compare functionality +// +// https://tools.ietf.org/html/rfc4511 +// +// CompareRequest ::= [APPLICATION 14] SEQUENCE { +// entry LDAPDN, +// ava AttributeValueAssertion } +// +// AttributeValueAssertion ::= SEQUENCE { +// attributeDesc AttributeDescription, +// assertionValue AssertionValue } +// +// AttributeDescription ::= LDAPString +// -- Constrained to +// -- [RFC4512] +// +// AttributeValue ::= OCTET STRING +// + +package ldap + +import ( + "errors" + "fmt" + + "gopkg.in/asn1-ber.v1" +) + +// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise +// false with any error that occurs if any. +func (l *Conn) Compare(dn, attribute, value string) (bool, error) { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request") + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN")) + + ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion") + ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc")) + ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagOctetString, value, "AssertionValue")) + request.AppendChild(ava) + packet.AppendChild(request) + + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return false, err + } + defer l.finishMessage(msgCtx) + + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return false, err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return false, err + } + ber.PrintPacket(packet) + } + + if packet.Children[1].Tag == ApplicationCompareResponse { + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode == LDAPResultCompareTrue { + return true, nil + } else if resultCode == LDAPResultCompareFalse { + return false, nil + } else { + return false, NewError(resultCode, errors.New(resultDescription)) + } + } + return false, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag) +} diff --git a/src/vendor/gopkg.in/ldap.v2/conn.go b/src/vendor/gopkg.in/ldap.v2/conn.go new file mode 100644 index 000000000..b5bd99adb --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/conn.go @@ -0,0 +1,467 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ldap + +import ( + "crypto/tls" + "errors" + "fmt" + "log" + "net" + "sync" + "time" + + "gopkg.in/asn1-ber.v1" +) + +const ( + // MessageQuit causes the processMessages loop to exit + MessageQuit = 0 + // MessageRequest sends a request to the server + MessageRequest = 1 + // MessageResponse receives a response from the server + MessageResponse = 2 + // MessageFinish indicates the client considers a particular message ID to be finished + MessageFinish = 3 + // MessageTimeout indicates the client-specified timeout for a particular message ID has been reached + MessageTimeout = 4 +) + +// PacketResponse contains the packet or error encountered reading a response +type PacketResponse struct { + // Packet is the packet read from the server + Packet *ber.Packet + // Error is an error encountered while reading + Error error +} + +// ReadPacket returns the packet or an error +func (pr *PacketResponse) ReadPacket() (*ber.Packet, error) { + if (pr == nil) || (pr.Packet == nil && pr.Error == nil) { + return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve response")) + } + return pr.Packet, pr.Error +} + +type messageContext struct { + id int64 + // close(done) should only be called from finishMessage() + done chan struct{} + // close(responses) should only be called from processMessages(), and only sent to from sendResponse() + responses chan *PacketResponse +} + +// sendResponse should only be called within the processMessages() loop which +// is also responsible for closing the responses channel. +func (msgCtx *messageContext) sendResponse(packet *PacketResponse) { + select { + case msgCtx.responses <- packet: + // Successfully sent packet to message handler. + case <-msgCtx.done: + // The request handler is done and will not receive more + // packets. + } +} + +type messagePacket struct { + Op int + MessageID int64 + Packet *ber.Packet + Context *messageContext +} + +type sendMessageFlags uint + +const ( + startTLS sendMessageFlags = 1 << iota +) + +// Conn represents an LDAP Connection +type Conn struct { + conn net.Conn + isTLS bool + isClosing bool + closeErr error + isStartingTLS bool + Debug debugging + chanConfirm chan bool + messageContexts map[int64]*messageContext + chanMessage chan *messagePacket + chanMessageID chan int64 + wgSender sync.WaitGroup + wgClose sync.WaitGroup + once sync.Once + outstandingRequests uint + messageMutex sync.Mutex + requestTimeout time.Duration +} + +var _ Client = &Conn{} + +// DefaultTimeout is a package-level variable that sets the timeout value +// used for the Dial and DialTLS methods. +// +// WARNING: since this is a package-level variable, setting this value from +// multiple places will probably result in undesired behaviour. +var DefaultTimeout = 60 * time.Second + +// Dial connects to the given address on the given network using net.Dial +// and then returns a new Conn for the connection. +func Dial(network, addr string) (*Conn, error) { + c, err := net.DialTimeout(network, addr, DefaultTimeout) + if err != nil { + return nil, NewError(ErrorNetwork, err) + } + conn := NewConn(c, false) + conn.Start() + return conn, nil +} + +// DialTLS connects to the given address on the given network using tls.Dial +// and then returns a new Conn for the connection. +func DialTLS(network, addr string, config *tls.Config) (*Conn, error) { + dc, err := net.DialTimeout(network, addr, DefaultTimeout) + if err != nil { + return nil, NewError(ErrorNetwork, err) + } + c := tls.Client(dc, config) + err = c.Handshake() + if err != nil { + // Handshake error, close the established connection before we return an error + dc.Close() + return nil, NewError(ErrorNetwork, err) + } + conn := NewConn(c, true) + conn.Start() + return conn, nil +} + +// NewConn returns a new Conn using conn for network I/O. +func NewConn(conn net.Conn, isTLS bool) *Conn { + return &Conn{ + conn: conn, + chanConfirm: make(chan bool), + chanMessageID: make(chan int64), + chanMessage: make(chan *messagePacket, 10), + messageContexts: map[int64]*messageContext{}, + requestTimeout: 0, + isTLS: isTLS, + } +} + +// Start initializes goroutines to read responses and process messages +func (l *Conn) Start() { + go l.reader() + go l.processMessages() + l.wgClose.Add(1) +} + +// Close closes the connection. +func (l *Conn) Close() { + l.once.Do(func() { + l.isClosing = true + l.wgSender.Wait() + + l.Debug.Printf("Sending quit message and waiting for confirmation") + l.chanMessage <- &messagePacket{Op: MessageQuit} + <-l.chanConfirm + close(l.chanMessage) + + l.Debug.Printf("Closing network connection") + if err := l.conn.Close(); err != nil { + log.Print(err) + } + + l.wgClose.Done() + }) + l.wgClose.Wait() +} + +// SetTimeout sets the time after a request is sent that a MessageTimeout triggers +func (l *Conn) SetTimeout(timeout time.Duration) { + if timeout > 0 { + l.requestTimeout = timeout + } +} + +// Returns the next available messageID +func (l *Conn) nextMessageID() int64 { + if l.chanMessageID != nil { + if messageID, ok := <-l.chanMessageID; ok { + return messageID + } + } + return 0 +} + +// StartTLS sends the command to start a TLS session and then creates a new TLS Client +func (l *Conn) StartTLS(config *tls.Config) error { + if l.isTLS { + return NewError(ErrorNetwork, errors.New("ldap: already encrypted")) + } + + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS") + request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command")) + packet.AppendChild(request) + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessageWithFlags(packet, startTLS) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + l.Debug.Printf("%d: waiting for response", msgCtx.id) + + packetResponse, ok := <-msgCtx.responses + if !ok { + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + l.Close() + return err + } + ber.PrintPacket(packet) + } + + if resultCode, message := getLDAPResultCode(packet); resultCode == LDAPResultSuccess { + conn := tls.Client(l.conn, config) + + if err := conn.Handshake(); err != nil { + l.Close() + return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err)) + } + + l.isTLS = true + l.conn = conn + } else { + return NewError(resultCode, fmt.Errorf("ldap: cannot StartTLS (%s)", message)) + } + go l.reader() + + return nil +} + +func (l *Conn) sendMessage(packet *ber.Packet) (*messageContext, error) { + return l.sendMessageWithFlags(packet, 0) +} + +func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (*messageContext, error) { + if l.isClosing { + return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed")) + } + l.messageMutex.Lock() + l.Debug.Printf("flags&startTLS = %d", flags&startTLS) + if l.isStartingTLS { + l.messageMutex.Unlock() + return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase")) + } + if flags&startTLS != 0 { + if l.outstandingRequests != 0 { + l.messageMutex.Unlock() + return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests")) + } + l.isStartingTLS = true + } + l.outstandingRequests++ + + l.messageMutex.Unlock() + + responses := make(chan *PacketResponse) + messageID := packet.Children[0].Value.(int64) + message := &messagePacket{ + Op: MessageRequest, + MessageID: messageID, + Packet: packet, + Context: &messageContext{ + id: messageID, + done: make(chan struct{}), + responses: responses, + }, + } + l.sendProcessMessage(message) + return message.Context, nil +} + +func (l *Conn) finishMessage(msgCtx *messageContext) { + close(msgCtx.done) + + if l.isClosing { + return + } + + l.messageMutex.Lock() + l.outstandingRequests-- + if l.isStartingTLS { + l.isStartingTLS = false + } + l.messageMutex.Unlock() + + message := &messagePacket{ + Op: MessageFinish, + MessageID: msgCtx.id, + } + l.sendProcessMessage(message) +} + +func (l *Conn) sendProcessMessage(message *messagePacket) bool { + if l.isClosing { + return false + } + l.wgSender.Add(1) + l.chanMessage <- message + l.wgSender.Done() + return true +} + +func (l *Conn) processMessages() { + defer func() { + if err := recover(); err != nil { + log.Printf("ldap: recovered panic in processMessages: %v", err) + } + for messageID, msgCtx := range l.messageContexts { + // If we are closing due to an error, inform anyone who + // is waiting about the error. + if l.isClosing && l.closeErr != nil { + msgCtx.sendResponse(&PacketResponse{Error: l.closeErr}) + } + l.Debug.Printf("Closing channel for MessageID %d", messageID) + close(msgCtx.responses) + delete(l.messageContexts, messageID) + } + close(l.chanMessageID) + l.chanConfirm <- true + close(l.chanConfirm) + }() + + var messageID int64 = 1 + for { + select { + case l.chanMessageID <- messageID: + messageID++ + case message, ok := <-l.chanMessage: + if !ok { + l.Debug.Printf("Shutting down - message channel is closed") + return + } + switch message.Op { + case MessageQuit: + l.Debug.Printf("Shutting down - quit message received") + return + case MessageRequest: + // Add to message list and write to network + l.Debug.Printf("Sending message %d", message.MessageID) + + buf := message.Packet.Bytes() + _, err := l.conn.Write(buf) + if err != nil { + l.Debug.Printf("Error Sending Message: %s", err.Error()) + message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)}) + close(message.Context.responses) + break + } + + // Only add to messageContexts if we were able to + // successfully write the message. + l.messageContexts[message.MessageID] = message.Context + + // Add timeout if defined + if l.requestTimeout > 0 { + go func() { + defer func() { + if err := recover(); err != nil { + log.Printf("ldap: recovered panic in RequestTimeout: %v", err) + } + }() + time.Sleep(l.requestTimeout) + timeoutMessage := &messagePacket{ + Op: MessageTimeout, + MessageID: message.MessageID, + } + l.sendProcessMessage(timeoutMessage) + }() + } + case MessageResponse: + l.Debug.Printf("Receiving message %d", message.MessageID) + if msgCtx, ok := l.messageContexts[message.MessageID]; ok { + msgCtx.sendResponse(&PacketResponse{message.Packet, nil}) + } else { + log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing) + ber.PrintPacket(message.Packet) + } + case MessageTimeout: + // Handle the timeout by closing the channel + // All reads will return immediately + if msgCtx, ok := l.messageContexts[message.MessageID]; ok { + l.Debug.Printf("Receiving message timeout for %d", message.MessageID) + msgCtx.sendResponse(&PacketResponse{message.Packet, errors.New("ldap: connection timed out")}) + delete(l.messageContexts, message.MessageID) + close(msgCtx.responses) + } + case MessageFinish: + l.Debug.Printf("Finished message %d", message.MessageID) + if msgCtx, ok := l.messageContexts[message.MessageID]; ok { + delete(l.messageContexts, message.MessageID) + close(msgCtx.responses) + } + } + } + } +} + +func (l *Conn) reader() { + cleanstop := false + defer func() { + if err := recover(); err != nil { + log.Printf("ldap: recovered panic in reader: %v", err) + } + if !cleanstop { + l.Close() + } + }() + + for { + if cleanstop { + l.Debug.Printf("reader clean stopping (without closing the connection)") + return + } + packet, err := ber.ReadPacket(l.conn) + if err != nil { + // A read error is expected here if we are closing the connection... + if !l.isClosing { + l.closeErr = fmt.Errorf("unable to read LDAP response packet: %s", err) + l.Debug.Printf("reader error: %s", err.Error()) + } + return + } + addLDAPDescriptions(packet) + if len(packet.Children) == 0 { + l.Debug.Printf("Received bad ldap packet") + continue + } + l.messageMutex.Lock() + if l.isStartingTLS { + cleanstop = true + } + l.messageMutex.Unlock() + message := &messagePacket{ + Op: MessageResponse, + MessageID: packet.Children[0].Value.(int64), + Packet: packet, + } + if !l.sendProcessMessage(message) { + return + } + } +} diff --git a/src/vendor/gopkg.in/ldap.v2/control.go b/src/vendor/gopkg.in/ldap.v2/control.go new file mode 100644 index 000000000..342f325ca --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/control.go @@ -0,0 +1,420 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ldap + +import ( + "fmt" + "strconv" + + "gopkg.in/asn1-ber.v1" +) + +const ( + // ControlTypePaging - https://www.ietf.org/rfc/rfc2696.txt + ControlTypePaging = "1.2.840.113556.1.4.319" + // ControlTypeBeheraPasswordPolicy - https://tools.ietf.org/html/draft-behera-ldap-password-policy-10 + ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1" + // ControlTypeVChuPasswordMustChange - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00 + ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4" + // ControlTypeVChuPasswordWarning - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00 + ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5" + // ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296 + ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2" +) + +// ControlTypeMap maps controls to text descriptions +var ControlTypeMap = map[string]string{ + ControlTypePaging: "Paging", + ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft", + ControlTypeManageDsaIT: "Manage DSA IT", +} + +// Control defines an interface controls provide to encode and describe themselves +type Control interface { + // GetControlType returns the OID + GetControlType() string + // Encode returns the ber packet representation + Encode() *ber.Packet + // String returns a human-readable description + String() string +} + +// ControlString implements the Control interface for simple controls +type ControlString struct { + ControlType string + Criticality bool + ControlValue string +} + +// GetControlType returns the OID +func (c *ControlString) GetControlType() string { + return c.ControlType +} + +// Encode returns the ber packet representation +func (c *ControlString) Encode() *ber.Packet { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")")) + if c.Criticality { + packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) + } + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value")) + return packet +} + +// String returns a human-readable description +func (c *ControlString) String() string { + return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue) +} + +// ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt +type ControlPaging struct { + // PagingSize indicates the page size + PagingSize uint32 + // Cookie is an opaque value returned by the server to track a paging cursor + Cookie []byte +} + +// GetControlType returns the OID +func (c *ControlPaging) GetControlType() string { + return ControlTypePaging +} + +// Encode returns the ber packet representation +func (c *ControlPaging) Encode() *ber.Packet { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")")) + + p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)") + seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value") + seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.PagingSize), "Paging Size")) + cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie") + cookie.Value = c.Cookie + cookie.Data.Write(c.Cookie) + seq.AppendChild(cookie) + p2.AppendChild(seq) + + packet.AppendChild(p2) + return packet +} + +// String returns a human-readable description +func (c *ControlPaging) String() string { + return fmt.Sprintf( + "Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q", + ControlTypeMap[ControlTypePaging], + ControlTypePaging, + false, + c.PagingSize, + c.Cookie) +} + +// SetCookie stores the given cookie in the paging control +func (c *ControlPaging) SetCookie(cookie []byte) { + c.Cookie = cookie +} + +// ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10 +type ControlBeheraPasswordPolicy struct { + // Expire contains the number of seconds before a password will expire + Expire int64 + // Grace indicates the remaining number of times a user will be allowed to authenticate with an expired password + Grace int64 + // Error indicates the error code + Error int8 + // ErrorString is a human readable error + ErrorString string +} + +// GetControlType returns the OID +func (c *ControlBeheraPasswordPolicy) GetControlType() string { + return ControlTypeBeheraPasswordPolicy +} + +// Encode returns the ber packet representation +func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")")) + + return packet +} + +// String returns a human-readable description +func (c *ControlBeheraPasswordPolicy) String() string { + return fmt.Sprintf( + "Control Type: %s (%q) Criticality: %t Expire: %d Grace: %d Error: %d, ErrorString: %s", + ControlTypeMap[ControlTypeBeheraPasswordPolicy], + ControlTypeBeheraPasswordPolicy, + false, + c.Expire, + c.Grace, + c.Error, + c.ErrorString) +} + +// ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00 +type ControlVChuPasswordMustChange struct { + // MustChange indicates if the password is required to be changed + MustChange bool +} + +// GetControlType returns the OID +func (c *ControlVChuPasswordMustChange) GetControlType() string { + return ControlTypeVChuPasswordMustChange +} + +// Encode returns the ber packet representation +func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet { + return nil +} + +// String returns a human-readable description +func (c *ControlVChuPasswordMustChange) String() string { + return fmt.Sprintf( + "Control Type: %s (%q) Criticality: %t MustChange: %v", + ControlTypeMap[ControlTypeVChuPasswordMustChange], + ControlTypeVChuPasswordMustChange, + false, + c.MustChange) +} + +// ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00 +type ControlVChuPasswordWarning struct { + // Expire indicates the time in seconds until the password expires + Expire int64 +} + +// GetControlType returns the OID +func (c *ControlVChuPasswordWarning) GetControlType() string { + return ControlTypeVChuPasswordWarning +} + +// Encode returns the ber packet representation +func (c *ControlVChuPasswordWarning) Encode() *ber.Packet { + return nil +} + +// String returns a human-readable description +func (c *ControlVChuPasswordWarning) String() string { + return fmt.Sprintf( + "Control Type: %s (%q) Criticality: %t Expire: %b", + ControlTypeMap[ControlTypeVChuPasswordWarning], + ControlTypeVChuPasswordWarning, + false, + c.Expire) +} + +// ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296 +type ControlManageDsaIT struct { + // Criticality indicates if this control is required + Criticality bool +} + +// GetControlType returns the OID +func (c *ControlManageDsaIT) GetControlType() string { + return ControlTypeManageDsaIT +} + +// Encode returns the ber packet representation +func (c *ControlManageDsaIT) Encode() *ber.Packet { + //FIXME + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")")) + if c.Criticality { + packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) + } + return packet +} + +// String returns a human-readable description +func (c *ControlManageDsaIT) String() string { + return fmt.Sprintf( + "Control Type: %s (%q) Criticality: %t", + ControlTypeMap[ControlTypeManageDsaIT], + ControlTypeManageDsaIT, + c.Criticality) +} + +// NewControlManageDsaIT returns a ControlManageDsaIT control +func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT { + return &ControlManageDsaIT{Criticality: Criticality} +} + +// FindControl returns the first control of the given type in the list, or nil +func FindControl(controls []Control, controlType string) Control { + for _, c := range controls { + if c.GetControlType() == controlType { + return c + } + } + return nil +} + +// DecodeControl returns a control read from the given packet, or nil if no recognized control can be made +func DecodeControl(packet *ber.Packet) Control { + var ( + ControlType = "" + Criticality = false + value *ber.Packet + ) + + switch len(packet.Children) { + case 0: + // at least one child is required for control type + return nil + + case 1: + // just type, no criticality or value + packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" + ControlType = packet.Children[0].Value.(string) + + case 2: + packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" + ControlType = packet.Children[0].Value.(string) + + // Children[1] could be criticality or value (both are optional) + // duck-type on whether this is a boolean + if _, ok := packet.Children[1].Value.(bool); ok { + packet.Children[1].Description = "Criticality" + Criticality = packet.Children[1].Value.(bool) + } else { + packet.Children[1].Description = "Control Value" + value = packet.Children[1] + } + + case 3: + packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" + ControlType = packet.Children[0].Value.(string) + + packet.Children[1].Description = "Criticality" + Criticality = packet.Children[1].Value.(bool) + + packet.Children[2].Description = "Control Value" + value = packet.Children[2] + + default: + // more than 3 children is invalid + return nil + } + + switch ControlType { + case ControlTypeManageDsaIT: + return NewControlManageDsaIT(Criticality) + case ControlTypePaging: + value.Description += " (Paging)" + c := new(ControlPaging) + if value.Value != nil { + valueChildren := ber.DecodePacket(value.Data.Bytes()) + value.Data.Truncate(0) + value.Value = nil + value.AppendChild(valueChildren) + } + value = value.Children[0] + value.Description = "Search Control Value" + value.Children[0].Description = "Paging Size" + value.Children[1].Description = "Cookie" + c.PagingSize = uint32(value.Children[0].Value.(int64)) + c.Cookie = value.Children[1].Data.Bytes() + value.Children[1].Value = c.Cookie + return c + case ControlTypeBeheraPasswordPolicy: + value.Description += " (Password Policy - Behera)" + c := NewControlBeheraPasswordPolicy() + if value.Value != nil { + valueChildren := ber.DecodePacket(value.Data.Bytes()) + value.Data.Truncate(0) + value.Value = nil + value.AppendChild(valueChildren) + } + + sequence := value.Children[0] + + for _, child := range sequence.Children { + if child.Tag == 0 { + //Warning + warningPacket := child.Children[0] + packet := ber.DecodePacket(warningPacket.Data.Bytes()) + val, ok := packet.Value.(int64) + if ok { + if warningPacket.Tag == 0 { + //timeBeforeExpiration + c.Expire = val + warningPacket.Value = c.Expire + } else if warningPacket.Tag == 1 { + //graceAuthNsRemaining + c.Grace = val + warningPacket.Value = c.Grace + } + } + } else if child.Tag == 1 { + // Error + packet := ber.DecodePacket(child.Data.Bytes()) + val, ok := packet.Value.(int8) + if !ok { + // what to do? + val = -1 + } + c.Error = val + child.Value = c.Error + c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error] + } + } + return c + case ControlTypeVChuPasswordMustChange: + c := &ControlVChuPasswordMustChange{MustChange: true} + return c + case ControlTypeVChuPasswordWarning: + c := &ControlVChuPasswordWarning{Expire: -1} + expireStr := ber.DecodeString(value.Data.Bytes()) + + expire, err := strconv.ParseInt(expireStr, 10, 64) + if err != nil { + return nil + } + c.Expire = expire + value.Value = c.Expire + + return c + default: + c := new(ControlString) + c.ControlType = ControlType + c.Criticality = Criticality + if value != nil { + c.ControlValue = value.Value.(string) + } + return c + } +} + +// NewControlString returns a generic control +func NewControlString(controlType string, criticality bool, controlValue string) *ControlString { + return &ControlString{ + ControlType: controlType, + Criticality: criticality, + ControlValue: controlValue, + } +} + +// NewControlPaging returns a paging control +func NewControlPaging(pagingSize uint32) *ControlPaging { + return &ControlPaging{PagingSize: pagingSize} +} + +// NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy +func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy { + return &ControlBeheraPasswordPolicy{ + Expire: -1, + Grace: -1, + Error: -1, + } +} + +func encodeControls(controls []Control) *ber.Packet { + packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls") + for _, control := range controls { + packet.AppendChild(control.Encode()) + } + return packet +} diff --git a/src/vendor/gopkg.in/ldap.v2/debug.go b/src/vendor/gopkg.in/ldap.v2/debug.go new file mode 100644 index 000000000..b8a7ecbff --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/debug.go @@ -0,0 +1,24 @@ +package ldap + +import ( + "log" + + "gopkg.in/asn1-ber.v1" +) + +// debbuging type +// - has a Printf method to write the debug output +type debugging bool + +// write debug output +func (debug debugging) Printf(format string, args ...interface{}) { + if debug { + log.Printf(format, args...) + } +} + +func (debug debugging) PrintPacket(packet *ber.Packet) { + if debug { + ber.PrintPacket(packet) + } +} diff --git a/src/vendor/gopkg.in/ldap.v2/del.go b/src/vendor/gopkg.in/ldap.v2/del.go new file mode 100644 index 000000000..4fd63dc3f --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/del.go @@ -0,0 +1,84 @@ +// +// https://tools.ietf.org/html/rfc4511 +// +// DelRequest ::= [APPLICATION 10] LDAPDN + +package ldap + +import ( + "errors" + "log" + + "gopkg.in/asn1-ber.v1" +) + +// DelRequest implements an LDAP deletion request +type DelRequest struct { + // DN is the name of the directory entry to delete + DN string + // Controls hold optional controls to send with the request + Controls []Control +} + +func (d DelRequest) encode() *ber.Packet { + request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request") + request.Data.Write([]byte(d.DN)) + return request +} + +// NewDelRequest creates a delete request for the given DN and controls +func NewDelRequest(DN string, + Controls []Control) *DelRequest { + return &DelRequest{ + DN: DN, + Controls: Controls, + } +} + +// Del executes the given delete request +func (l *Conn) Del(delRequest *DelRequest) error { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + packet.AppendChild(delRequest.encode()) + if delRequest.Controls != nil { + packet.AppendChild(encodeControls(delRequest.Controls)) + } + + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return err + } + ber.PrintPacket(packet) + } + + if packet.Children[1].Tag == ApplicationDelResponse { + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode != 0 { + return NewError(resultCode, errors.New(resultDescription)) + } + } else { + log.Printf("Unexpected Response: %d", packet.Children[1].Tag) + } + + l.Debug.Printf("%d: returning", msgCtx.id) + return nil +} diff --git a/src/vendor/gopkg.in/ldap.v2/dn.go b/src/vendor/gopkg.in/ldap.v2/dn.go new file mode 100644 index 000000000..a8ece3142 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/dn.go @@ -0,0 +1,244 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// File contains DN parsing functionallity +// +// https://tools.ietf.org/html/rfc4514 +// +// distinguishedName = [ relativeDistinguishedName +// *( COMMA relativeDistinguishedName ) ] +// relativeDistinguishedName = attributeTypeAndValue +// *( PLUS attributeTypeAndValue ) +// attributeTypeAndValue = attributeType EQUALS attributeValue +// attributeType = descr / numericoid +// attributeValue = string / hexstring +// +// ; The following characters are to be escaped when they appear +// ; in the value to be encoded: ESC, one of , leading +// ; SHARP or SPACE, trailing SPACE, and NULL. +// string = [ ( leadchar / pair ) [ *( stringchar / pair ) +// ( trailchar / pair ) ] ] +// +// leadchar = LUTF1 / UTFMB +// LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A / +// %x3D / %x3F-5B / %x5D-7F +// +// trailchar = TUTF1 / UTFMB +// TUTF1 = %x01-1F / %x21 / %x23-2A / %x2D-3A / +// %x3D / %x3F-5B / %x5D-7F +// +// stringchar = SUTF1 / UTFMB +// SUTF1 = %x01-21 / %x23-2A / %x2D-3A / +// %x3D / %x3F-5B / %x5D-7F +// +// pair = ESC ( ESC / special / hexpair ) +// special = escaped / SPACE / SHARP / EQUALS +// escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE +// hexstring = SHARP 1*hexpair +// hexpair = HEX HEX +// +// where the productions , , , , +// , , , , , , , , +// , , and are defined in [RFC4512]. +// + +package ldap + +import ( + "bytes" + enchex "encoding/hex" + "errors" + "fmt" + "strings" + + ber "gopkg.in/asn1-ber.v1" +) + +// AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514 +type AttributeTypeAndValue struct { + // Type is the attribute type + Type string + // Value is the attribute value + Value string +} + +// RelativeDN represents a relativeDistinguishedName from https://tools.ietf.org/html/rfc4514 +type RelativeDN struct { + Attributes []*AttributeTypeAndValue +} + +// DN represents a distinguishedName from https://tools.ietf.org/html/rfc4514 +type DN struct { + RDNs []*RelativeDN +} + +// ParseDN returns a distinguishedName or an error +func ParseDN(str string) (*DN, error) { + dn := new(DN) + dn.RDNs = make([]*RelativeDN, 0) + rdn := new(RelativeDN) + rdn.Attributes = make([]*AttributeTypeAndValue, 0) + buffer := bytes.Buffer{} + attribute := new(AttributeTypeAndValue) + escaping := false + + unescapedTrailingSpaces := 0 + stringFromBuffer := func() string { + s := buffer.String() + s = s[0 : len(s)-unescapedTrailingSpaces] + buffer.Reset() + unescapedTrailingSpaces = 0 + return s + } + + for i := 0; i < len(str); i++ { + char := str[i] + if escaping { + unescapedTrailingSpaces = 0 + escaping = false + switch char { + case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\': + buffer.WriteByte(char) + continue + } + // Not a special character, assume hex encoded octet + if len(str) == i+1 { + return nil, errors.New("Got corrupted escaped character") + } + + dst := []byte{0} + n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2])) + if err != nil { + return nil, fmt.Errorf("Failed to decode escaped character: %s", err) + } else if n != 1 { + return nil, fmt.Errorf("Expected 1 byte when un-escaping, got %d", n) + } + buffer.WriteByte(dst[0]) + i++ + } else if char == '\\' { + unescapedTrailingSpaces = 0 + escaping = true + } else if char == '=' { + attribute.Type = stringFromBuffer() + // Special case: If the first character in the value is # the + // following data is BER encoded so we can just fast forward + // and decode. + if len(str) > i+1 && str[i+1] == '#' { + i += 2 + index := strings.IndexAny(str[i:], ",+") + data := str + if index > 0 { + data = str[i : i+index] + } else { + data = str[i:] + } + rawBER, err := enchex.DecodeString(data) + if err != nil { + return nil, fmt.Errorf("Failed to decode BER encoding: %s", err) + } + packet := ber.DecodePacket(rawBER) + buffer.WriteString(packet.Data.String()) + i += len(data) - 1 + } + } else if char == ',' || char == '+' { + // We're done with this RDN or value, push it + attribute.Value = stringFromBuffer() + rdn.Attributes = append(rdn.Attributes, attribute) + attribute = new(AttributeTypeAndValue) + if char == ',' { + dn.RDNs = append(dn.RDNs, rdn) + rdn = new(RelativeDN) + rdn.Attributes = make([]*AttributeTypeAndValue, 0) + } + } else if char == ' ' && buffer.Len() == 0 { + // ignore unescaped leading spaces + continue + } else { + if char == ' ' { + // Track unescaped spaces in case they are trailing and we need to remove them + unescapedTrailingSpaces++ + } else { + // Reset if we see a non-space char + unescapedTrailingSpaces = 0 + } + buffer.WriteByte(char) + } + } + if buffer.Len() > 0 { + if len(attribute.Type) == 0 { + return nil, errors.New("DN ended with incomplete type, value pair") + } + attribute.Value = stringFromBuffer() + rdn.Attributes = append(rdn.Attributes, attribute) + dn.RDNs = append(dn.RDNs, rdn) + } + return dn, nil +} + +// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch). +// Returns true if they have the same number of relative distinguished names +// and corresponding relative distinguished names (by position) are the same. +func (d *DN) Equal(other *DN) bool { + if len(d.RDNs) != len(other.RDNs) { + return false + } + for i := range d.RDNs { + if !d.RDNs[i].Equal(other.RDNs[i]) { + return false + } + } + return true +} + +// AncestorOf returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN. +// "ou=widgets,o=acme.com" is an ancestor of "ou=sprockets,ou=widgets,o=acme.com" +// "ou=widgets,o=acme.com" is not an ancestor of "ou=sprockets,ou=widgets,o=foo.com" +// "ou=widgets,o=acme.com" is not an ancestor of "ou=widgets,o=acme.com" +func (d *DN) AncestorOf(other *DN) bool { + if len(d.RDNs) >= len(other.RDNs) { + return false + } + // Take the last `len(d.RDNs)` RDNs from the other DN to compare against + otherRDNs := other.RDNs[len(other.RDNs)-len(d.RDNs):] + for i := range d.RDNs { + if !d.RDNs[i].Equal(otherRDNs[i]) { + return false + } + } + return true +} + +// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch). +// Relative distinguished names are the same if and only if they have the same number of AttributeTypeAndValues +// and each attribute of the first RDN is the same as the attribute of the second RDN with the same attribute type. +// The order of attributes is not significant. +// Case of attribute types is not significant. +func (r *RelativeDN) Equal(other *RelativeDN) bool { + if len(r.Attributes) != len(other.Attributes) { + return false + } + return r.hasAllAttributes(other.Attributes) && other.hasAllAttributes(r.Attributes) +} + +func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool { + for _, attr := range attrs { + found := false + for _, myattr := range r.Attributes { + if myattr.Equal(attr) { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +// Equal returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue +// Case of the attribute type is not significant +func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool { + return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value +} diff --git a/src/vendor/gopkg.in/ldap.v2/doc.go b/src/vendor/gopkg.in/ldap.v2/doc.go new file mode 100644 index 000000000..f20d39bc9 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/doc.go @@ -0,0 +1,4 @@ +/* +Package ldap provides basic LDAP v3 functionality. +*/ +package ldap diff --git a/src/vendor/gopkg.in/ldap.v2/error.go b/src/vendor/gopkg.in/ldap.v2/error.go new file mode 100644 index 000000000..ff697873d --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/error.go @@ -0,0 +1,148 @@ +package ldap + +import ( + "fmt" + + "gopkg.in/asn1-ber.v1" +) + +// LDAP Result Codes +const ( + LDAPResultSuccess = 0 + LDAPResultOperationsError = 1 + LDAPResultProtocolError = 2 + LDAPResultTimeLimitExceeded = 3 + LDAPResultSizeLimitExceeded = 4 + LDAPResultCompareFalse = 5 + LDAPResultCompareTrue = 6 + LDAPResultAuthMethodNotSupported = 7 + LDAPResultStrongAuthRequired = 8 + LDAPResultReferral = 10 + LDAPResultAdminLimitExceeded = 11 + LDAPResultUnavailableCriticalExtension = 12 + LDAPResultConfidentialityRequired = 13 + LDAPResultSaslBindInProgress = 14 + LDAPResultNoSuchAttribute = 16 + LDAPResultUndefinedAttributeType = 17 + LDAPResultInappropriateMatching = 18 + LDAPResultConstraintViolation = 19 + LDAPResultAttributeOrValueExists = 20 + LDAPResultInvalidAttributeSyntax = 21 + LDAPResultNoSuchObject = 32 + LDAPResultAliasProblem = 33 + LDAPResultInvalidDNSyntax = 34 + LDAPResultAliasDereferencingProblem = 36 + LDAPResultInappropriateAuthentication = 48 + LDAPResultInvalidCredentials = 49 + LDAPResultInsufficientAccessRights = 50 + LDAPResultBusy = 51 + LDAPResultUnavailable = 52 + LDAPResultUnwillingToPerform = 53 + LDAPResultLoopDetect = 54 + LDAPResultNamingViolation = 64 + LDAPResultObjectClassViolation = 65 + LDAPResultNotAllowedOnNonLeaf = 66 + LDAPResultNotAllowedOnRDN = 67 + LDAPResultEntryAlreadyExists = 68 + LDAPResultObjectClassModsProhibited = 69 + LDAPResultAffectsMultipleDSAs = 71 + LDAPResultOther = 80 + + ErrorNetwork = 200 + ErrorFilterCompile = 201 + ErrorFilterDecompile = 202 + ErrorDebugging = 203 + ErrorUnexpectedMessage = 204 + ErrorUnexpectedResponse = 205 +) + +// LDAPResultCodeMap contains string descriptions for LDAP error codes +var LDAPResultCodeMap = map[uint8]string{ + LDAPResultSuccess: "Success", + LDAPResultOperationsError: "Operations Error", + LDAPResultProtocolError: "Protocol Error", + LDAPResultTimeLimitExceeded: "Time Limit Exceeded", + LDAPResultSizeLimitExceeded: "Size Limit Exceeded", + LDAPResultCompareFalse: "Compare False", + LDAPResultCompareTrue: "Compare True", + LDAPResultAuthMethodNotSupported: "Auth Method Not Supported", + LDAPResultStrongAuthRequired: "Strong Auth Required", + LDAPResultReferral: "Referral", + LDAPResultAdminLimitExceeded: "Admin Limit Exceeded", + LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension", + LDAPResultConfidentialityRequired: "Confidentiality Required", + LDAPResultSaslBindInProgress: "Sasl Bind In Progress", + LDAPResultNoSuchAttribute: "No Such Attribute", + LDAPResultUndefinedAttributeType: "Undefined Attribute Type", + LDAPResultInappropriateMatching: "Inappropriate Matching", + LDAPResultConstraintViolation: "Constraint Violation", + LDAPResultAttributeOrValueExists: "Attribute Or Value Exists", + LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax", + LDAPResultNoSuchObject: "No Such Object", + LDAPResultAliasProblem: "Alias Problem", + LDAPResultInvalidDNSyntax: "Invalid DN Syntax", + LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem", + LDAPResultInappropriateAuthentication: "Inappropriate Authentication", + LDAPResultInvalidCredentials: "Invalid Credentials", + LDAPResultInsufficientAccessRights: "Insufficient Access Rights", + LDAPResultBusy: "Busy", + LDAPResultUnavailable: "Unavailable", + LDAPResultUnwillingToPerform: "Unwilling To Perform", + LDAPResultLoopDetect: "Loop Detect", + LDAPResultNamingViolation: "Naming Violation", + LDAPResultObjectClassViolation: "Object Class Violation", + LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf", + LDAPResultNotAllowedOnRDN: "Not Allowed On RDN", + LDAPResultEntryAlreadyExists: "Entry Already Exists", + LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited", + LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs", + LDAPResultOther: "Other", +} + +func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) { + if packet == nil { + return ErrorUnexpectedResponse, "Empty packet" + } else if len(packet.Children) >= 2 { + response := packet.Children[1] + if response == nil { + return ErrorUnexpectedResponse, "Empty response in packet" + } + if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 { + // Children[1].Children[2] is the diagnosticMessage which is guaranteed to exist as seen here: https://tools.ietf.org/html/rfc4511#section-4.1.9 + return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string) + } + } + + return ErrorNetwork, "Invalid packet format" +} + +// Error holds LDAP error information +type Error struct { + // Err is the underlying error + Err error + // ResultCode is the LDAP error code + ResultCode uint8 +} + +func (e *Error) Error() string { + return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error()) +} + +// NewError creates an LDAP error with the given code and underlying error +func NewError(resultCode uint8, err error) error { + return &Error{ResultCode: resultCode, Err: err} +} + +// IsErrorWithCode returns true if the given error is an LDAP error with the given result code +func IsErrorWithCode(err error, desiredResultCode uint8) bool { + if err == nil { + return false + } + + serverError, ok := err.(*Error) + if !ok { + return false + } + + return serverError.ResultCode == desiredResultCode +} diff --git a/src/vendor/gopkg.in/ldap.v2/filter.go b/src/vendor/gopkg.in/ldap.v2/filter.go new file mode 100644 index 000000000..7eae310f1 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/filter.go @@ -0,0 +1,466 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ldap + +import ( + "bytes" + hexpac "encoding/hex" + "errors" + "fmt" + "strings" + "unicode/utf8" + + "gopkg.in/asn1-ber.v1" +) + +// Filter choices +const ( + FilterAnd = 0 + FilterOr = 1 + FilterNot = 2 + FilterEqualityMatch = 3 + FilterSubstrings = 4 + FilterGreaterOrEqual = 5 + FilterLessOrEqual = 6 + FilterPresent = 7 + FilterApproxMatch = 8 + FilterExtensibleMatch = 9 +) + +// FilterMap contains human readable descriptions of Filter choices +var FilterMap = map[uint64]string{ + FilterAnd: "And", + FilterOr: "Or", + FilterNot: "Not", + FilterEqualityMatch: "Equality Match", + FilterSubstrings: "Substrings", + FilterGreaterOrEqual: "Greater Or Equal", + FilterLessOrEqual: "Less Or Equal", + FilterPresent: "Present", + FilterApproxMatch: "Approx Match", + FilterExtensibleMatch: "Extensible Match", +} + +// SubstringFilter options +const ( + FilterSubstringsInitial = 0 + FilterSubstringsAny = 1 + FilterSubstringsFinal = 2 +) + +// FilterSubstringsMap contains human readable descriptions of SubstringFilter choices +var FilterSubstringsMap = map[uint64]string{ + FilterSubstringsInitial: "Substrings Initial", + FilterSubstringsAny: "Substrings Any", + FilterSubstringsFinal: "Substrings Final", +} + +// MatchingRuleAssertion choices +const ( + MatchingRuleAssertionMatchingRule = 1 + MatchingRuleAssertionType = 2 + MatchingRuleAssertionMatchValue = 3 + MatchingRuleAssertionDNAttributes = 4 +) + +// MatchingRuleAssertionMap contains human readable descriptions of MatchingRuleAssertion choices +var MatchingRuleAssertionMap = map[uint64]string{ + MatchingRuleAssertionMatchingRule: "Matching Rule Assertion Matching Rule", + MatchingRuleAssertionType: "Matching Rule Assertion Type", + MatchingRuleAssertionMatchValue: "Matching Rule Assertion Match Value", + MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes", +} + +// CompileFilter converts a string representation of a filter into a BER-encoded packet +func CompileFilter(filter string) (*ber.Packet, error) { + if len(filter) == 0 || filter[0] != '(' { + return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('")) + } + packet, pos, err := compileFilter(filter, 1) + if err != nil { + return nil, err + } + if pos != len(filter) { + return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:]))) + } + return packet, nil +} + +// DecompileFilter converts a packet representation of a filter into a string representation +func DecompileFilter(packet *ber.Packet) (ret string, err error) { + defer func() { + if r := recover(); r != nil { + err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter")) + } + }() + ret = "(" + err = nil + childStr := "" + + switch packet.Tag { + case FilterAnd: + ret += "&" + for _, child := range packet.Children { + childStr, err = DecompileFilter(child) + if err != nil { + return + } + ret += childStr + } + case FilterOr: + ret += "|" + for _, child := range packet.Children { + childStr, err = DecompileFilter(child) + if err != nil { + return + } + ret += childStr + } + case FilterNot: + ret += "!" + childStr, err = DecompileFilter(packet.Children[0]) + if err != nil { + return + } + ret += childStr + + case FilterSubstrings: + ret += ber.DecodeString(packet.Children[0].Data.Bytes()) + ret += "=" + for i, child := range packet.Children[1].Children { + if i == 0 && child.Tag != FilterSubstringsInitial { + ret += "*" + } + ret += EscapeFilter(ber.DecodeString(child.Data.Bytes())) + if child.Tag != FilterSubstringsFinal { + ret += "*" + } + } + case FilterEqualityMatch: + ret += ber.DecodeString(packet.Children[0].Data.Bytes()) + ret += "=" + ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + case FilterGreaterOrEqual: + ret += ber.DecodeString(packet.Children[0].Data.Bytes()) + ret += ">=" + ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + case FilterLessOrEqual: + ret += ber.DecodeString(packet.Children[0].Data.Bytes()) + ret += "<=" + ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + case FilterPresent: + ret += ber.DecodeString(packet.Data.Bytes()) + ret += "=*" + case FilterApproxMatch: + ret += ber.DecodeString(packet.Children[0].Data.Bytes()) + ret += "~=" + ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())) + case FilterExtensibleMatch: + attr := "" + dnAttributes := false + matchingRule := "" + value := "" + + for _, child := range packet.Children { + switch child.Tag { + case MatchingRuleAssertionMatchingRule: + matchingRule = ber.DecodeString(child.Data.Bytes()) + case MatchingRuleAssertionType: + attr = ber.DecodeString(child.Data.Bytes()) + case MatchingRuleAssertionMatchValue: + value = ber.DecodeString(child.Data.Bytes()) + case MatchingRuleAssertionDNAttributes: + dnAttributes = child.Value.(bool) + } + } + + if len(attr) > 0 { + ret += attr + } + if dnAttributes { + ret += ":dn" + } + if len(matchingRule) > 0 { + ret += ":" + ret += matchingRule + } + ret += ":=" + ret += EscapeFilter(value) + } + + ret += ")" + return +} + +func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) { + for pos < len(filter) && filter[pos] == '(' { + child, newPos, err := compileFilter(filter, pos+1) + if err != nil { + return pos, err + } + pos = newPos + parent.AppendChild(child) + } + if pos == len(filter) { + return pos, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter")) + } + + return pos + 1, nil +} + +func compileFilter(filter string, pos int) (*ber.Packet, int, error) { + var ( + packet *ber.Packet + err error + ) + + defer func() { + if r := recover(); r != nil { + err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter")) + } + }() + newPos := pos + + currentRune, currentWidth := utf8.DecodeRuneInString(filter[newPos:]) + + switch currentRune { + case utf8.RuneError: + return nil, 0, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos)) + case '(': + packet, newPos, err = compileFilter(filter, pos+currentWidth) + newPos++ + return packet, newPos, err + case '&': + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd]) + newPos, err = compileFilterSet(filter, pos+currentWidth, packet) + return packet, newPos, err + case '|': + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr]) + newPos, err = compileFilterSet(filter, pos+currentWidth, packet) + return packet, newPos, err + case '!': + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot]) + var child *ber.Packet + child, newPos, err = compileFilter(filter, pos+currentWidth) + packet.AppendChild(child) + return packet, newPos, err + default: + const ( + stateReadingAttr = 0 + stateReadingExtensibleMatchingRule = 1 + stateReadingCondition = 2 + ) + + state := stateReadingAttr + + attribute := "" + extensibleDNAttributes := false + extensibleMatchingRule := "" + condition := "" + + for newPos < len(filter) { + remainingFilter := filter[newPos:] + currentRune, currentWidth = utf8.DecodeRuneInString(remainingFilter) + if currentRune == ')' { + break + } + if currentRune == utf8.RuneError { + return packet, newPos, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos)) + } + + switch state { + case stateReadingAttr: + switch { + // Extensible rule, with only DN-matching + case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:="): + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch]) + extensibleDNAttributes = true + state = stateReadingCondition + newPos += 5 + + // Extensible rule, with DN-matching and a matching OID + case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:"): + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch]) + extensibleDNAttributes = true + state = stateReadingExtensibleMatchingRule + newPos += 4 + + // Extensible rule, with attr only + case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="): + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch]) + state = stateReadingCondition + newPos += 2 + + // Extensible rule, with no DN attribute matching + case currentRune == ':': + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch]) + state = stateReadingExtensibleMatchingRule + newPos++ + + // Equality condition + case currentRune == '=': + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch]) + state = stateReadingCondition + newPos++ + + // Greater-than or equal + case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="): + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual]) + state = stateReadingCondition + newPos += 2 + + // Less-than or equal + case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="): + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual]) + state = stateReadingCondition + newPos += 2 + + // Approx + case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="): + packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch]) + state = stateReadingCondition + newPos += 2 + + // Still reading the attribute name + default: + attribute += fmt.Sprintf("%c", currentRune) + newPos += currentWidth + } + + case stateReadingExtensibleMatchingRule: + switch { + + // Matching rule OID is done + case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="): + state = stateReadingCondition + newPos += 2 + + // Still reading the matching rule oid + default: + extensibleMatchingRule += fmt.Sprintf("%c", currentRune) + newPos += currentWidth + } + + case stateReadingCondition: + // append to the condition + condition += fmt.Sprintf("%c", currentRune) + newPos += currentWidth + } + } + + if newPos == len(filter) { + err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter")) + return packet, newPos, err + } + if packet == nil { + err = NewError(ErrorFilterCompile, errors.New("ldap: error parsing filter")) + return packet, newPos, err + } + + switch { + case packet.Tag == FilterExtensibleMatch: + // MatchingRuleAssertion ::= SEQUENCE { + // matchingRule [1] MatchingRuleID OPTIONAL, + // type [2] AttributeDescription OPTIONAL, + // matchValue [3] AssertionValue, + // dnAttributes [4] BOOLEAN DEFAULT FALSE + // } + + // Include the matching rule oid, if specified + if len(extensibleMatchingRule) > 0 { + packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule, MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule])) + } + + // Include the attribute, if specified + if len(attribute) > 0 { + packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute, MatchingRuleAssertionMap[MatchingRuleAssertionType])) + } + + // Add the value (only required child) + encodedString, encodeErr := escapedStringToEncodedBytes(condition) + if encodeErr != nil { + return packet, newPos, encodeErr + } + packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue])) + + // Defaults to false, so only include in the sequence if true + if extensibleDNAttributes { + packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes])) + } + + case packet.Tag == FilterEqualityMatch && condition == "*": + packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute, FilterMap[FilterPresent]) + case packet.Tag == FilterEqualityMatch && strings.Contains(condition, "*"): + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) + packet.Tag = FilterSubstrings + packet.Description = FilterMap[uint64(packet.Tag)] + seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings") + parts := strings.Split(condition, "*") + for i, part := range parts { + if part == "" { + continue + } + var tag ber.Tag + switch i { + case 0: + tag = FilterSubstringsInitial + case len(parts) - 1: + tag = FilterSubstringsFinal + default: + tag = FilterSubstringsAny + } + encodedString, encodeErr := escapedStringToEncodedBytes(part) + if encodeErr != nil { + return packet, newPos, encodeErr + } + seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)])) + } + packet.AppendChild(seq) + default: + encodedString, encodeErr := escapedStringToEncodedBytes(condition) + if encodeErr != nil { + return packet, newPos, encodeErr + } + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) + packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition")) + } + + newPos += currentWidth + return packet, newPos, err + } +} + +// Convert from "ABC\xx\xx\xx" form to literal bytes for transport +func escapedStringToEncodedBytes(escapedString string) (string, error) { + var buffer bytes.Buffer + i := 0 + for i < len(escapedString) { + currentRune, currentWidth := utf8.DecodeRuneInString(escapedString[i:]) + if currentRune == utf8.RuneError { + return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", i)) + } + + // Check for escaped hex characters and convert them to their literal value for transport. + if currentRune == '\\' { + // http://tools.ietf.org/search/rfc4515 + // \ (%x5C) is not a valid character unless it is followed by two HEX characters due to not + // being a member of UTF1SUBSET. + if i+2 > len(escapedString) { + return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter")) + } + escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]) + if decodeErr != nil { + return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter")) + } + buffer.WriteByte(escByte[0]) + i += 2 // +1 from end of loop, so 3 total for \xx. + } else { + buffer.WriteRune(currentRune) + } + + i += currentWidth + } + return buffer.String(), nil +} diff --git a/src/vendor/gopkg.in/ldap.v2/ldap.go b/src/vendor/gopkg.in/ldap.v2/ldap.go new file mode 100644 index 000000000..d27e639d0 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/ldap.go @@ -0,0 +1,320 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ldap + +import ( + "errors" + "io/ioutil" + "os" + + ber "gopkg.in/asn1-ber.v1" +) + +// LDAP Application Codes +const ( + ApplicationBindRequest = 0 + ApplicationBindResponse = 1 + ApplicationUnbindRequest = 2 + ApplicationSearchRequest = 3 + ApplicationSearchResultEntry = 4 + ApplicationSearchResultDone = 5 + ApplicationModifyRequest = 6 + ApplicationModifyResponse = 7 + ApplicationAddRequest = 8 + ApplicationAddResponse = 9 + ApplicationDelRequest = 10 + ApplicationDelResponse = 11 + ApplicationModifyDNRequest = 12 + ApplicationModifyDNResponse = 13 + ApplicationCompareRequest = 14 + ApplicationCompareResponse = 15 + ApplicationAbandonRequest = 16 + ApplicationSearchResultReference = 19 + ApplicationExtendedRequest = 23 + ApplicationExtendedResponse = 24 +) + +// ApplicationMap contains human readable descriptions of LDAP Application Codes +var ApplicationMap = map[uint8]string{ + ApplicationBindRequest: "Bind Request", + ApplicationBindResponse: "Bind Response", + ApplicationUnbindRequest: "Unbind Request", + ApplicationSearchRequest: "Search Request", + ApplicationSearchResultEntry: "Search Result Entry", + ApplicationSearchResultDone: "Search Result Done", + ApplicationModifyRequest: "Modify Request", + ApplicationModifyResponse: "Modify Response", + ApplicationAddRequest: "Add Request", + ApplicationAddResponse: "Add Response", + ApplicationDelRequest: "Del Request", + ApplicationDelResponse: "Del Response", + ApplicationModifyDNRequest: "Modify DN Request", + ApplicationModifyDNResponse: "Modify DN Response", + ApplicationCompareRequest: "Compare Request", + ApplicationCompareResponse: "Compare Response", + ApplicationAbandonRequest: "Abandon Request", + ApplicationSearchResultReference: "Search Result Reference", + ApplicationExtendedRequest: "Extended Request", + ApplicationExtendedResponse: "Extended Response", +} + +// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10) +const ( + BeheraPasswordExpired = 0 + BeheraAccountLocked = 1 + BeheraChangeAfterReset = 2 + BeheraPasswordModNotAllowed = 3 + BeheraMustSupplyOldPassword = 4 + BeheraInsufficientPasswordQuality = 5 + BeheraPasswordTooShort = 6 + BeheraPasswordTooYoung = 7 + BeheraPasswordInHistory = 8 +) + +// BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes +var BeheraPasswordPolicyErrorMap = map[int8]string{ + BeheraPasswordExpired: "Password expired", + BeheraAccountLocked: "Account locked", + BeheraChangeAfterReset: "Password must be changed", + BeheraPasswordModNotAllowed: "Policy prevents password modification", + BeheraMustSupplyOldPassword: "Policy requires old password in order to change password", + BeheraInsufficientPasswordQuality: "Password fails quality checks", + BeheraPasswordTooShort: "Password is too short for policy", + BeheraPasswordTooYoung: "Password has been changed too recently", + BeheraPasswordInHistory: "New password is in list of old passwords", +} + +// Adds descriptions to an LDAP Response packet for debugging +func addLDAPDescriptions(packet *ber.Packet) (err error) { + defer func() { + if r := recover(); r != nil { + err = NewError(ErrorDebugging, errors.New("ldap: cannot process packet to add descriptions")) + } + }() + packet.Description = "LDAP Response" + packet.Children[0].Description = "Message ID" + + application := uint8(packet.Children[1].Tag) + packet.Children[1].Description = ApplicationMap[application] + + switch application { + case ApplicationBindRequest: + addRequestDescriptions(packet) + case ApplicationBindResponse: + addDefaultLDAPResponseDescriptions(packet) + case ApplicationUnbindRequest: + addRequestDescriptions(packet) + case ApplicationSearchRequest: + addRequestDescriptions(packet) + case ApplicationSearchResultEntry: + packet.Children[1].Children[0].Description = "Object Name" + packet.Children[1].Children[1].Description = "Attributes" + for _, child := range packet.Children[1].Children[1].Children { + child.Description = "Attribute" + child.Children[0].Description = "Attribute Name" + child.Children[1].Description = "Attribute Values" + for _, grandchild := range child.Children[1].Children { + grandchild.Description = "Attribute Value" + } + } + if len(packet.Children) == 3 { + addControlDescriptions(packet.Children[2]) + } + case ApplicationSearchResultDone: + addDefaultLDAPResponseDescriptions(packet) + case ApplicationModifyRequest: + addRequestDescriptions(packet) + case ApplicationModifyResponse: + case ApplicationAddRequest: + addRequestDescriptions(packet) + case ApplicationAddResponse: + case ApplicationDelRequest: + addRequestDescriptions(packet) + case ApplicationDelResponse: + case ApplicationModifyDNRequest: + addRequestDescriptions(packet) + case ApplicationModifyDNResponse: + case ApplicationCompareRequest: + addRequestDescriptions(packet) + case ApplicationCompareResponse: + case ApplicationAbandonRequest: + addRequestDescriptions(packet) + case ApplicationSearchResultReference: + case ApplicationExtendedRequest: + addRequestDescriptions(packet) + case ApplicationExtendedResponse: + } + + return nil +} + +func addControlDescriptions(packet *ber.Packet) { + packet.Description = "Controls" + for _, child := range packet.Children { + var value *ber.Packet + controlType := "" + child.Description = "Control" + switch len(child.Children) { + case 0: + // at least one child is required for control type + continue + + case 1: + // just type, no criticality or value + controlType = child.Children[0].Value.(string) + child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")" + + case 2: + controlType = child.Children[0].Value.(string) + child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")" + // Children[1] could be criticality or value (both are optional) + // duck-type on whether this is a boolean + if _, ok := child.Children[1].Value.(bool); ok { + child.Children[1].Description = "Criticality" + } else { + child.Children[1].Description = "Control Value" + value = child.Children[1] + } + + case 3: + // criticality and value present + controlType = child.Children[0].Value.(string) + child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")" + child.Children[1].Description = "Criticality" + child.Children[2].Description = "Control Value" + value = child.Children[2] + + default: + // more than 3 children is invalid + continue + } + if value == nil { + continue + } + switch controlType { + case ControlTypePaging: + value.Description += " (Paging)" + if value.Value != nil { + valueChildren := ber.DecodePacket(value.Data.Bytes()) + value.Data.Truncate(0) + value.Value = nil + valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes() + value.AppendChild(valueChildren) + } + value.Children[0].Description = "Real Search Control Value" + value.Children[0].Children[0].Description = "Paging Size" + value.Children[0].Children[1].Description = "Cookie" + + case ControlTypeBeheraPasswordPolicy: + value.Description += " (Password Policy - Behera Draft)" + if value.Value != nil { + valueChildren := ber.DecodePacket(value.Data.Bytes()) + value.Data.Truncate(0) + value.Value = nil + value.AppendChild(valueChildren) + } + sequence := value.Children[0] + for _, child := range sequence.Children { + if child.Tag == 0 { + //Warning + warningPacket := child.Children[0] + packet := ber.DecodePacket(warningPacket.Data.Bytes()) + val, ok := packet.Value.(int64) + if ok { + if warningPacket.Tag == 0 { + //timeBeforeExpiration + value.Description += " (TimeBeforeExpiration)" + warningPacket.Value = val + } else if warningPacket.Tag == 1 { + //graceAuthNsRemaining + value.Description += " (GraceAuthNsRemaining)" + warningPacket.Value = val + } + } + } else if child.Tag == 1 { + // Error + packet := ber.DecodePacket(child.Data.Bytes()) + val, ok := packet.Value.(int8) + if !ok { + val = -1 + } + child.Description = "Error" + child.Value = val + } + } + } + } +} + +func addRequestDescriptions(packet *ber.Packet) { + packet.Description = "LDAP Request" + packet.Children[0].Description = "Message ID" + packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)] + if len(packet.Children) == 3 { + addControlDescriptions(packet.Children[2]) + } +} + +func addDefaultLDAPResponseDescriptions(packet *ber.Packet) { + resultCode, _ := getLDAPResultCode(packet) + packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")" + packet.Children[1].Children[1].Description = "Matched DN" + packet.Children[1].Children[2].Description = "Error Message" + if len(packet.Children[1].Children) > 3 { + packet.Children[1].Children[3].Description = "Referral" + } + if len(packet.Children) == 3 { + addControlDescriptions(packet.Children[2]) + } +} + +// DebugBinaryFile reads and prints packets from the given filename +func DebugBinaryFile(fileName string) error { + file, err := ioutil.ReadFile(fileName) + if err != nil { + return NewError(ErrorDebugging, err) + } + ber.PrintBytes(os.Stdout, file, "") + packet := ber.DecodePacket(file) + addLDAPDescriptions(packet) + ber.PrintPacket(packet) + + return nil +} + +var hex = "0123456789abcdef" + +func mustEscape(c byte) bool { + return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0 +} + +// EscapeFilter escapes from the provided LDAP filter string the special +// characters in the set `()*\` and those out of the range 0 < c < 0x80, +// as defined in RFC4515. +func EscapeFilter(filter string) string { + escape := 0 + for i := 0; i < len(filter); i++ { + if mustEscape(filter[i]) { + escape++ + } + } + if escape == 0 { + return filter + } + buf := make([]byte, len(filter)+escape*2) + for i, j := 0, 0; i < len(filter); i++ { + c := filter[i] + if mustEscape(c) { + buf[j+0] = '\\' + buf[j+1] = hex[c>>4] + buf[j+2] = hex[c&0xf] + j += 3 + } else { + buf[j] = c + j++ + } + } + return string(buf) +} diff --git a/src/vendor/gopkg.in/ldap.v2/modify.go b/src/vendor/gopkg.in/ldap.v2/modify.go new file mode 100644 index 000000000..e4ab6cefc --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/modify.go @@ -0,0 +1,170 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// File contains Modify functionality +// +// https://tools.ietf.org/html/rfc4511 +// +// ModifyRequest ::= [APPLICATION 6] SEQUENCE { +// object LDAPDN, +// changes SEQUENCE OF change SEQUENCE { +// operation ENUMERATED { +// add (0), +// delete (1), +// replace (2), +// ... }, +// modification PartialAttribute } } +// +// PartialAttribute ::= SEQUENCE { +// type AttributeDescription, +// vals SET OF value AttributeValue } +// +// AttributeDescription ::= LDAPString +// -- Constrained to +// -- [RFC4512] +// +// AttributeValue ::= OCTET STRING +// + +package ldap + +import ( + "errors" + "log" + + "gopkg.in/asn1-ber.v1" +) + +// Change operation choices +const ( + AddAttribute = 0 + DeleteAttribute = 1 + ReplaceAttribute = 2 +) + +// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 +type PartialAttribute struct { + // Type is the type of the partial attribute + Type string + // Vals are the values of the partial attribute + Vals []string +} + +func (p *PartialAttribute) encode() *ber.Packet { + seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute") + seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type")) + set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") + for _, value := range p.Vals { + set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) + } + seq.AppendChild(set) + return seq +} + +// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 +type ModifyRequest struct { + // DN is the distinguishedName of the directory entry to modify + DN string + // AddAttributes contain the attributes to add + AddAttributes []PartialAttribute + // DeleteAttributes contain the attributes to delete + DeleteAttributes []PartialAttribute + // ReplaceAttributes contain the attributes to replace + ReplaceAttributes []PartialAttribute +} + +// Add inserts the given attribute to the list of attributes to add +func (m *ModifyRequest) Add(attrType string, attrVals []string) { + m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) +} + +// Delete inserts the given attribute to the list of attributes to delete +func (m *ModifyRequest) Delete(attrType string, attrVals []string) { + m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) +} + +// Replace inserts the given attribute to the list of attributes to replace +func (m *ModifyRequest) Replace(attrType string, attrVals []string) { + m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals}) +} + +func (m ModifyRequest) encode() *ber.Packet { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request") + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN")) + changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes") + for _, attribute := range m.AddAttributes { + change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") + change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation")) + change.AppendChild(attribute.encode()) + changes.AppendChild(change) + } + for _, attribute := range m.DeleteAttributes { + change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") + change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation")) + change.AppendChild(attribute.encode()) + changes.AppendChild(change) + } + for _, attribute := range m.ReplaceAttributes { + change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") + change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation")) + change.AppendChild(attribute.encode()) + changes.AppendChild(change) + } + request.AppendChild(changes) + return request +} + +// NewModifyRequest creates a modify request for the given DN +func NewModifyRequest( + dn string, +) *ModifyRequest { + return &ModifyRequest{ + DN: dn, + } +} + +// Modify performs the ModifyRequest +func (l *Conn) Modify(modifyRequest *ModifyRequest) error { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + packet.AppendChild(modifyRequest.encode()) + + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return err + } + defer l.finishMessage(msgCtx) + + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return err + } + ber.PrintPacket(packet) + } + + if packet.Children[1].Tag == ApplicationModifyResponse { + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode != 0 { + return NewError(resultCode, errors.New(resultDescription)) + } + } else { + log.Printf("Unexpected Response: %d", packet.Children[1].Tag) + } + + l.Debug.Printf("%d: returning", msgCtx.id) + return nil +} diff --git a/src/vendor/gopkg.in/ldap.v2/passwdmodify.go b/src/vendor/gopkg.in/ldap.v2/passwdmodify.go new file mode 100644 index 000000000..26110ccf4 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/passwdmodify.go @@ -0,0 +1,148 @@ +// This file contains the password modify extended operation as specified in rfc 3062 +// +// https://tools.ietf.org/html/rfc3062 +// + +package ldap + +import ( + "errors" + "fmt" + + "gopkg.in/asn1-ber.v1" +) + +const ( + passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1" +) + +// PasswordModifyRequest implements the Password Modify Extended Operation as defined in https://www.ietf.org/rfc/rfc3062.txt +type PasswordModifyRequest struct { + // UserIdentity is an optional string representation of the user associated with the request. + // This string may or may not be an LDAPDN [RFC2253]. + // If no UserIdentity field is present, the request acts up upon the password of the user currently associated with the LDAP session + UserIdentity string + // OldPassword, if present, contains the user's current password + OldPassword string + // NewPassword, if present, contains the desired password for this user + NewPassword string +} + +// PasswordModifyResult holds the server response to a PasswordModifyRequest +type PasswordModifyResult struct { + // GeneratedPassword holds a password generated by the server, if present + GeneratedPassword string +} + +func (r *PasswordModifyRequest) encode() (*ber.Packet, error) { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation") + request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID")) + extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request") + passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request") + if r.UserIdentity != "" { + passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity")) + } + if r.OldPassword != "" { + passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password")) + } + if r.NewPassword != "" { + passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password")) + } + + extendedRequestValue.AppendChild(passwordModifyRequestValue) + request.AppendChild(extendedRequestValue) + + return request, nil +} + +// NewPasswordModifyRequest creates a new PasswordModifyRequest +// +// According to the RFC 3602: +// userIdentity is a string representing the user associated with the request. +// This string may or may not be an LDAPDN (RFC 2253). +// If userIdentity is empty then the operation will act on the user associated +// with the session. +// +// oldPassword is the current user's password, it can be empty or it can be +// needed depending on the session user access rights (usually an administrator +// can change a user's password without knowing the current one) and the +// password policy (see pwdSafeModify password policy's attribute) +// +// newPassword is the desired user's password. If empty the server can return +// an error or generate a new password that will be available in the +// PasswordModifyResult.GeneratedPassword +// +func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest { + return &PasswordModifyRequest{ + UserIdentity: userIdentity, + OldPassword: oldPassword, + NewPassword: newPassword, + } +} + +// PasswordModify performs the modification request +func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + + encodedPasswordModifyRequest, err := passwordModifyRequest.encode() + if err != nil { + return nil, err + } + packet.AppendChild(encodedPasswordModifyRequest) + + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + + result := &PasswordModifyResult{} + + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return nil, err + } + + if packet == nil { + return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message")) + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return nil, err + } + ber.PrintPacket(packet) + } + + if packet.Children[1].Tag == ApplicationExtendedResponse { + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode != 0 { + return nil, NewError(resultCode, errors.New(resultDescription)) + } + } else { + return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag)) + } + + extendedResponse := packet.Children[1] + for _, child := range extendedResponse.Children { + if child.Tag == 11 { + passwordModifyReponseValue := ber.DecodePacket(child.Data.Bytes()) + if len(passwordModifyReponseValue.Children) == 1 { + if passwordModifyReponseValue.Children[0].Tag == 0 { + result.GeneratedPassword = ber.DecodeString(passwordModifyReponseValue.Children[0].Data.Bytes()) + } + } + } + } + + return result, nil +} diff --git a/src/vendor/gopkg.in/ldap.v2/search.go b/src/vendor/gopkg.in/ldap.v2/search.go new file mode 100644 index 000000000..2a99894c9 --- /dev/null +++ b/src/vendor/gopkg.in/ldap.v2/search.go @@ -0,0 +1,450 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// File contains Search functionality +// +// https://tools.ietf.org/html/rfc4511 +// +// SearchRequest ::= [APPLICATION 3] SEQUENCE { +// baseObject LDAPDN, +// scope ENUMERATED { +// baseObject (0), +// singleLevel (1), +// wholeSubtree (2), +// ... }, +// derefAliases ENUMERATED { +// neverDerefAliases (0), +// derefInSearching (1), +// derefFindingBaseObj (2), +// derefAlways (3) }, +// sizeLimit INTEGER (0 .. maxInt), +// timeLimit INTEGER (0 .. maxInt), +// typesOnly BOOLEAN, +// filter Filter, +// attributes AttributeSelection } +// +// AttributeSelection ::= SEQUENCE OF selector LDAPString +// -- The LDAPString is constrained to +// -- in Section 4.5.1.8 +// +// Filter ::= CHOICE { +// and [0] SET SIZE (1..MAX) OF filter Filter, +// or [1] SET SIZE (1..MAX) OF filter Filter, +// not [2] Filter, +// equalityMatch [3] AttributeValueAssertion, +// substrings [4] SubstringFilter, +// greaterOrEqual [5] AttributeValueAssertion, +// lessOrEqual [6] AttributeValueAssertion, +// present [7] AttributeDescription, +// approxMatch [8] AttributeValueAssertion, +// extensibleMatch [9] MatchingRuleAssertion, +// ... } +// +// SubstringFilter ::= SEQUENCE { +// type AttributeDescription, +// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { +// initial [0] AssertionValue, -- can occur at most once +// any [1] AssertionValue, +// final [2] AssertionValue } -- can occur at most once +// } +// +// MatchingRuleAssertion ::= SEQUENCE { +// matchingRule [1] MatchingRuleId OPTIONAL, +// type [2] AttributeDescription OPTIONAL, +// matchValue [3] AssertionValue, +// dnAttributes [4] BOOLEAN DEFAULT FALSE } +// +// + +package ldap + +import ( + "errors" + "fmt" + "sort" + "strings" + + "gopkg.in/asn1-ber.v1" +) + +// scope choices +const ( + ScopeBaseObject = 0 + ScopeSingleLevel = 1 + ScopeWholeSubtree = 2 +) + +// ScopeMap contains human readable descriptions of scope choices +var ScopeMap = map[int]string{ + ScopeBaseObject: "Base Object", + ScopeSingleLevel: "Single Level", + ScopeWholeSubtree: "Whole Subtree", +} + +// derefAliases +const ( + NeverDerefAliases = 0 + DerefInSearching = 1 + DerefFindingBaseObj = 2 + DerefAlways = 3 +) + +// DerefMap contains human readable descriptions of derefAliases choices +var DerefMap = map[int]string{ + NeverDerefAliases: "NeverDerefAliases", + DerefInSearching: "DerefInSearching", + DerefFindingBaseObj: "DerefFindingBaseObj", + DerefAlways: "DerefAlways", +} + +// NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs. +// The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the +// same input map of attributes, the output entry will contain the same order of attributes +func NewEntry(dn string, attributes map[string][]string) *Entry { + var attributeNames []string + for attributeName := range attributes { + attributeNames = append(attributeNames, attributeName) + } + sort.Strings(attributeNames) + + var encodedAttributes []*EntryAttribute + for _, attributeName := range attributeNames { + encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName])) + } + return &Entry{ + DN: dn, + Attributes: encodedAttributes, + } +} + +// Entry represents a single search result entry +type Entry struct { + // DN is the distinguished name of the entry + DN string + // Attributes are the returned attributes for the entry + Attributes []*EntryAttribute +} + +// GetAttributeValues returns the values for the named attribute, or an empty list +func (e *Entry) GetAttributeValues(attribute string) []string { + for _, attr := range e.Attributes { + if attr.Name == attribute { + return attr.Values + } + } + return []string{} +} + +// GetRawAttributeValues returns the byte values for the named attribute, or an empty list +func (e *Entry) GetRawAttributeValues(attribute string) [][]byte { + for _, attr := range e.Attributes { + if attr.Name == attribute { + return attr.ByteValues + } + } + return [][]byte{} +} + +// GetAttributeValue returns the first value for the named attribute, or "" +func (e *Entry) GetAttributeValue(attribute string) string { + values := e.GetAttributeValues(attribute) + if len(values) == 0 { + return "" + } + return values[0] +} + +// GetRawAttributeValue returns the first value for the named attribute, or an empty slice +func (e *Entry) GetRawAttributeValue(attribute string) []byte { + values := e.GetRawAttributeValues(attribute) + if len(values) == 0 { + return []byte{} + } + return values[0] +} + +// Print outputs a human-readable description +func (e *Entry) Print() { + fmt.Printf("DN: %s\n", e.DN) + for _, attr := range e.Attributes { + attr.Print() + } +} + +// PrettyPrint outputs a human-readable description indenting +func (e *Entry) PrettyPrint(indent int) { + fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN) + for _, attr := range e.Attributes { + attr.PrettyPrint(indent + 2) + } +} + +// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair +func NewEntryAttribute(name string, values []string) *EntryAttribute { + var bytes [][]byte + for _, value := range values { + bytes = append(bytes, []byte(value)) + } + return &EntryAttribute{ + Name: name, + Values: values, + ByteValues: bytes, + } +} + +// EntryAttribute holds a single attribute +type EntryAttribute struct { + // Name is the name of the attribute + Name string + // Values contain the string values of the attribute + Values []string + // ByteValues contain the raw values of the attribute + ByteValues [][]byte +} + +// Print outputs a human-readable description +func (e *EntryAttribute) Print() { + fmt.Printf("%s: %s\n", e.Name, e.Values) +} + +// PrettyPrint outputs a human-readable description with indenting +func (e *EntryAttribute) PrettyPrint(indent int) { + fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values) +} + +// SearchResult holds the server's response to a search request +type SearchResult struct { + // Entries are the returned entries + Entries []*Entry + // Referrals are the returned referrals + Referrals []string + // Controls are the returned controls + Controls []Control +} + +// Print outputs a human-readable description +func (s *SearchResult) Print() { + for _, entry := range s.Entries { + entry.Print() + } +} + +// PrettyPrint outputs a human-readable description with indenting +func (s *SearchResult) PrettyPrint(indent int) { + for _, entry := range s.Entries { + entry.PrettyPrint(indent) + } +} + +// SearchRequest represents a search request to send to the server +type SearchRequest struct { + BaseDN string + Scope int + DerefAliases int + SizeLimit int + TimeLimit int + TypesOnly bool + Filter string + Attributes []string + Controls []Control +} + +func (s *SearchRequest) encode() (*ber.Packet, error) { + request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request") + request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN")) + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope")) + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases")) + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit")) + request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit")) + request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only")) + // compile and encode filter + filterPacket, err := CompileFilter(s.Filter) + if err != nil { + return nil, err + } + request.AppendChild(filterPacket) + // encode attributes + attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") + for _, attribute := range s.Attributes { + attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) + } + request.AppendChild(attributesPacket) + return request, nil +} + +// NewSearchRequest creates a new search request +func NewSearchRequest( + BaseDN string, + Scope, DerefAliases, SizeLimit, TimeLimit int, + TypesOnly bool, + Filter string, + Attributes []string, + Controls []Control, +) *SearchRequest { + return &SearchRequest{ + BaseDN: BaseDN, + Scope: Scope, + DerefAliases: DerefAliases, + SizeLimit: SizeLimit, + TimeLimit: TimeLimit, + TypesOnly: TypesOnly, + Filter: Filter, + Attributes: Attributes, + Controls: Controls, + } +} + +// SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the +// search request. All paged LDAP query responses will be buffered and the final result will be returned atomically. +// The following four cases are possible given the arguments: +// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size +// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries +// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request +// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries +// A requested pagingSize of 0 is interpreted as no limit by LDAP servers. +func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) { + var pagingControl *ControlPaging + + control := FindControl(searchRequest.Controls, ControlTypePaging) + if control == nil { + pagingControl = NewControlPaging(pagingSize) + searchRequest.Controls = append(searchRequest.Controls, pagingControl) + } else { + castControl, ok := control.(*ControlPaging) + if !ok { + return nil, fmt.Errorf("Expected paging control to be of type *ControlPaging, got %v", control) + } + if castControl.PagingSize != pagingSize { + return nil, fmt.Errorf("Paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize) + } + pagingControl = castControl + } + + searchResult := new(SearchResult) + for { + result, err := l.Search(searchRequest) + l.Debug.Printf("Looking for Paging Control...") + if err != nil { + return searchResult, err + } + if result == nil { + return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received")) + } + + for _, entry := range result.Entries { + searchResult.Entries = append(searchResult.Entries, entry) + } + for _, referral := range result.Referrals { + searchResult.Referrals = append(searchResult.Referrals, referral) + } + for _, control := range result.Controls { + searchResult.Controls = append(searchResult.Controls, control) + } + + l.Debug.Printf("Looking for Paging Control...") + pagingResult := FindControl(result.Controls, ControlTypePaging) + if pagingResult == nil { + pagingControl = nil + l.Debug.Printf("Could not find paging control. Breaking...") + break + } + + cookie := pagingResult.(*ControlPaging).Cookie + if len(cookie) == 0 { + pagingControl = nil + l.Debug.Printf("Could not find cookie. Breaking...") + break + } + pagingControl.SetCookie(cookie) + } + + if pagingControl != nil { + l.Debug.Printf("Abandoning Paging...") + pagingControl.PagingSize = 0 + l.Search(searchRequest) + } + + return searchResult, nil +} + +// Search performs the given search request +func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) + // encode search request + encodedSearchRequest, err := searchRequest.encode() + if err != nil { + return nil, err + } + packet.AppendChild(encodedSearchRequest) + // encode search controls + if searchRequest.Controls != nil { + packet.AppendChild(encodeControls(searchRequest.Controls)) + } + + l.Debug.PrintPacket(packet) + + msgCtx, err := l.sendMessage(packet) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + + result := &SearchResult{ + Entries: make([]*Entry, 0), + Referrals: make([]string, 0), + Controls: make([]Control, 0)} + + foundSearchResultDone := false + for !foundSearchResultDone { + l.Debug.Printf("%d: waiting for response", msgCtx.id) + packetResponse, ok := <-msgCtx.responses + if !ok { + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return nil, err + } + + if l.Debug { + if err := addLDAPDescriptions(packet); err != nil { + return nil, err + } + ber.PrintPacket(packet) + } + + switch packet.Children[1].Tag { + case 4: + entry := new(Entry) + entry.DN = packet.Children[1].Children[0].Value.(string) + for _, child := range packet.Children[1].Children[1].Children { + attr := new(EntryAttribute) + attr.Name = child.Children[0].Value.(string) + for _, value := range child.Children[1].Children { + attr.Values = append(attr.Values, value.Value.(string)) + attr.ByteValues = append(attr.ByteValues, value.ByteValue) + } + entry.Attributes = append(entry.Attributes, attr) + } + result.Entries = append(result.Entries, entry) + case 5: + resultCode, resultDescription := getLDAPResultCode(packet) + if resultCode != 0 { + return result, NewError(resultCode, errors.New(resultDescription)) + } + if len(packet.Children) == 3 { + for _, child := range packet.Children[2].Children { + result.Controls = append(result.Controls, DecodeControl(child)) + } + } + foundSearchResultDone = true + case 19: + result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string)) + } + } + l.Debug.Printf("%d: returning", msgCtx.id) + return result, nil +} diff --git a/src/vendor/vendor.json b/src/vendor/vendor.json index 138aa7806..b508990f9 100644 --- a/src/vendor/vendor.json +++ b/src/vendor/vendor.json @@ -300,12 +300,6 @@ "revision": "3fb7a0e792edd47bf0cf1e919dfc14e2be412e15", "revisionTime": "2016-09-07T16:20:43Z" }, - { - "checksumSHA1": "JPm8dTnTbiYhi1kdn4JjHc4AZd0=", - "path": "github.com/mqu/openldap", - "revision": "91bfc2150f6c4991335de476c800caf26a990324", - "revisionTime": "2013-12-25T09:52:30Z" - }, { "checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=", "path": "golang.org/x/crypto/pbkdf2", @@ -321,6 +315,18 @@ { "path": "golang.org/x/sys/unix", "revision": "" + }, + { + "checksumSHA1": "wSu8owMAP7GixsYoSZ4CmKUVhnU=", + "path": "gopkg.in/asn1-ber.v1", + "revision": "4e86f4367175e39f69d9358a5f17b4dda270378d", + "revisionTime": "2015-09-24T05:17:56Z" + }, + { + "checksumSHA1": "itYnRitfdzJjy2mZlvJ+hCJZvtY=", + "path": "gopkg.in/ldap.v2", + "revision": "8168ee085ee43257585e50c6441aadf54ecb2c9f", + "revisionTime": "2016-12-01T20:47:33Z" } ], "rootPath": "github.com/vmware/harbor/src" From 4c213a87172f82a5666de2dff49fbc0f395422b2 Mon Sep 17 00:00:00 2001 From: yhua Date: Wed, 4 Jan 2017 14:10:13 +0800 Subject: [PATCH 3/6] remove openldap lib from Dockerfile and support build with offcial golang image(1.7.3) --- Makefile | 12 ++-------- docs/compile_guide.md | 37 +++++-------------------------- make/dev/jobservice/Dockerfile | 4 ---- make/dev/ui/Dockerfile | 6 ----- make/photon/ui/Dockerfile | 5 ++--- make/ubuntu/jobservice/Dockerfile | 5 +---- make/ubuntu/ui/Dockerfile | 7 +----- tools/Dockerfile.golang | 5 ----- 8 files changed, 11 insertions(+), 70 deletions(-) delete mode 100644 tools/Dockerfile.golang diff --git a/Makefile b/Makefile index d6c7d442a..974118b9c 100644 --- a/Makefile +++ b/Makefile @@ -5,14 +5,11 @@ # all: prepare env, compile binarys, build images and install images # prepare: prepare env # compile: compile ui and jobservice code -# compile_buildgolangimage: -# compile local building golang image -# forexample : make compile_buildgolangimage -e \ -# GOBUILDIMAGE=harborgo:1.6.2 +# # compile_golangimage: # compile from golang image # for example: make compile_golangimage -e GOBUILDIMAGE= \ -# harborgo:1.6.2 +# golang:1.7.3 # compile_ui, compile_jobservice: compile specific binary # # build: build Harbor docker images (defuault: build_photon) @@ -192,11 +189,6 @@ compile_jobservice: compile_normal: compile_ui compile_jobservice -compile_buildgolangimage: - @echo "compiling golang image for harbor ..." - @$(DOCKERBUILD) -t $(GOBUILDIMAGE) -f $(TOOLSPATH)/$(GOLANGDOCKERFILENAME) . - @echo "Done." - compile_golangimage: @echo "compiling binary for ui (golang image)..." @echo $(GOBASEPATH) diff --git a/docs/compile_guide.md b/docs/compile_guide.md index 39530e3d6..76a973647 100644 --- a/docs/compile_guide.md +++ b/docs/compile_guide.md @@ -24,26 +24,7 @@ golang* | 1.6.0 + $ git clone https://github.com/vmware/harbor ``` -## Step 3: Resolving dependencies of Go language -You can compile the source code by using a Golang dev image. In this case, you can skip this step. - -If you are building Harbor using your own Go compiling environment. You need to install LDAP packages manually. - -For PhotonOS: - - ```sh - $ tdnf install -y sed apr-util-ldap - ``` - -For Ubuntu: - - ```sh - $ apt-get update && apt-get install -y libldap2-dev - ``` - -For other platforms, please consult the relevant documentation of installing LDAP package. - -## Step 4: Building and installing Harbor +## Step 3: Building and installing Harbor ### Configuration @@ -58,18 +39,18 @@ Edit the file **make/harbor.cfg** and make necessary configuration changes such You can compile the code by one of the three approaches: -#### I. Create a Golang dev image, then build Harbor +#### I. Build with offical Golang image -* Build Golang dev image: +* Get offcial Golang image from docker hub: ```sh - $ make compile_buildgolangimage -e GOBUILDIMAGE=harborgo:1.7.3 + $ docker pull golang:1.7.3 ``` * Build, install and bring up Harbor: ```sh - $ make install -e GOBUILDIMAGE=harborgo:1.7.3 COMPILETAG=compile_golangimage + $ make install -e GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage ``` #### II. Compile code with your own Golang environment, then build Harbor @@ -142,7 +123,6 @@ Target | Description all | prepare env, compile binaries, build images and install images prepare | prepare env compile | compile ui and jobservice code -compile_golangimage | compile local golang image compile_ui | compile ui binary compile_jobservice | compile jobservice binary build | build Harbor docker images (default: using build_photon) @@ -163,13 +143,6 @@ cleanpackage | remove online/offline install package #### EXAMPLE: -#### Build a golang dev image (for building Harbor): - - ```sh - $ make compile_golangimage -e GOBUILDIMAGE= [$YOURIMAGE] - - ``` - #### Build Harbor images based on Ubuntu ```sh diff --git a/make/dev/jobservice/Dockerfile b/make/dev/jobservice/Dockerfile index 8cd8467e1..87bc159fb 100644 --- a/make/dev/jobservice/Dockerfile +++ b/make/dev/jobservice/Dockerfile @@ -2,10 +2,6 @@ FROM golang:1.7.3 MAINTAINER jiangd@vmware.com -RUN apt-get update \ - && apt-get install -y libldap2-dev \ - && rm -r /var/lib/apt/lists/* - COPY . /go/src/github.com/vmware/harbor WORKDIR /go/src/github.com/vmware/harbor/src/jobservice diff --git a/make/dev/ui/Dockerfile b/make/dev/ui/Dockerfile index 1f7fb2fb2..db2f0aa04 100644 --- a/make/dev/ui/Dockerfile +++ b/make/dev/ui/Dockerfile @@ -2,10 +2,6 @@ FROM golang:1.7.3 MAINTAINER jiangd@vmware.com -RUN apt-get update \ - && apt-get install -y libldap2-dev \ - && rm -r /var/lib/apt/lists/* - COPY src/. /go/src/github.com/vmware/harbor/src WORKDIR /go/src/github.com/vmware/harbor/src/ui @@ -21,8 +17,6 @@ COPY src/favicon.ico /go/bin/favicon.ico COPY make/jsminify.sh /tmp/jsminify.sh RUN chmod u+x /go/bin/harbor_ui \ - && sed -i 's/TLS_CACERT/#TLS_CACERT/g' /etc/ldap/ldap.conf \ - && sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \ && timestamp=`date '+%s'` \ && /tmp/jsminify.sh /go/bin/views/sections/script-include.htm /go/bin/static/resources/js/harbor.app.min.$timestamp.js /go/bin/ \ && sed -i "s/harbor\.app\.min\.js/harbor\.app\.min\.$timestamp\.js/g" /go/bin/views/sections/script-min-include.htm diff --git a/make/photon/ui/Dockerfile b/make/photon/ui/Dockerfile index 29722d4dc..e52164471 100644 --- a/make/photon/ui/Dockerfile +++ b/make/photon/ui/Dockerfile @@ -1,7 +1,7 @@ FROM library/photon:1.0 RUN mkdir /harbor/ -RUN tdnf install -y sed apr-util-ldap +RUN tdnf install -y sed COPY ./make/dev/ui/harbor_ui /harbor/ @@ -13,8 +13,7 @@ COPY ./make/jsminify.sh /tmp/jsminify.sh RUN chmod u+x /harbor/harbor_ui \ && timestamp=`date '+%s'` \ && /tmp/jsminify.sh /harbor/views/sections/script-include.htm /harbor/static/resources/js/harbor.app.min.$timestamp.js /harbor/ \ - && sed -i "s/harbor\.app\.min\.js/harbor\.app\.min\.$timestamp\.js/g" /harbor/views/sections/script-min-include.htm \ - && echo "TLS_REQCERT allow" >> /etc/openldap/ldap.conf + && sed -i "s/harbor\.app\.min\.js/harbor\.app\.min\.$timestamp\.js/g" /harbor/views/sections/script-min-include.htm WORKDIR /harbor/ ENTRYPOINT ["/harbor/harbor_ui"] diff --git a/make/ubuntu/jobservice/Dockerfile b/make/ubuntu/jobservice/Dockerfile index 0528c9ae4..29814703d 100644 --- a/make/ubuntu/jobservice/Dockerfile +++ b/make/ubuntu/jobservice/Dockerfile @@ -1,10 +1,7 @@ -FROM golang:1.7.3 +FROM library/ubuntu:14.04 MAINTAINER jiangd@vmware.com -RUN apt-get update && apt-get install -y libldap2-dev \ - && rm -r /var/lib/apt/lists/* - RUN mkdir /harbor/ COPY ./make/dev/jobservice/harbor_jobservice /harbor/ diff --git a/make/ubuntu/ui/Dockerfile b/make/ubuntu/ui/Dockerfile index d54749741..f44d4fdf9 100644 --- a/make/ubuntu/ui/Dockerfile +++ b/make/ubuntu/ui/Dockerfile @@ -1,10 +1,7 @@ -FROM golang:1.7.3 +FROM library/ubuntu:14.04 MAINTAINER jiangd@vmware.com -RUN apt-get update && apt-get install -y libldap2-dev \ - && rm -r /var/lib/apt/lists/* - ENV MYSQL_USR root \ MYSQL_PWD root \ REGISTRY_URL localhost:5000 @@ -18,8 +15,6 @@ COPY ./src/favicon.ico /harbor/favicon.ico COPY ./make/jsminify.sh /tmp/jsminify.sh RUN chmod u+x /harbor/harbor_ui \ - && sed -i 's/TLS_CACERT/#TLS_CACERT/g' /etc/ldap/ldap.conf \ - && sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \ && timestamp=`date '+%s'` \ && /tmp/jsminify.sh /harbor/views/sections/script-include.htm /harbor/static/resources/js/harbor.app.min.$timestamp.js /harbor/ \ && sed -i "s/harbor\.app\.min\.js/harbor\.app\.min\.$timestamp\.js/g" /harbor/views/sections/script-min-include.htm diff --git a/tools/Dockerfile.golang b/tools/Dockerfile.golang deleted file mode 100644 index ef4916388..000000000 --- a/tools/Dockerfile.golang +++ /dev/null @@ -1,5 +0,0 @@ -FROM golang:1.7.3 - -RUN apt-get update \ - && apt-get install -y libldap2-dev \ - && rm -r /var/lib/apt/lists/* From ce0a44010ddf02aebcb7078d89d1b57b6e547baa Mon Sep 17 00:00:00 2001 From: wemeya <1013939285@qq.com> Date: Fri, 6 Jan 2017 15:41:07 +0800 Subject: [PATCH 4/6] add test file for systeminfo.go (#1288) LGTM --- .travis.yml | 2 + src/ui/api/harborapi_test.go | 25 +++++++++++ src/ui/api/systeminfo_test.go | 64 ++++++++++++++++++++++++++++ tests/apitests/apilib/system_info.go | 32 ++++++++++++++ tests/ca.crt | 1 + 5 files changed, 124 insertions(+) create mode 100644 src/ui/api/systeminfo_test.go create mode 100644 tests/apitests/apilib/system_info.go create mode 100644 tests/ca.crt diff --git a/.travis.yml b/.travis.yml index e463cfc70..a80c95942 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,6 +73,8 @@ before_script: - sudo sqlite3 /registry.db < make/common/db/registry_sqlite.sql script: + - sudo mkdir -p /harbor_storage/ca_download + - sudo mv ./tests/ca.crt /harbor_storage/ca_download - sudo service mysql stop - sudo ./tests/testprepare.sh - docker-compose -f ./make/docker-compose.test.yml up -d diff --git a/src/ui/api/harborapi_test.go b/src/ui/api/harborapi_test.go index 749927438..4fd57a1c3 100644 --- a/src/ui/api/harborapi_test.go +++ b/src/ui/api/harborapi_test.go @@ -88,6 +88,8 @@ func init() { beego.Router("/api/policies/replication", &RepPolicyAPI{}, "get:List") beego.Router("/api/policies/replication", &RepPolicyAPI{}, "post:Post;delete:Delete") beego.Router("/api/policies/replication/:id([0-9]+)/enablement", &RepPolicyAPI{}, "put:UpdateEnablement") + beego.Router("/api/systeminfo/volumes", &SystemInfoAPI{}, "get:GetVolumeInfo") + beego.Router("/api/systeminfo/getcert", &SystemInfoAPI{}, "get:GetCert") _ = updateInitPassword(1, "Harbor12345") @@ -872,3 +874,26 @@ func updateInitPassword(userID int, password string) error { } return nil } + +//Get system volume info +func (a testapi) VolumeInfoGet(authInfo usrInfo) (int, apilib.SystemInfo, error) { + _sling := sling.New().Get(a.basePath) + path := "/api/systeminfo/volumes" + _sling = _sling.Path(path) + httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo) + var successPayLoad apilib.SystemInfo + if 200 == httpStatusCode && nil == err { + err = json.Unmarshal(body, &successPayLoad) + } + + return httpStatusCode, successPayLoad, err +} + +//Get system cert +func (a testapi) CertGet(authInfo usrInfo) (int, []byte, error) { + _sling := sling.New().Get(a.basePath) + path := "/api/systeminfo/getcert" + _sling = _sling.Path(path) + httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo) + return httpStatusCode, body, err +} diff --git a/src/ui/api/systeminfo_test.go b/src/ui/api/systeminfo_test.go new file mode 100644 index 000000000..933e47640 --- /dev/null +++ b/src/ui/api/systeminfo_test.go @@ -0,0 +1,64 @@ +package api + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetVolumeInfo(t *testing.T) { + fmt.Println("Testing Get Volume Info") + assert := assert.New(t) + apiTest := newHarborAPI() + + //case 1: get volume info without admin role + CommonAddUser() + code, _, err := apiTest.VolumeInfoGet(*testUser) + if err != nil { + t.Error("Error occured while get system volume info") + t.Log(err) + } else { + assert.Equal(403, code, "Get system volume info should be 403") + } + //case 2: get volume info with admin role + code, info, err := apiTest.VolumeInfoGet(*admin) + if err != nil { + t.Error("Error occured while get system volume info") + t.Log(err) + } else { + assert.Equal(200, code, "Get system volume info should be 200") + if info.HarborStorage.Total <= 0 { + assert.Equal(1, info.HarborStorage.Total, "Total storage of system should be larger than 0") + } + if info.HarborStorage.Free <= 0 { + assert.Equal(1, info.HarborStorage.Free, "Free storage of system should be larger than 0") + } + } + +} + +func TestGetCert(t *testing.T) { + fmt.Println("Testing Get Cert") + assert := assert.New(t) + apiTest := newHarborAPI() + + //case 1: get cert without admin role + code, _, err := apiTest.CertGet(*testUser) + if err != nil { + t.Error("Error occured while get system cert") + t.Log(err) + } else { + assert.Equal(403, code, "Get system cert should be 403") + } + //case 2: get cert with admin role + code, content, err := apiTest.CertGet(*admin) + if err != nil { + t.Error("Error occured while get system cert") + t.Log(err) + } else { + assert.Equal(200, code, "Get system cert should be 200") + assert.Equal("test for ca.crt.\n", string(content), "Get system cert content should be equal") + + } + CommonDelUser() +} diff --git a/tests/apitests/apilib/system_info.go b/tests/apitests/apilib/system_info.go new file mode 100644 index 000000000..927fa0354 --- /dev/null +++ b/tests/apitests/apilib/system_info.go @@ -0,0 +1,32 @@ +/* + * Harbor API + * + * These APIs provide services for manipulating Harbor project. + * + * OpenAPI spec version: 0.3.0 + * + * Generated by: https://github.com/swagger-api/swagger-codegen.git + * + * 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 apilib + +type SystemInfo struct { + HarborStorage Storage `json:"storage"` +} + +type Storage struct { + Total uint64 `json:"total"` + Free uint64 `json:"free"` +} diff --git a/tests/ca.crt b/tests/ca.crt new file mode 100644 index 000000000..ed71ba717 --- /dev/null +++ b/tests/ca.crt @@ -0,0 +1 @@ +test for ca.crt. From 3c7a09a8c46131f7b4e7dd68f989dfdc45f1709e Mon Sep 17 00:00:00 2001 From: Dmitry Lazurkin Date: Fri, 6 Jan 2017 21:53:29 +0300 Subject: [PATCH 5/6] Fix filtering top repositories (#783, #1281) --- src/common/dao/dao_test.go | 7 -- src/common/dao/repository.go | 16 ++- src/common/dao/repository_test.go | 158 ++++++++++++++++++++++++++++++ src/ui/api/repository.go | 7 +- 4 files changed, 177 insertions(+), 11 deletions(-) diff --git a/src/common/dao/dao_test.go b/src/common/dao/dao_test.go index 861bf3ee7..116ccbd72 100644 --- a/src/common/dao/dao_test.go +++ b/src/common/dao/dao_test.go @@ -980,13 +980,6 @@ func TestGetRecentLogs(t *testing.T) { } } -func TestGetTopRepos(t *testing.T) { - _, err := GetTopRepos(10) - if err != nil { - t.Fatalf("error occured in getting top repos, error: %v", err) - } -} - var targetID, policyID, policyID2, policyID3, jobID, jobID2, jobID3 int64 func TestAddRepTarget(t *testing.T) { diff --git a/src/common/dao/repository.go b/src/common/dao/repository.go index cda2f5cba..5d9353a0c 100644 --- a/src/common/dao/repository.go +++ b/src/common/dao/repository.go @@ -102,12 +102,22 @@ func GetRepositoryByProjectName(name string) ([]*models.RepoRecord, error) { } //GetTopRepos returns the most popular repositories -func GetTopRepos(count int) ([]models.TopRepo, error) { +func GetTopRepos(userID int, count int) ([]models.TopRepo, error) { topRepos := []models.TopRepo{} + sql := `select r.name, r.pull_count from repository r + inner join project p on r.project_id = p.project_id + where ( + p.deleted = 0 and ( + p.public = 1 or ( + ? <> ? and exists ( + select 1 from project_member pm + where pm.project_id = p.project_id and pm.user_id = ? + )))) + order by r.pull_count desc, r.name limit ?` repositories := []*models.RepoRecord{} - if _, err := GetOrmer().QueryTable(&models.RepoRecord{}). - OrderBy("-PullCount", "Name").Limit(count).All(&repositories); err != nil { + _, err := GetOrmer().Raw(sql, userID, NonExistUserID, userID, count).QueryRows(&repositories) + if err != nil { return topRepos, err } diff --git a/src/common/dao/repository_test.go b/src/common/dao/repository_test.go index 1cc251f60..e8efc7e6b 100644 --- a/src/common/dao/repository_test.go +++ b/src/common/dao/repository_test.go @@ -16,8 +16,11 @@ package dao import ( + "fmt" "testing" + "time" + "github.com/stretchr/testify/require" "github.com/vmware/harbor/src/common/models" ) @@ -160,6 +163,161 @@ func TestGetTotalOfUserRelevantRepositories(t *testing.T) { } } +func TestGetTopRepos(t *testing.T) { + var err error + require := require.New(t) + + require.NoError(GetOrmer().Begin()) + defer func() { + require.NoError(GetOrmer().Rollback()) + }() + + admin, err := GetUser(models.User{Username: "admin"}) + require.NoError(err) + + user := models.User{ + Username: "user", + Password: "user", + Email: "user@test.com", + } + userID, err := Register(user) + require.NoError(err) + user.UserID = int(userID) + + // + // public project with 1 repository + // non-public project with 2 repositories visible by admin + // non-public project with 1 repository visible by admin and user + // deleted public project with 1 repository + // + + project1 := models.Project{ + OwnerID: admin.UserID, + Name: "project1", + CreationTime: time.Now(), + OwnerName: admin.Username, + Public: 0, + } + project1.ProjectID, err = AddProject(project1) + require.NoError(err) + + project2 := models.Project{ + OwnerID: admin.UserID, + Name: "project2", + CreationTime: time.Now(), + OwnerName: admin.Username, + Public: 0, + } + project2.ProjectID, err = AddProject(project2) + require.NoError(err) + + require.NoError(AddProjectMember(project2.ProjectID, user.UserID, models.PROJECTADMIN)) + + err = AddRepository(*repository) + require.NoError(err) + + repository1 := &models.RepoRecord{ + Name: fmt.Sprintf("%v/repository1", project1.Name), + OwnerName: admin.Username, + ProjectName: project1.Name, + } + err = AddRepository(*repository1) + require.NoError(err) + require.NoError(IncreasePullCount(repository1.Name)) + repository1, err = GetRepositoryByName(repository1.Name) + require.NoError(err) + + repository2 := &models.RepoRecord{ + Name: fmt.Sprintf("%v/repository2", project1.Name), + OwnerName: admin.Username, + ProjectName: project1.Name, + } + err = AddRepository(*repository2) + require.NoError(err) + require.NoError(IncreasePullCount(repository2.Name)) + require.NoError(IncreasePullCount(repository2.Name)) + repository2, err = GetRepositoryByName(repository2.Name) + require.NoError(err) + + repository3 := &models.RepoRecord{ + Name: fmt.Sprintf("%v/repository3", project2.Name), + OwnerName: admin.Username, + ProjectName: project2.Name, + } + err = AddRepository(*repository3) + require.NoError(err) + require.NoError(IncreasePullCount(repository3.Name)) + require.NoError(IncreasePullCount(repository3.Name)) + require.NoError(IncreasePullCount(repository3.Name)) + repository3, err = GetRepositoryByName(repository3.Name) + require.NoError(err) + + deletedPublicProject := models.Project{ + OwnerID: admin.UserID, + Name: "public-deleted", + CreationTime: time.Now(), + OwnerName: admin.Username, + Public: 1, + } + deletedPublicProject.ProjectID, err = AddProject(deletedPublicProject) + require.NoError(err) + deletedPublicRepository1 := &models.RepoRecord{ + Name: fmt.Sprintf("%v/repository1", deletedPublicProject.Name), + OwnerName: admin.Username, + ProjectName: deletedPublicProject.Name, + } + err = AddRepository(*deletedPublicRepository1) + require.NoError(err) + DeleteProject(deletedPublicProject.ProjectID) + + var topRepos []models.TopRepo + + // anonymous should retrieve public non-deleted repositories + topRepos, err = GetTopRepos(NonExistUserID, 3) + require.NoError(err) + require.Len(topRepos, 1) + require.Equal(topRepos, []models.TopRepo{ + models.TopRepo{ + RepoName: repository.Name, + AccessCount: repository.PullCount, + }, + }) + + // admin should retrieve all visible repositories limited by count + topRepos, err = GetTopRepos(admin.UserID, 3) + require.NoError(err) + require.Len(topRepos, 3) + require.Equal(topRepos, []models.TopRepo{ + models.TopRepo{ + RepoName: repository3.Name, + AccessCount: repository3.PullCount, + }, + models.TopRepo{ + RepoName: repository2.Name, + AccessCount: repository2.PullCount, + }, + models.TopRepo{ + RepoName: repository1.Name, + AccessCount: repository1.PullCount, + }, + }) + + // user should retrieve all visible repositories + topRepos, err = GetTopRepos(user.UserID, 3) + require.NoError(err) + require.Len(topRepos, 2) + require.Equal(topRepos, []models.TopRepo{ + models.TopRepo{ + RepoName: repository3.Name, + AccessCount: repository3.PullCount, + }, + models.TopRepo{ + RepoName: repository.Name, + AccessCount: repository.PullCount, + }, + }) +} + func addRepository(repository *models.RepoRecord) error { return AddRepository(*repository) } diff --git a/src/ui/api/repository.go b/src/ui/api/repository.go index 4db0d1eb1..ac5706b2e 100644 --- a/src/ui/api/repository.go +++ b/src/ui/api/repository.go @@ -416,7 +416,12 @@ func (ra *RepositoryAPI) GetTopRepos() { ra.CustomAbort(http.StatusBadRequest, "invalid count") } - repos, err := dao.GetTopRepos(count) + userID, _, ok := ra.GetUserIDForRequest() + if !ok { + userID = dao.NonExistUserID + } + + repos, err := dao.GetTopRepos(userID, count) if err != nil { log.Errorf("failed to get top repos: %v", err) ra.CustomAbort(http.StatusInternalServerError, "internal server error") From c61016512f8455bf0798623dfdc8e78dac8a9227 Mon Sep 17 00:00:00 2001 From: yhua Date: Wed, 11 Jan 2017 10:39:32 +0800 Subject: [PATCH 6/6] fix project name length and follow docker project name rule --- src/ui/api/project.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ui/api/project.go b/src/ui/api/project.go index 7ba50fa3b..ed31dd3df 100644 --- a/src/ui/api/project.go +++ b/src/ui/api/project.go @@ -44,7 +44,8 @@ type projectReq struct { } const projectNameMaxLen int = 30 -const projectNameMinLen int = 4 +const projectNameMinLen int = 2 +const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*` const dupProjectPattern = `Duplicate entry '\w+' for key 'name'` // Prepare validates the URL and the user @@ -417,9 +418,9 @@ func isProjectAdmin(userID int, pid int64) bool { func validateProjectReq(req projectReq) error { pn := req.ProjectName if isIllegalLength(req.ProjectName, projectNameMinLen, projectNameMaxLen) { - return fmt.Errorf("Project name is illegal in length. (greater than 4 or less than 30)") + return fmt.Errorf("Project name is illegal in length. (greater than 2 or less than 30)") } - validProjectName := regexp.MustCompile(`^[a-z0-9](?:-*[a-z0-9])*(?:[._][a-z0-9](?:-*[a-z0-9])*)*$`) + validProjectName := regexp.MustCompile(`^` + restrictedNameChars + `$`) legal := validProjectName.MatchString(pn) if !legal { return fmt.Errorf("project name is not in lower case or contains illegal characters")