Merge pull request #178 from fnordahl/ldap_connector
connector: add LDAP connector
This commit is contained in:
commit
cd72a1f69f
88 changed files with 4714 additions and 87 deletions
|
@ -10,15 +10,23 @@ go:
|
|||
|
||||
env:
|
||||
- DEX_TEST_DSN="postgres://postgres@127.0.0.1:15432/postgres?sslmode=disable" ISOLATED=true
|
||||
DEX_TEST_LDAP_URI="ldap://tlstest.local:1389/????bindname=cn%3Dadmin%2Cdc%3Dexample%2Cdc%3Dorg,X-BINDPW=admin"
|
||||
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get golang.org/x/tools/cmd/vet
|
||||
- docker pull quay.io/coreos/postgres
|
||||
- docker pull osixia/openldap
|
||||
|
||||
script:
|
||||
- docker run -d -p 127.0.0.1:15432:5432 quay.io/coreos/postgres
|
||||
- LDAPCONTAINER=`docker run -e LDAP_TLS_PROTOCOL_MIN=3.0 -e LDAP_TLS_CIPHER_SUITE=NORMAL -d -p 127.0.0.1:1389:389 -p 127.0.0.1:1636:636 -h tlstest.local osixia/openldap`
|
||||
- ./test
|
||||
- docker cp ${LDAPCONTAINER}:container/service/:cfssl/assets/default-ca/default-ca.pem /tmp/openldap-ca.pem
|
||||
- docker cp ${LDAPCONTAINER}:container/service/slapd/assets/certs/ldap.key /tmp/ldap.key
|
||||
- chmod 644 /tmp/ldap.key
|
||||
- docker cp ${LDAPCONTAINER}:container/service/slapd/assets/certs/ldap.crt /tmp/ldap.crt
|
||||
- sudo sh -c 'echo "127.0.0.1 tlstest.local" >> /etc/hosts'
|
||||
- ./test-functional
|
||||
|
||||
deploy:
|
||||
|
|
|
@ -134,6 +134,83 @@ Here's an example of a `bitbucket` connector; the clientID and clientSecret shou
|
|||
}
|
||||
```
|
||||
|
||||
### `ldap` connector
|
||||
|
||||
The `ldap` connector allows email/password based authentication hosted by dex, backed by a LDAP directory.
|
||||
|
||||
Authentication is performed by binding to the configured LDAP server using the user supplied credentials. Successfull bind equals authenticated user.
|
||||
|
||||
Optionally the connector can be configured to search before authentication. The entryDN found will be used to bind to the LDAP server.
|
||||
|
||||
This feature must be enabled to get supplementary information from the directory (ID, Name, Email). This feature can also be used to limit access to the service.
|
||||
|
||||
Example use case: Allow your users to log in with e-mail address as username instead of the identification string in your DNs (typically username).
|
||||
|
||||
___NOTE:___ Users must register with dex at first login. For this to work you have to run dex-worker with --enable-registration.
|
||||
|
||||
In addition to `id` and `type`, the `ldap` connector takes the following additional fields:
|
||||
* serverHost: a `string`. The hostname for the LDAP Server.
|
||||
|
||||
* serverPort: a `unsigned 16-bit integer`. The port for the LDAP Server.
|
||||
|
||||
* timeout: `duration in milliseconds`. The timeout for connecting to and reading from LDAP Server in Milliseconds. Default: `60000` (60 Seconds)
|
||||
|
||||
* useTLS: a `boolean`. Whether the LDAP Connector should issue a StartTLS after successfully connecting to the LDAP Server.
|
||||
|
||||
* useSSL: a `boolean`. Whether the LDAP Connector should expect the connection to be encrypted, typically used with ldaps port (636/tcp).
|
||||
|
||||
* certFile: a `string`. Optional Certificate to present to LDAP server.
|
||||
|
||||
* keyFile: a `string`. Key associated with Certificate specified in `certFile`.
|
||||
|
||||
* caFile: a `string`. Filename for PEM-file containing the set of root certificate authorities that the LDAP client use when verifying the server certificates. Default: use the host's root CA set.
|
||||
|
||||
* skipCertVerification: a `boolean`. Skip server certificate chain verification.
|
||||
|
||||
* baseDN: a `string`. Base DN from which Bind DN is built and searches are based.
|
||||
|
||||
* nameAttribute: a `string`. Attribute to map to Name. Default: `cn`
|
||||
|
||||
* emailAttribute: a `string`. Attribute to map to Email. Default: `mail`
|
||||
|
||||
* searchBeforeAuth: a `boolean`. Perform search for entryDN to be used for bind.
|
||||
|
||||
* searchFilter: a `string`. Filter to apply to search. Variable substititions: `%u` User supplied username/e-mail address. `%b` BaseDN.
|
||||
|
||||
* searchScope: a `string`. Scope of the search. `base|one|sub`. Default: `one`
|
||||
|
||||
* searchBindDN: a `string`. DN to bind as for search operations.
|
||||
|
||||
* searchBindPw: a `string`. Password for bind for search operations.
|
||||
|
||||
* bindTemplate: a `string`. Template to build bindDN from user supplied credentials. Variable subtitutions: `%u` User supplied username/e-mail address. `%b` BaseDN. Default: `uid=%u,%b` ___NOTE:___ This is not used when searchBeforeAuth is enabled.
|
||||
|
||||
* trustedEmailProvider: a `boolean`. If true dex will trust the email address claims from this provider and not require that users verify their emails.
|
||||
|
||||
Here's an example of a `ldap` connector;
|
||||
|
||||
```
|
||||
{
|
||||
"type": "ldap",
|
||||
"id": "ldap",
|
||||
"serverHost": "127.0.0.1",
|
||||
"serverPort": 389,
|
||||
"useTLS": true,
|
||||
"useSSL": false,
|
||||
"skipCertVerification": false,
|
||||
"baseDN": "ou=People,dc=example,dc=com",
|
||||
"nameAttribute": "cn",
|
||||
"emailAttribute": "mail",
|
||||
"searchBeforeAuth": true,
|
||||
"searchFilter": "(mail=%u)",
|
||||
"searchScope": "one",
|
||||
"searchBindDN": "searchuser",
|
||||
"searchBindPw": "supersecret",
|
||||
"bindTemplate": "uid=%u,%b",
|
||||
"trustedEmailProvider": true
|
||||
}
|
||||
```
|
||||
|
||||
## Setting the Configuration
|
||||
|
||||
To set a connectors configuration in dex, put it in some temporary file, then use the dexctl command to upload it to dex:
|
||||
|
|
10
Godeps/Godeps.json
generated
10
Godeps/Godeps.json
generated
|
@ -135,6 +135,11 @@
|
|||
"ImportPath": "gopkg.in/alexcesaro/quotedprintable.v3",
|
||||
"Rev": "2caba252f4dc53eaf6b553000885530023f54623"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/asn1-ber.v1",
|
||||
"Comment": "v1.1",
|
||||
"Rev": "4e86f4367175e39f69d9358a5f17b4dda270378d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/gomail.v2",
|
||||
"Comment": "2.0.0-2-gb1e5552",
|
||||
|
@ -144,6 +149,11 @@
|
|||
"ImportPath": "gopkg.in/gorp.v1",
|
||||
"Comment": "v1.7.1",
|
||||
"Rev": "c87af80f3cc5036b55b83d77171e156791085e2e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/ldap.v2",
|
||||
"Comment": "v2.2",
|
||||
"Rev": "e9a325d64989e2844be629682cb085d2c58eef8d"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
15
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/.travis.yml
generated
vendored
Normal file
15
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- tip
|
||||
go_import_path: gopkg.in/asn-ber.v1
|
||||
install:
|
||||
- go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
|
||||
- go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
|
||||
- go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
|
||||
- go build -v ./...
|
||||
script:
|
||||
- go test -v -cover ./...
|
27
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/LICENSE
generated
vendored
Normal file
|
@ -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.
|
24
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/README.md
generated
vendored
Normal file
24
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/README.md
generated
vendored
Normal file
|
@ -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
|
504
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/ber.go
generated
vendored
Normal file
504
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/ber.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
25
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/content_int.go
generated
vendored
Normal file
25
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/content_int.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
29
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/header.go
generated
vendored
Normal file
29
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/header.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
103
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/identifier.go
generated
vendored
Normal file
103
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/identifier.go
generated
vendored
Normal file
|
@ -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<<highBit) != 0 {
|
||||
break
|
||||
}
|
||||
highBit--
|
||||
}
|
||||
|
||||
tagBytes := int(math.Ceil(float64(highBit) / 7.0))
|
||||
for i := tagBytes - 1; i >= 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
|
||||
}
|
71
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/length.go
generated
vendored
Normal file
71
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/length.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc1.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc1.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc10.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc10.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<07><04><><EFBFBD><EFBFBD>
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc11.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc11.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
015625
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc12.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc12.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
I
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc13.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc13.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc14.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc14.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc15.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc15.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<0C> <><7F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc16.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc16.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<>
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc17.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc17.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<14> <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc18.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc18.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<03><>
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc19.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc19.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc2.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc2.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc20.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc20.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc21.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc21.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Q
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc22.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc22.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><0F>
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc23.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc23.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<><7F><EFBFBD><EFBFBD><EFBFBD>
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc24.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc24.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Î`†HˆŸO …îåJ…ä¿c‹Û/
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc25.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc25.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc26.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc26.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc27.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc27.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc28.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc28.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<01>
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc29.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc29.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc3.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc3.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc30.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc30.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc31.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc31.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc32.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc32.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc33.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc33.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc34.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc34.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc35.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc35.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc36.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc36.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc37.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc37.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc38.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc38.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc39.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc39.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc4.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc4.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc40.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc40.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc41.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc41.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc42.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc42.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc43.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc43.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
$
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc44.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc44.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc45.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc45.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc46.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc46.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc47.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc47.ber
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc48.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc48.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc5.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc5.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc6.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc6.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
+0.E-5
|
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc7.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc7.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
-0.E-5
|
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc8.ber
generated
vendored
Normal file
BIN
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc8.ber
generated
vendored
Normal file
Binary file not shown.
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc9.ber
generated
vendored
Normal file
1
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/tests/tc9.ber
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
件
|
24
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/util.go
generated
vendored
Normal file
24
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/util.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
0
Godeps/_workspace/src/gopkg.in/ldap.v2/.gitignore
generated
vendored
Normal file
0
Godeps/_workspace/src/gopkg.in/ldap.v2/.gitignore
generated
vendored
Normal file
15
Godeps/_workspace/src/gopkg.in/ldap.v2/.travis.yml
generated
vendored
Normal file
15
Godeps/_workspace/src/gopkg.in/ldap.v2/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- tip
|
||||
go_import_path: gopkg.in/ldap.v2
|
||||
install:
|
||||
- go get gopkg.in/asn1-ber.v1
|
||||
- go get gopkg.in/ldap.v2
|
||||
- go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
|
||||
- go build -v ./...
|
||||
script:
|
||||
- go test -v -cover ./...
|
27
Godeps/_workspace/src/gopkg.in/ldap.v2/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/gopkg.in/ldap.v2/LICENSE
generated
vendored
Normal file
|
@ -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.
|
55
Godeps/_workspace/src/gopkg.in/ldap.v2/README.md
generated
vendored
Normal file
55
Godeps/_workspace/src/gopkg.in/ldap.v2/README.md
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
[![GoDoc](https://godoc.org/gopkg.in/ldap.v1?status.svg)](https://godoc.org/gopkg.in/ldap.v1)
|
||||
[![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
|
||||
|
||||
## Working:
|
||||
|
||||
- Connecting to LDAP server
|
||||
- Binding to LDAP server
|
||||
- Searching for entries
|
||||
- Compiling string filters to LDAP filters
|
||||
- Paging Search Results
|
||||
- Modify Requests / Responses
|
||||
- Add Requests / Responses
|
||||
- Delete Requests / Responses
|
||||
- Better Unicode support
|
||||
|
||||
## Examples:
|
||||
|
||||
- search
|
||||
- modify
|
||||
|
||||
## Tests Implemented:
|
||||
|
||||
- Filter Compile / Decompile
|
||||
|
||||
## TODO:
|
||||
|
||||
- [x] Add Requests / Responses
|
||||
- [x] Delete Requests / Responses
|
||||
- [x] Modify DN Requests / Responses
|
||||
- [ ] Compare Requests / Responses
|
||||
- [ ] 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
|
104
Godeps/_workspace/src/gopkg.in/ldap.v2/add.go
generated
vendored
Normal file
104
Godeps/_workspace/src/gopkg.in/ldap.v2/add.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// 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"
|
||||
)
|
||||
|
||||
type Attribute struct {
|
||||
attrType string
|
||||
attrVals []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.attrType, "Type"))
|
||||
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
|
||||
for _, value := range a.attrVals {
|
||||
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
|
||||
}
|
||||
seq.AppendChild(set)
|
||||
return seq
|
||||
}
|
||||
|
||||
type AddRequest struct {
|
||||
dn string
|
||||
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
|
||||
}
|
||||
|
||||
func (a *AddRequest) Attribute(attrType string, attrVals []string) {
|
||||
a.attributes = append(a.attributes, Attribute{attrType: attrType, attrVals: attrVals})
|
||||
}
|
||||
|
||||
func NewAddRequest(dn string) *AddRequest {
|
||||
return &AddRequest{
|
||||
dn: dn,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *Conn) Add(addRequest *AddRequest) error {
|
||||
messageID := l.nextMessageID()
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
|
||||
packet.AppendChild(addRequest.encode())
|
||||
|
||||
l.Debug.PrintPacket(packet)
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channel == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
l.Debug.Printf("%d: waiting for response", messageID)
|
||||
packet = <-channel
|
||||
l.Debug.Printf("%d: got response %p", messageID, packet)
|
||||
if packet == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
|
||||
}
|
||||
|
||||
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", messageID)
|
||||
return nil
|
||||
}
|
135
Godeps/_workspace/src/gopkg.in/ldap.v2/bind.go
generated
vendored
Normal file
135
Godeps/_workspace/src/gopkg.in/ldap.v2/bind.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
|||
// 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"
|
||||
)
|
||||
|
||||
type SimpleBindRequest struct {
|
||||
Username string
|
||||
Password string
|
||||
Controls []Control
|
||||
}
|
||||
|
||||
type SimpleBindResult struct {
|
||||
Controls []Control
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
|
||||
messageID := l.nextMessageID()
|
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
|
||||
encodedBindRequest := simpleBindRequest.encode()
|
||||
packet.AppendChild(encodedBindRequest)
|
||||
|
||||
if l.Debug {
|
||||
ber.PrintPacket(packet)
|
||||
}
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channel == nil {
|
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
packet = <-channel
|
||||
if packet == nil {
|
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve response"))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (l *Conn) Bind(username, password string) error {
|
||||
messageID := l.nextMessageID()
|
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "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)
|
||||
}
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channel == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
packet = <-channel
|
||||
if packet == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve response"))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
23
Godeps/_workspace/src/gopkg.in/ldap.v2/client.go
generated
vendored
Normal file
23
Godeps/_workspace/src/gopkg.in/ldap.v2/client.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package ldap
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
// Client knows how to interact with an LDAP server
|
||||
type Client interface {
|
||||
Start()
|
||||
StartTLS(config *tls.Config) error
|
||||
Close()
|
||||
|
||||
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)
|
||||
}
|
85
Godeps/_workspace/src/gopkg.in/ldap.v2/compare.go
generated
vendored
Normal file
85
Godeps/_workspace/src/gopkg.in/ldap.v2/compare.go
generated
vendored
Normal file
|
@ -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 <attributedescription>
|
||||
// -- [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) {
|
||||
messageID := l.nextMessageID()
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "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)
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if channel == nil {
|
||||
return false, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
l.Debug.Printf("%d: waiting for response", messageID)
|
||||
packet = <-channel
|
||||
l.Debug.Printf("%d: got response %p", messageID, packet)
|
||||
if packet == nil {
|
||||
return false, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
369
Godeps/_workspace/src/gopkg.in/ldap.v2/conn.go
generated
vendored
Normal file
369
Godeps/_workspace/src/gopkg.in/ldap.v2/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,369 @@
|
|||
// 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 = 0
|
||||
MessageRequest = 1
|
||||
MessageResponse = 2
|
||||
MessageFinish = 3
|
||||
)
|
||||
|
||||
type messagePacket struct {
|
||||
Op int
|
||||
MessageID int64
|
||||
Packet *ber.Packet
|
||||
Channel chan *ber.Packet
|
||||
}
|
||||
|
||||
type sendMessageFlags uint
|
||||
|
||||
const (
|
||||
startTLS sendMessageFlags = 1 << iota
|
||||
)
|
||||
|
||||
// Conn represents an LDAP Connection
|
||||
type Conn struct {
|
||||
conn net.Conn
|
||||
isTLS bool
|
||||
isClosing bool
|
||||
isStartingTLS bool
|
||||
Debug debugging
|
||||
chanConfirm chan bool
|
||||
chanResults map[int64]chan *ber.Packet
|
||||
chanMessage chan *messagePacket
|
||||
chanMessageID chan int64
|
||||
wgSender sync.WaitGroup
|
||||
wgClose sync.WaitGroup
|
||||
once sync.Once
|
||||
outstandingRequests uint
|
||||
messageMutex sync.Mutex
|
||||
}
|
||||
|
||||
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),
|
||||
chanResults: map[int64]chan *ber.Packet{},
|
||||
isTLS: isTLS,
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
messageID := l.nextMessageID()
|
||||
|
||||
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, messageID, "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)
|
||||
|
||||
channel, err := l.sendMessageWithFlags(packet, startTLS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channel == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
|
||||
l.Debug.Printf("%d: waiting for response", messageID)
|
||||
packet = <-channel
|
||||
l.Debug.Printf("%d: got response %p", messageID, packet)
|
||||
l.finishMessage(messageID)
|
||||
|
||||
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) (chan *ber.Packet, error) {
|
||||
return l.sendMessageWithFlags(packet, 0)
|
||||
}
|
||||
|
||||
func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (chan *ber.Packet, 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"))
|
||||
} else {
|
||||
l.isStartingTLS = true
|
||||
}
|
||||
}
|
||||
l.outstandingRequests++
|
||||
|
||||
l.messageMutex.Unlock()
|
||||
|
||||
out := make(chan *ber.Packet)
|
||||
message := &messagePacket{
|
||||
Op: MessageRequest,
|
||||
MessageID: packet.Children[0].Value.(int64),
|
||||
Packet: packet,
|
||||
Channel: out,
|
||||
}
|
||||
l.sendProcessMessage(message)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (l *Conn) finishMessage(messageID int64) {
|
||||
if l.isClosing {
|
||||
return
|
||||
}
|
||||
|
||||
l.messageMutex.Lock()
|
||||
l.outstandingRequests--
|
||||
if l.isStartingTLS {
|
||||
l.isStartingTLS = false
|
||||
}
|
||||
l.messageMutex.Unlock()
|
||||
|
||||
message := &messagePacket{
|
||||
Op: MessageFinish,
|
||||
MessageID: messageID,
|
||||
}
|
||||
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, channel := range l.chanResults {
|
||||
l.Debug.Printf("Closing channel for MessageID %d", messageID)
|
||||
close(channel)
|
||||
delete(l.chanResults, messageID)
|
||||
}
|
||||
close(l.chanMessageID)
|
||||
l.chanConfirm <- true
|
||||
close(l.chanConfirm)
|
||||
}()
|
||||
|
||||
var messageID int64 = 1
|
||||
for {
|
||||
select {
|
||||
case l.chanMessageID <- messageID:
|
||||
messageID++
|
||||
case messagePacket, ok := <-l.chanMessage:
|
||||
if !ok {
|
||||
l.Debug.Printf("Shutting down - message channel is closed")
|
||||
return
|
||||
}
|
||||
switch messagePacket.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", messagePacket.MessageID)
|
||||
l.chanResults[messagePacket.MessageID] = messagePacket.Channel
|
||||
// go routine
|
||||
buf := messagePacket.Packet.Bytes()
|
||||
|
||||
_, err := l.conn.Write(buf)
|
||||
if err != nil {
|
||||
l.Debug.Printf("Error Sending Message: %s", err.Error())
|
||||
break
|
||||
}
|
||||
case MessageResponse:
|
||||
l.Debug.Printf("Receiving message %d", messagePacket.MessageID)
|
||||
if chanResult, ok := l.chanResults[messagePacket.MessageID]; ok {
|
||||
chanResult <- messagePacket.Packet
|
||||
} else {
|
||||
log.Printf("Received unexpected message %d", messagePacket.MessageID)
|
||||
ber.PrintPacket(messagePacket.Packet)
|
||||
}
|
||||
case MessageFinish:
|
||||
// Remove from message list
|
||||
l.Debug.Printf("Finished message %d", messagePacket.MessageID)
|
||||
close(l.chanResults[messagePacket.MessageID])
|
||||
delete(l.chanResults, messagePacket.MessageID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
332
Godeps/_workspace/src/gopkg.in/ldap.v2/control.go
generated
vendored
Normal file
332
Godeps/_workspace/src/gopkg.in/ldap.v2/control.go
generated
vendored
Normal file
|
@ -0,0 +1,332 @@
|
|||
// 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 = "1.2.840.113556.1.4.319"
|
||||
ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
|
||||
ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
|
||||
ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
|
||||
ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
|
||||
)
|
||||
|
||||
var ControlTypeMap = map[string]string{
|
||||
ControlTypePaging: "Paging",
|
||||
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
|
||||
ControlTypeManageDsaIT: "Manage DSA IT",
|
||||
}
|
||||
|
||||
type Control interface {
|
||||
GetControlType() string
|
||||
Encode() *ber.Packet
|
||||
String() string
|
||||
}
|
||||
|
||||
type ControlString struct {
|
||||
ControlType string
|
||||
Criticality bool
|
||||
ControlValue string
|
||||
}
|
||||
|
||||
func (c *ControlString) GetControlType() string {
|
||||
return c.ControlType
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type ControlPaging struct {
|
||||
PagingSize uint32
|
||||
Cookie []byte
|
||||
}
|
||||
|
||||
func (c *ControlPaging) GetControlType() string {
|
||||
return ControlTypePaging
|
||||
}
|
||||
|
||||
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, uint64(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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (c *ControlPaging) SetCookie(cookie []byte) {
|
||||
c.Cookie = cookie
|
||||
}
|
||||
|
||||
type ControlBeheraPasswordPolicy struct {
|
||||
Expire int64
|
||||
Grace int64
|
||||
Error int8
|
||||
ErrorString string
|
||||
}
|
||||
|
||||
func (c *ControlBeheraPasswordPolicy) GetControlType() string {
|
||||
return ControlTypeBeheraPasswordPolicy
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type ControlVChuPasswordMustChange struct {
|
||||
MustChange bool
|
||||
}
|
||||
|
||||
func (c *ControlVChuPasswordMustChange) GetControlType() string {
|
||||
return ControlTypeVChuPasswordMustChange
|
||||
}
|
||||
|
||||
func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ControlVChuPasswordMustChange) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality: %t MustChange: %b",
|
||||
ControlTypeMap[ControlTypeVChuPasswordMustChange],
|
||||
ControlTypeVChuPasswordMustChange,
|
||||
false,
|
||||
c.MustChange)
|
||||
}
|
||||
|
||||
type ControlVChuPasswordWarning struct {
|
||||
Expire int64
|
||||
}
|
||||
|
||||
func (c *ControlVChuPasswordWarning) GetControlType() string {
|
||||
return ControlTypeVChuPasswordWarning
|
||||
}
|
||||
|
||||
func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ControlVChuPasswordWarning) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality: %t Expire: %b",
|
||||
ControlTypeMap[ControlTypeVChuPasswordWarning],
|
||||
ControlTypeVChuPasswordWarning,
|
||||
false,
|
||||
c.Expire)
|
||||
}
|
||||
|
||||
type ControlManageDsaIT struct {
|
||||
Criticality bool
|
||||
}
|
||||
|
||||
func (c *ControlManageDsaIT) GetControlType() string {
|
||||
return ControlTypeManageDsaIT
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (c *ControlManageDsaIT) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality: %t",
|
||||
ControlTypeMap[ControlTypeManageDsaIT],
|
||||
ControlTypeManageDsaIT,
|
||||
c.Criticality)
|
||||
}
|
||||
|
||||
func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
|
||||
return &ControlManageDsaIT{Criticality: Criticality}
|
||||
}
|
||||
|
||||
func FindControl(controls []Control, controlType string) Control {
|
||||
for _, c := range controls {
|
||||
if c.GetControlType() == controlType {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeControl(packet *ber.Packet) Control {
|
||||
ControlType := packet.Children[0].Value.(string)
|
||||
Criticality := false
|
||||
|
||||
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
||||
value := packet.Children[1]
|
||||
if len(packet.Children) == 3 {
|
||||
value = packet.Children[2]
|
||||
packet.Children[1].Description = "Criticality"
|
||||
Criticality = packet.Children[1].Value.(bool)
|
||||
}
|
||||
|
||||
value.Description = "Control Value"
|
||||
switch ControlType {
|
||||
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
|
||||
child := child.Children[0]
|
||||
packet := ber.DecodePacket(child.Data.Bytes())
|
||||
val, ok := packet.Value.(int64)
|
||||
if ok {
|
||||
if child.Tag == 0 {
|
||||
//timeBeforeExpiration
|
||||
c.Expire = val
|
||||
child.Value = c.Expire
|
||||
} else if child.Tag == 1 {
|
||||
//graceAuthNsRemaining
|
||||
c.Grace = val
|
||||
child.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
|
||||
}
|
||||
c := new(ControlString)
|
||||
c.ControlType = ControlType
|
||||
c.Criticality = Criticality
|
||||
c.ControlValue = value.Value.(string)
|
||||
return c
|
||||
}
|
||||
|
||||
func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
|
||||
return &ControlString{
|
||||
ControlType: controlType,
|
||||
Criticality: criticality,
|
||||
ControlValue: controlValue,
|
||||
}
|
||||
}
|
||||
|
||||
func NewControlPaging(pagingSize uint32) *ControlPaging {
|
||||
return &ControlPaging{PagingSize: pagingSize}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
24
Godeps/_workspace/src/gopkg.in/ldap.v2/debug.go
generated
vendored
Normal file
24
Godeps/_workspace/src/gopkg.in/ldap.v2/debug.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
79
Godeps/_workspace/src/gopkg.in/ldap.v2/del.go
generated
vendored
Normal file
79
Godeps/_workspace/src/gopkg.in/ldap.v2/del.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// https://tools.ietf.org/html/rfc4511
|
||||
//
|
||||
// DelRequest ::= [APPLICATION 10] LDAPDN
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"gopkg.in/asn1-ber.v1"
|
||||
)
|
||||
|
||||
type DelRequest struct {
|
||||
DN string
|
||||
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
|
||||
}
|
||||
|
||||
func NewDelRequest(DN string,
|
||||
Controls []Control) *DelRequest {
|
||||
return &DelRequest{
|
||||
DN: DN,
|
||||
Controls: Controls,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Conn) Del(delRequest *DelRequest) error {
|
||||
messageID := l.nextMessageID()
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
|
||||
packet.AppendChild(delRequest.encode())
|
||||
if delRequest.Controls != nil {
|
||||
packet.AppendChild(encodeControls(delRequest.Controls))
|
||||
}
|
||||
|
||||
l.Debug.PrintPacket(packet)
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channel == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
l.Debug.Printf("%d: waiting for response", messageID)
|
||||
packet = <-channel
|
||||
l.Debug.Printf("%d: got response %p", messageID, packet)
|
||||
if packet == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
|
||||
}
|
||||
|
||||
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", messageID)
|
||||
return nil
|
||||
}
|
155
Godeps/_workspace/src/gopkg.in/ldap.v2/dn.go
generated
vendored
Normal file
155
Godeps/_workspace/src/gopkg.in/ldap.v2/dn.go
generated
vendored
Normal file
|
@ -0,0 +1,155 @@
|
|||
// 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 <escaped>, 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 <descr>, <numericoid>, <COMMA>, <DQUOTE>,
|
||||
// <EQUALS>, <ESC>, <HEX>, <LANGLE>, <NULL>, <PLUS>, <RANGLE>, <SEMI>,
|
||||
// <SPACE>, <SHARP>, and <UTFMB> are defined in [RFC4512].
|
||||
//
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
enchex "encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
ber "gopkg.in/asn1-ber.v1"
|
||||
)
|
||||
|
||||
type AttributeTypeAndValue struct {
|
||||
Type string
|
||||
Value string
|
||||
}
|
||||
|
||||
type RelativeDN struct {
|
||||
Attributes []*AttributeTypeAndValue
|
||||
}
|
||||
|
||||
type DN struct {
|
||||
RDNs []*RelativeDN
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
for i := 0; i < len(str); i++ {
|
||||
char := str[i]
|
||||
if escaping {
|
||||
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, errors.New(
|
||||
fmt.Sprintf("Failed to decode escaped character: %s", err))
|
||||
} else if n != 1 {
|
||||
return nil, errors.New(
|
||||
fmt.Sprintf("Expected 1 byte when un-escaping, got %d", n))
|
||||
}
|
||||
buffer.WriteByte(dst[0])
|
||||
i++
|
||||
} else if char == '\\' {
|
||||
escaping = true
|
||||
} else if char == '=' {
|
||||
attribute.Type = buffer.String()
|
||||
buffer.Reset()
|
||||
// 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:]
|
||||
}
|
||||
raw_ber, err := enchex.DecodeString(data)
|
||||
if err != nil {
|
||||
return nil, errors.New(
|
||||
fmt.Sprintf("Failed to decode BER encoding: %s", err))
|
||||
}
|
||||
packet := ber.DecodePacket(raw_ber)
|
||||
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 = buffer.String()
|
||||
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)
|
||||
}
|
||||
buffer.Reset()
|
||||
} else {
|
||||
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 = buffer.String()
|
||||
rdn.Attributes = append(rdn.Attributes, attribute)
|
||||
dn.RDNs = append(dn.RDNs, rdn)
|
||||
}
|
||||
return dn, nil
|
||||
}
|
4
Godeps/_workspace/src/gopkg.in/ldap.v2/doc.go
generated
vendored
Normal file
4
Godeps/_workspace/src/gopkg.in/ldap.v2/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/*
|
||||
Package ldap provides basic LDAP v3 functionality.
|
||||
*/
|
||||
package ldap
|
137
Godeps/_workspace/src/gopkg.in/ldap.v2/error.go
generated
vendored
Normal file
137
Godeps/_workspace/src/gopkg.in/ldap.v2/error.go
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
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
|
||||
)
|
||||
|
||||
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 len(packet.Children) >= 2 {
|
||||
response := packet.Children[1]
|
||||
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"
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Err error
|
||||
ResultCode uint8
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
|
||||
}
|
||||
|
||||
func NewError(resultCode uint8, err error) error {
|
||||
return &Error{ResultCode: resultCode, Err: err}
|
||||
}
|
||||
|
||||
func IsErrorWithCode(err error, desiredResultCode uint8) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
serverError, ok := err.(*Error)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return serverError.ResultCode == desiredResultCode
|
||||
}
|
456
Godeps/_workspace/src/gopkg.in/ldap.v2/filter.go
generated
vendored
Normal file
456
Godeps/_workspace/src/gopkg.in/ldap.v2/filter.go
generated
vendored
Normal file
|
@ -0,0 +1,456 @@
|
|||
// 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"
|
||||
)
|
||||
|
||||
const (
|
||||
FilterAnd = 0
|
||||
FilterOr = 1
|
||||
FilterNot = 2
|
||||
FilterEqualityMatch = 3
|
||||
FilterSubstrings = 4
|
||||
FilterGreaterOrEqual = 5
|
||||
FilterLessOrEqual = 6
|
||||
FilterPresent = 7
|
||||
FilterApproxMatch = 8
|
||||
FilterExtensibleMatch = 9
|
||||
)
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
const (
|
||||
FilterSubstringsInitial = 0
|
||||
FilterSubstringsAny = 1
|
||||
FilterSubstringsFinal = 2
|
||||
)
|
||||
|
||||
var FilterSubstringsMap = map[uint64]string{
|
||||
FilterSubstringsInitial: "Substrings Initial",
|
||||
FilterSubstringsAny: "Substrings Any",
|
||||
FilterSubstringsFinal: "Substrings Final",
|
||||
}
|
||||
|
||||
const (
|
||||
MatchingRuleAssertionMatchingRule = 1
|
||||
MatchingRuleAssertionType = 2
|
||||
MatchingRuleAssertionMatchValue = 3
|
||||
MatchingRuleAssertionDNAttributes = 4
|
||||
)
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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:
|
||||
READING_ATTR := 0
|
||||
READING_EXTENSIBLE_MATCHING_RULE := 1
|
||||
READING_CONDITION := 2
|
||||
|
||||
state := READING_ATTR
|
||||
|
||||
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 READING_ATTR:
|
||||
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 = READING_CONDITION
|
||||
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 = READING_EXTENSIBLE_MATCHING_RULE
|
||||
newPos += 4
|
||||
|
||||
// Extensible rule, with attr only
|
||||
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
|
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
|
||||
state = READING_CONDITION
|
||||
newPos += 2
|
||||
|
||||
// Extensible rule, with no DN attribute matching
|
||||
case currentRune == ':':
|
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
|
||||
state = READING_EXTENSIBLE_MATCHING_RULE
|
||||
newPos += 1
|
||||
|
||||
// Equality condition
|
||||
case currentRune == '=':
|
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
|
||||
state = READING_CONDITION
|
||||
newPos += 1
|
||||
|
||||
// Greater-than or equal
|
||||
case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="):
|
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
|
||||
state = READING_CONDITION
|
||||
newPos += 2
|
||||
|
||||
// Less-than or equal
|
||||
case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="):
|
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
|
||||
state = READING_CONDITION
|
||||
newPos += 2
|
||||
|
||||
// Approx
|
||||
case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="):
|
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch])
|
||||
state = READING_CONDITION
|
||||
newPos += 2
|
||||
|
||||
// Still reading the attribute name
|
||||
default:
|
||||
attribute += fmt.Sprintf("%c", currentRune)
|
||||
newPos += currentWidth
|
||||
}
|
||||
|
||||
case READING_EXTENSIBLE_MATCHING_RULE:
|
||||
switch {
|
||||
|
||||
// Matching rule OID is done
|
||||
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
|
||||
state = READING_CONDITION
|
||||
newPos += 2
|
||||
|
||||
// Still reading the matching rule oid
|
||||
default:
|
||||
extensibleMatchingRule += fmt.Sprintf("%c", currentRune)
|
||||
newPos += currentWidth
|
||||
}
|
||||
|
||||
case READING_CONDITION:
|
||||
// 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, err := escapedStringToEncodedBytes(condition)
|
||||
if err != nil {
|
||||
return packet, newPos, err
|
||||
}
|
||||
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, err := escapedStringToEncodedBytes(part)
|
||||
if err != nil {
|
||||
return packet, newPos, err
|
||||
}
|
||||
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)]))
|
||||
}
|
||||
packet.AppendChild(seq)
|
||||
default:
|
||||
encodedString, err := escapedStringToEncodedBytes(condition)
|
||||
if err != nil {
|
||||
return packet, newPos, err
|
||||
}
|
||||
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"))
|
||||
}
|
||||
if escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]); decodeErr != nil {
|
||||
return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter"))
|
||||
} else {
|
||||
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
|
||||
}
|
286
Godeps/_workspace/src/gopkg.in/ldap.v2/ldap.go
generated
vendored
Normal file
286
Godeps/_workspace/src/gopkg.in/ldap.v2/ldap.go
generated
vendored
Normal file
|
@ -0,0 +1,286 @@
|
|||
// 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
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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 {
|
||||
child.Description = "Control"
|
||||
child.Children[0].Description = "Control Type (" + ControlTypeMap[child.Children[0].Value.(string)] + ")"
|
||||
value := child.Children[1]
|
||||
if len(child.Children) == 3 {
|
||||
child.Children[1].Description = "Criticality"
|
||||
value = child.Children[2]
|
||||
}
|
||||
value.Description = "Control Value"
|
||||
|
||||
switch child.Children[0].Value.(string) {
|
||||
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
|
||||
child := child.Children[0]
|
||||
packet := ber.DecodePacket(child.Data.Bytes())
|
||||
val, ok := packet.Value.(int64)
|
||||
if ok {
|
||||
if child.Tag == 0 {
|
||||
//timeBeforeExpiration
|
||||
value.Description += " (TimeBeforeExpiration)"
|
||||
child.Value = val
|
||||
} else if child.Tag == 1 {
|
||||
//graceAuthNsRemaining
|
||||
value.Description += " (GraceAuthNsRemaining)"
|
||||
child.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])
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
156
Godeps/_workspace/src/gopkg.in/ldap.v2/modify.go
generated
vendored
Normal file
156
Godeps/_workspace/src/gopkg.in/ldap.v2/modify.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
// 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 <attributedescription>
|
||||
// -- [RFC4512]
|
||||
//
|
||||
// AttributeValue ::= OCTET STRING
|
||||
//
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"gopkg.in/asn1-ber.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
AddAttribute = 0
|
||||
DeleteAttribute = 1
|
||||
ReplaceAttribute = 2
|
||||
)
|
||||
|
||||
type PartialAttribute struct {
|
||||
attrType string
|
||||
attrVals []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.attrType, "Type"))
|
||||
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
|
||||
for _, value := range p.attrVals {
|
||||
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
|
||||
}
|
||||
seq.AppendChild(set)
|
||||
return seq
|
||||
}
|
||||
|
||||
type ModifyRequest struct {
|
||||
dn string
|
||||
addAttributes []PartialAttribute
|
||||
deleteAttributes []PartialAttribute
|
||||
replaceAttributes []PartialAttribute
|
||||
}
|
||||
|
||||
func (m *ModifyRequest) Add(attrType string, attrVals []string) {
|
||||
m.addAttributes = append(m.addAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
|
||||
}
|
||||
|
||||
func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
|
||||
m.deleteAttributes = append(m.deleteAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
|
||||
}
|
||||
|
||||
func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
|
||||
m.replaceAttributes = append(m.replaceAttributes, PartialAttribute{attrType: attrType, attrVals: 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
|
||||
}
|
||||
|
||||
func NewModifyRequest(
|
||||
dn string,
|
||||
) *ModifyRequest {
|
||||
return &ModifyRequest{
|
||||
dn: dn,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
|
||||
messageID := l.nextMessageID()
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
|
||||
packet.AppendChild(modifyRequest.encode())
|
||||
|
||||
l.Debug.PrintPacket(packet)
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channel == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
l.Debug.Printf("%d: waiting for response", messageID)
|
||||
packet = <-channel
|
||||
l.Debug.Printf("%d: got response %p", messageID, packet)
|
||||
if packet == nil {
|
||||
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
|
||||
}
|
||||
|
||||
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", messageID)
|
||||
return nil
|
||||
}
|
137
Godeps/_workspace/src/gopkg.in/ldap.v2/passwdmodify.go
generated
vendored
Normal file
137
Godeps/_workspace/src/gopkg.in/ldap.v2/passwdmodify.go
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
// 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"
|
||||
)
|
||||
|
||||
type PasswordModifyRequest struct {
|
||||
UserIdentity string
|
||||
OldPassword string
|
||||
NewPassword string
|
||||
}
|
||||
|
||||
type PasswordModifyResult struct {
|
||||
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
|
||||
}
|
||||
|
||||
// Create 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,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) {
|
||||
messageID := l.nextMessageID()
|
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
|
||||
|
||||
encodedPasswordModifyRequest, err := passwordModifyRequest.encode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packet.AppendChild(encodedPasswordModifyRequest)
|
||||
|
||||
l.Debug.PrintPacket(packet)
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channel == nil {
|
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
result := &PasswordModifyResult{}
|
||||
|
||||
l.Debug.Printf("%d: waiting for response", messageID)
|
||||
packet = <-channel
|
||||
l.Debug.Printf("%d: got response %p", messageID, packet)
|
||||
|
||||
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
|
||||
}
|
403
Godeps/_workspace/src/gopkg.in/ldap.v2/search.go
generated
vendored
Normal file
403
Godeps/_workspace/src/gopkg.in/ldap.v2/search.go
generated
vendored
Normal file
|
@ -0,0 +1,403 @@
|
|||
// 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
|
||||
// -- <attributeSelector> 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"
|
||||
)
|
||||
|
||||
const (
|
||||
ScopeBaseObject = 0
|
||||
ScopeSingleLevel = 1
|
||||
ScopeWholeSubtree = 2
|
||||
)
|
||||
|
||||
var ScopeMap = map[int]string{
|
||||
ScopeBaseObject: "Base Object",
|
||||
ScopeSingleLevel: "Single Level",
|
||||
ScopeWholeSubtree: "Whole Subtree",
|
||||
}
|
||||
|
||||
const (
|
||||
NeverDerefAliases = 0
|
||||
DerefInSearching = 1
|
||||
DerefFindingBaseObj = 2
|
||||
DerefAlways = 3
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
DN string
|
||||
Attributes []*EntryAttribute
|
||||
}
|
||||
|
||||
func (e *Entry) GetAttributeValues(attribute string) []string {
|
||||
for _, attr := range e.Attributes {
|
||||
if attr.Name == attribute {
|
||||
return attr.Values
|
||||
}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
|
||||
for _, attr := range e.Attributes {
|
||||
if attr.Name == attribute {
|
||||
return attr.ByteValues
|
||||
}
|
||||
}
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
func (e *Entry) GetAttributeValue(attribute string) string {
|
||||
values := e.GetAttributeValues(attribute)
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
}
|
||||
return values[0]
|
||||
}
|
||||
|
||||
func (e *Entry) GetRawAttributeValue(attribute string) []byte {
|
||||
values := e.GetRawAttributeValues(attribute)
|
||||
if len(values) == 0 {
|
||||
return []byte{}
|
||||
}
|
||||
return values[0]
|
||||
}
|
||||
|
||||
func (e *Entry) Print() {
|
||||
fmt.Printf("DN: %s\n", e.DN)
|
||||
for _, attr := range e.Attributes {
|
||||
attr.Print()
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
type EntryAttribute struct {
|
||||
Name string
|
||||
Values []string
|
||||
ByteValues [][]byte
|
||||
}
|
||||
|
||||
func (e *EntryAttribute) Print() {
|
||||
fmt.Printf("%s: %s\n", e.Name, e.Values)
|
||||
}
|
||||
|
||||
func (e *EntryAttribute) PrettyPrint(indent int) {
|
||||
fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
|
||||
}
|
||||
|
||||
type SearchResult struct {
|
||||
Entries []*Entry
|
||||
Referrals []string
|
||||
Controls []Control
|
||||
}
|
||||
|
||||
func (s *SearchResult) Print() {
|
||||
for _, entry := range s.Entries {
|
||||
entry.Print()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SearchResult) PrettyPrint(indent int) {
|
||||
for _, entry := range s.Entries {
|
||||
entry.PrettyPrint(indent)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
|
||||
if searchRequest.Controls == nil {
|
||||
searchRequest.Controls = make([]Control, 0)
|
||||
}
|
||||
|
||||
pagingControl := NewControlPaging(pagingSize)
|
||||
searchRequest.Controls = append(searchRequest.Controls, pagingControl)
|
||||
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
|
||||
}
|
||||
|
||||
func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
|
||||
messageID := l.nextMessageID()
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "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)
|
||||
|
||||
channel, err := l.sendMessage(packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channel == nil {
|
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
|
||||
}
|
||||
defer l.finishMessage(messageID)
|
||||
|
||||
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", messageID)
|
||||
packet = <-channel
|
||||
l.Debug.Printf("%d: got response %p", messageID, packet)
|
||||
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)
|
||||
}
|
||||
|
||||
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", messageID)
|
||||
return result, nil
|
||||
}
|
339
connector/connector_ldap.go
Normal file
339
connector/connector_ldap.go
Normal file
|
@ -0,0 +1,339 @@
|
|||
package connector
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
|
||||
"fmt"
|
||||
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/dex/pkg/log"
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
|
||||
"gopkg.in/ldap.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
LDAPConnectorType = "ldap"
|
||||
LDAPLoginPageTemplateName = "ldap-login.html"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterConnectorConfigType(LDAPConnectorType, func() ConnectorConfig { return &LDAPConnectorConfig{} })
|
||||
}
|
||||
|
||||
type LDAPConnectorConfig struct {
|
||||
ID string `json:"id"`
|
||||
ServerHost string `json:"serverHost"`
|
||||
ServerPort uint16 `json:"serverPort"`
|
||||
Timeout time.Duration `json:"timeout"`
|
||||
UseTLS bool `json:"useTLS"`
|
||||
UseSSL bool `json:"useSSL"`
|
||||
CertFile string `json:"certFile"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
CaFile string `json:"caFile"`
|
||||
SkipCertVerification bool `json:"skipCertVerification"`
|
||||
BaseDN string `json:"baseDN"`
|
||||
NameAttribute string `json:"nameAttribute"`
|
||||
EmailAttribute string `json:"emailAttribute"`
|
||||
SearchBeforeAuth bool `json:"searchBeforeAuth"`
|
||||
SearchFilter string `json:"searchFilter"`
|
||||
SearchScope string `json:"searchScope"`
|
||||
SearchBindDN string `json:"searchBindDN"`
|
||||
SearchBindPw string `json:"searchBindPw"`
|
||||
BindTemplate string `json:"bindTemplate"`
|
||||
TrustedEmailProvider bool `json:"trustedEmailProvider"`
|
||||
}
|
||||
|
||||
func (cfg *LDAPConnectorConfig) ConnectorID() string {
|
||||
return cfg.ID
|
||||
}
|
||||
|
||||
func (cfg *LDAPConnectorConfig) ConnectorType() string {
|
||||
return LDAPConnectorType
|
||||
}
|
||||
|
||||
type LDAPConnector struct {
|
||||
id string
|
||||
idp *LDAPIdentityProvider
|
||||
namespace url.URL
|
||||
trustedEmailProvider bool
|
||||
loginFunc oidc.LoginFunc
|
||||
loginTpl *template.Template
|
||||
}
|
||||
|
||||
func (cfg *LDAPConnectorConfig) Connector(ns url.URL, lf oidc.LoginFunc, tpls *template.Template) (Connector, error) {
|
||||
ns.Path = path.Join(ns.Path, httpPathCallback)
|
||||
tpl := tpls.Lookup(LDAPLoginPageTemplateName)
|
||||
if tpl == nil {
|
||||
return nil, fmt.Errorf("unable to find necessary HTML template")
|
||||
}
|
||||
|
||||
if cfg.UseTLS && cfg.UseSSL {
|
||||
return nil, fmt.Errorf("Invalid configuration. useTLS and useSSL are mutual exclusive.")
|
||||
}
|
||||
|
||||
if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 {
|
||||
return nil, fmt.Errorf("Invalid configuration. Both certFile and keyFile must be specified.")
|
||||
}
|
||||
|
||||
var nameAttribute, emailAttribute, bindTemplate string
|
||||
if len(cfg.NameAttribute) > 0 {
|
||||
nameAttribute = cfg.NameAttribute
|
||||
} else {
|
||||
nameAttribute = "cn"
|
||||
}
|
||||
|
||||
if len(cfg.EmailAttribute) > 0 {
|
||||
emailAttribute = cfg.EmailAttribute
|
||||
} else {
|
||||
emailAttribute = "mail"
|
||||
}
|
||||
|
||||
if len(cfg.BindTemplate) > 0 {
|
||||
if cfg.SearchBeforeAuth {
|
||||
log.Warningf("bindTemplate not used when searchBeforeAuth specified.")
|
||||
}
|
||||
bindTemplate = cfg.BindTemplate
|
||||
} else {
|
||||
bindTemplate = "uid=%u,%b"
|
||||
}
|
||||
|
||||
var searchScope int
|
||||
if len(cfg.SearchScope) > 0 {
|
||||
switch {
|
||||
case strings.EqualFold(cfg.SearchScope, "BASE"):
|
||||
searchScope = ldap.ScopeBaseObject
|
||||
case strings.EqualFold(cfg.SearchScope, "ONE"):
|
||||
searchScope = ldap.ScopeSingleLevel
|
||||
case strings.EqualFold(cfg.SearchScope, "SUB"):
|
||||
searchScope = ldap.ScopeWholeSubtree
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid value for searchScope: '%v'. Must be one of 'base', 'one' or 'sub'.", cfg.SearchScope)
|
||||
}
|
||||
} else {
|
||||
searchScope = ldap.ScopeSingleLevel
|
||||
}
|
||||
|
||||
if cfg.Timeout != 0 {
|
||||
ldap.DefaultTimeout = cfg.Timeout * time.Millisecond
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: cfg.ServerHost,
|
||||
InsecureSkipVerify: cfg.SkipCertVerification,
|
||||
}
|
||||
|
||||
if (cfg.UseTLS || cfg.UseSSL) && len(cfg.CaFile) > 0 {
|
||||
buf, err := ioutil.ReadFile(cfg.CaFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootCertPool := x509.NewCertPool()
|
||||
ok := rootCertPool.AppendCertsFromPEM(buf)
|
||||
if ok {
|
||||
tlsConfig.RootCAs = rootCertPool
|
||||
} else {
|
||||
return nil, fmt.Errorf("%v: Unable to parse certificate data.", cfg.CaFile)
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.UseTLS || cfg.UseSSL) && len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
|
||||
cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
idp := &LDAPIdentityProvider{
|
||||
serverHost: cfg.ServerHost,
|
||||
serverPort: cfg.ServerPort,
|
||||
useTLS: cfg.UseTLS,
|
||||
useSSL: cfg.UseSSL,
|
||||
baseDN: cfg.BaseDN,
|
||||
nameAttribute: nameAttribute,
|
||||
emailAttribute: emailAttribute,
|
||||
searchBeforeAuth: cfg.SearchBeforeAuth,
|
||||
searchFilter: cfg.SearchFilter,
|
||||
searchScope: searchScope,
|
||||
searchBindDN: cfg.SearchBindDN,
|
||||
searchBindPw: cfg.SearchBindPw,
|
||||
bindTemplate: bindTemplate,
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
|
||||
idpc := &LDAPConnector{
|
||||
id: cfg.ID,
|
||||
idp: idp,
|
||||
namespace: ns,
|
||||
trustedEmailProvider: cfg.TrustedEmailProvider,
|
||||
loginFunc: lf,
|
||||
loginTpl: tpl,
|
||||
}
|
||||
|
||||
return idpc, nil
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) Healthy() error {
|
||||
ldapConn, err := c.idp.LDAPConnect()
|
||||
if err == nil {
|
||||
ldapConn.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) LoginURL(sessionKey, prompt string) (string, error) {
|
||||
q := url.Values{}
|
||||
q.Set("session_key", sessionKey)
|
||||
q.Set("prompt", prompt)
|
||||
enc := q.Encode()
|
||||
|
||||
return path.Join(c.namespace.Path, "login") + "?" + enc, nil
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) Register(mux *http.ServeMux, errorURL url.URL) {
|
||||
route := path.Join(c.namespace.Path, "login")
|
||||
mux.Handle(route, handleLoginFunc(c.loginFunc, c.loginTpl, c.idp, route, errorURL))
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) Sync() chan struct{} {
|
||||
return make(chan struct{})
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) TrustedEmailProvider() bool {
|
||||
return c.trustedEmailProvider
|
||||
}
|
||||
|
||||
type LDAPIdentityProvider struct {
|
||||
serverHost string
|
||||
serverPort uint16
|
||||
useTLS bool
|
||||
useSSL bool
|
||||
baseDN string
|
||||
nameAttribute string
|
||||
emailAttribute string
|
||||
searchBeforeAuth bool
|
||||
searchFilter string
|
||||
searchScope int
|
||||
searchBindDN string
|
||||
searchBindPw string
|
||||
bindTemplate string
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func (m *LDAPIdentityProvider) LDAPConnect() (*ldap.Conn, error) {
|
||||
var err error
|
||||
var ldapConn *ldap.Conn
|
||||
|
||||
log.Debugf("LDAPConnect()")
|
||||
if m.useSSL {
|
||||
ldapConn, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", m.serverHost, m.serverPort), m.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
ldapConn, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", m.serverHost, m.serverPort))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if m.useTLS {
|
||||
err = ldapConn.StartTLS(m.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ldapConn, err
|
||||
}
|
||||
|
||||
func (m *LDAPIdentityProvider) ParseString(template, username string) string {
|
||||
result := template
|
||||
result = strings.Replace(result, "%u", username, -1)
|
||||
result = strings.Replace(result, "%b", m.baseDN, -1)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *LDAPIdentityProvider) Identity(username, password string) (*oidc.Identity, error) {
|
||||
var err error
|
||||
var bindDN, ldapUid, ldapName, ldapEmail string
|
||||
var ldapConn *ldap.Conn
|
||||
|
||||
ldapConn, err = m.LDAPConnect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ldapConn.Close()
|
||||
|
||||
if m.searchBeforeAuth {
|
||||
err = ldapConn.Bind(m.searchBindDN, m.searchBindPw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filter := m.ParseString(m.searchFilter, username)
|
||||
|
||||
attributes := []string{
|
||||
"entryDN",
|
||||
m.nameAttribute,
|
||||
m.emailAttribute,
|
||||
}
|
||||
|
||||
s := ldap.NewSearchRequest(m.baseDN, m.searchScope, ldap.NeverDerefAliases, 0, 0, false, filter, attributes, nil)
|
||||
|
||||
sr, err := ldapConn.Search(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(sr.Entries) == 0 {
|
||||
err = fmt.Errorf("Search returned no match. filter='%v' base='%v'", filter, m.baseDN)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bindDN = sr.Entries[0].GetAttributeValue("entryDN")
|
||||
ldapName = sr.Entries[0].GetAttributeValue(m.nameAttribute)
|
||||
ldapEmail = sr.Entries[0].GetAttributeValue(m.emailAttribute)
|
||||
|
||||
// drop to anonymous bind, prepare for bind as user
|
||||
err = ldapConn.Bind("", "")
|
||||
if err != nil {
|
||||
// unsupported or disallowed, reconnect
|
||||
log.Warningf("Re-connecting to LDAP Server after failure to bind anonymously: %v", err)
|
||||
ldapConn.Close()
|
||||
ldapConn, err = m.LDAPConnect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bindDN = m.ParseString(m.bindTemplate, username)
|
||||
}
|
||||
|
||||
// authenticate user
|
||||
err = ldapConn.Bind(bindDN, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ldapUid = bindDN
|
||||
|
||||
return &oidc.Identity{
|
||||
ID: ldapUid,
|
||||
Name: ldapName,
|
||||
Email: ldapEmail,
|
||||
}, nil
|
||||
}
|
94
connector/connector_ldap_test.go
Normal file
94
connector/connector_ldap_test.go
Normal file
|
@ -0,0 +1,94 @@
|
|||
package connector
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
)
|
||||
|
||||
var (
|
||||
ns url.URL
|
||||
lf oidc.LoginFunc
|
||||
templates *template.Template
|
||||
)
|
||||
|
||||
func init() {
|
||||
templates = template.New(LDAPLoginPageTemplateName)
|
||||
}
|
||||
|
||||
func TestLDAPConnectorConfigValidTLS(t *testing.T) {
|
||||
cc := LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
UseTLS: true,
|
||||
UseSSL: false,
|
||||
}
|
||||
|
||||
_, err := cc.Connector(ns, lf, templates)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLDAPConnectorConfigInvalidSSLandTLS(t *testing.T) {
|
||||
cc := LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
UseTLS: true,
|
||||
UseSSL: true,
|
||||
}
|
||||
|
||||
_, err := cc.Connector(ns, lf, templates)
|
||||
if err == nil {
|
||||
t.Fatal("Expected LDAPConnector initialization to fail when both TLS and SSL enabled.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLDAPConnectorConfigValidSearchScope(t *testing.T) {
|
||||
cc := LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
SearchScope: "one",
|
||||
}
|
||||
|
||||
_, err := cc.Connector(ns, lf, templates)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLDAPConnectorConfigInvalidSearchScope(t *testing.T) {
|
||||
cc := LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
SearchScope: "three",
|
||||
}
|
||||
|
||||
_, err := cc.Connector(ns, lf, templates)
|
||||
if err == nil {
|
||||
t.Fatal("Expected LDAPConnector initialization to fail when invalid value provided for SearchScope.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLDAPConnectorConfigInvalidCertFileNoKeyFile(t *testing.T) {
|
||||
cc := LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
CertFile: "/tmp/ldap.crt",
|
||||
}
|
||||
|
||||
_, err := cc.Connector(ns, lf, templates)
|
||||
if err == nil {
|
||||
t.Fatal("Expected LDAPConnector initialization to fail when CertFile specified without KeyFile.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLDAPConnectorConfigValidCertFileAndKeyFile(t *testing.T) {
|
||||
cc := LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
CertFile: "/tmp/ldap.crt",
|
||||
KeyFile: "/tmp/ldap.key",
|
||||
}
|
||||
|
||||
_, err := cc.Connector(ns, lf, templates)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -7,10 +7,7 @@ import (
|
|||
"net/url"
|
||||
"path"
|
||||
|
||||
phttp "github.com/coreos/dex/pkg/http"
|
||||
"github.com/coreos/dex/pkg/log"
|
||||
"github.com/coreos/dex/user"
|
||||
"github.com/coreos/go-oidc/oauth2"
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
)
|
||||
|
||||
|
@ -102,90 +99,6 @@ func (c *LocalConnector) TrustedEmailProvider() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func redirectPostError(w http.ResponseWriter, errorURL url.URL, q url.Values) {
|
||||
redirectURL := phttp.MergeQuery(errorURL, q)
|
||||
w.Header().Set("Location", redirectURL.String())
|
||||
w.WriteHeader(http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func handleLoginFunc(lf oidc.LoginFunc, tpl *template.Template, idp *LocalIdentityProvider, localErrorPath string, errorURL url.URL) http.HandlerFunc {
|
||||
handleGET := func(w http.ResponseWriter, r *http.Request, errMsg string) {
|
||||
q := r.URL.Query()
|
||||
sessionKey := q.Get("session_key")
|
||||
|
||||
p := &Page{PostURL: r.URL.String(), Name: "Local", SessionKey: sessionKey}
|
||||
if errMsg != "" {
|
||||
p.Error = true
|
||||
p.Message = errMsg
|
||||
}
|
||||
|
||||
if err := tpl.Execute(w, p); err != nil {
|
||||
phttp.WriteError(w, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
handlePOST := func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
msg := fmt.Sprintf("unable to parse form from body: %v", err)
|
||||
phttp.WriteError(w, http.StatusBadRequest, msg)
|
||||
return
|
||||
}
|
||||
|
||||
userid := r.PostForm.Get("userid")
|
||||
if userid == "" {
|
||||
handleGET(w, r, "missing email address")
|
||||
return
|
||||
}
|
||||
|
||||
password := r.PostForm.Get("password")
|
||||
if password == "" {
|
||||
handleGET(w, r, "missing password")
|
||||
return
|
||||
}
|
||||
|
||||
ident, err := idp.Identity(userid, password)
|
||||
log.Errorf("IDENTITY: err: %v", err)
|
||||
|
||||
if ident == nil || err != nil {
|
||||
handleGET(w, r, "invalid login")
|
||||
return
|
||||
}
|
||||
|
||||
q := r.URL.Query()
|
||||
sessionKey := r.FormValue("session_key")
|
||||
if sessionKey == "" {
|
||||
q.Set("error", oauth2.ErrorInvalidRequest)
|
||||
q.Set("error_description", "missing session_key")
|
||||
redirectPostError(w, errorURL, q)
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL, err := lf(*ident, sessionKey)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to log in %#v: %v", *ident, err)
|
||||
q.Set("error", oauth2.ErrorAccessDenied)
|
||||
q.Set("error_description", "login failed")
|
||||
redirectPostError(w, errorURL, q)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Location", redirectURL)
|
||||
w.WriteHeader(http.StatusFound)
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case "POST":
|
||||
handlePOST(w, r)
|
||||
case "GET":
|
||||
handleGET(w, r, "")
|
||||
default:
|
||||
w.Header().Set("Allow", "GET, POST")
|
||||
phttp.WriteError(w, http.StatusMethodNotAllowed, "GET and POST only acceptable methods")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type LocalIdentityProvider struct {
|
||||
PasswordInfoRepo user.PasswordInfoRepo
|
||||
UserRepo user.UserRepo
|
||||
|
|
|
@ -64,3 +64,7 @@ type ConnectorConfigRepo interface {
|
|||
All() ([]ConnectorConfig, error)
|
||||
GetConnectorByID(repo.Transaction, string) (ConnectorConfig, error)
|
||||
}
|
||||
|
||||
type IdentityProvider interface {
|
||||
Identity(email, password string) (*oidc.Identity, error)
|
||||
}
|
||||
|
|
97
connector/login_local.go
Normal file
97
connector/login_local.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package connector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
phttp "github.com/coreos/dex/pkg/http"
|
||||
"github.com/coreos/dex/pkg/log"
|
||||
"github.com/coreos/go-oidc/oauth2"
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
)
|
||||
|
||||
func redirectPostError(w http.ResponseWriter, errorURL url.URL, q url.Values) {
|
||||
redirectURL := phttp.MergeQuery(errorURL, q)
|
||||
w.Header().Set("Location", redirectURL.String())
|
||||
w.WriteHeader(http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func handleLoginFunc(lf oidc.LoginFunc, tpl *template.Template, idp IdentityProvider, localErrorPath string, errorURL url.URL) http.HandlerFunc {
|
||||
handleGET := func(w http.ResponseWriter, r *http.Request, errMsg string) {
|
||||
q := r.URL.Query()
|
||||
sessionKey := q.Get("session_key")
|
||||
|
||||
p := &Page{PostURL: r.URL.String(), Name: "Local", SessionKey: sessionKey}
|
||||
if errMsg != "" {
|
||||
p.Error = true
|
||||
p.Message = errMsg
|
||||
}
|
||||
|
||||
if err := tpl.Execute(w, p); err != nil {
|
||||
phttp.WriteError(w, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
handlePOST := func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
msg := fmt.Sprintf("unable to parse form from body: %v", err)
|
||||
phttp.WriteError(w, http.StatusBadRequest, msg)
|
||||
return
|
||||
}
|
||||
|
||||
userid := r.PostForm.Get("userid")
|
||||
if userid == "" {
|
||||
handleGET(w, r, "missing email address")
|
||||
return
|
||||
}
|
||||
|
||||
password := r.PostForm.Get("password")
|
||||
if password == "" {
|
||||
handleGET(w, r, "missing password")
|
||||
return
|
||||
}
|
||||
|
||||
ident, err := idp.Identity(userid, password)
|
||||
log.Errorf("IDENTITY: err: %v", err)
|
||||
|
||||
if ident == nil || err != nil {
|
||||
handleGET(w, r, "invalid login")
|
||||
return
|
||||
}
|
||||
|
||||
q := r.URL.Query()
|
||||
sessionKey := r.FormValue("session_key")
|
||||
if sessionKey == "" {
|
||||
q.Set("error", oauth2.ErrorInvalidRequest)
|
||||
q.Set("error_description", "missing session_key")
|
||||
redirectPostError(w, errorURL, q)
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL, err := lf(*ident, sessionKey)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to log in %#v: %v", *ident, err)
|
||||
q.Set("error", oauth2.ErrorAccessDenied)
|
||||
q.Set("error_description", "login failed")
|
||||
redirectPostError(w, errorURL, q)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Location", redirectURL)
|
||||
w.WriteHeader(http.StatusFound)
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case "POST":
|
||||
handlePOST(w, r)
|
||||
case "GET":
|
||||
handleGET(w, r, "")
|
||||
default:
|
||||
w.Header().Set("Allow", "GET, POST")
|
||||
phttp.WriteError(w, http.StatusMethodNotAllowed, "GET and POST only acceptable methods")
|
||||
}
|
||||
}
|
||||
}
|
207
functional/ldap_test.go
Normal file
207
functional/ldap_test.go
Normal file
|
@ -0,0 +1,207 @@
|
|||
package functional
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/dex/connector"
|
||||
"github.com/coreos/dex/repo"
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
"gopkg.in/ldap.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
ldapHost string
|
||||
ldapPort uint16
|
||||
ldapBindDN string
|
||||
ldapBindPw string
|
||||
)
|
||||
|
||||
func init() {
|
||||
ldapuri := os.Getenv("DEX_TEST_LDAP_URI")
|
||||
if ldapuri == "" {
|
||||
fmt.Println("Unable to proceed with empty env var " +
|
||||
"DEX_TEST_LDAP_URI")
|
||||
os.Exit(1)
|
||||
}
|
||||
u, err := url.Parse(ldapuri)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to parse DEX_TEST_LDAP_URI")
|
||||
os.Exit(1)
|
||||
}
|
||||
if strings.Index(u.RawQuery, "?") < 0 {
|
||||
fmt.Println("Unable to parse DEX_TEST_LDAP_URI")
|
||||
os.Exit(1)
|
||||
}
|
||||
extentions := make(map[string]string)
|
||||
kvs := strings.Split(strings.TrimLeft(u.RawQuery, "?"), ",")
|
||||
for i := range kvs {
|
||||
fmt.Println(kvs[i])
|
||||
kv := strings.Split(kvs[i], "=")
|
||||
if len(kv) < 2 {
|
||||
fmt.Println("Unable to parse DEX_TEST_LDAP_URI")
|
||||
os.Exit(1)
|
||||
}
|
||||
extentions[kv[0]] = kv[1]
|
||||
}
|
||||
hostport := strings.Split(u.Host, ":")
|
||||
port := 389
|
||||
if len(hostport) > 1 {
|
||||
port, _ = strconv.Atoi(hostport[1])
|
||||
}
|
||||
|
||||
ldapHost = hostport[0]
|
||||
ldapPort = uint16(port)
|
||||
|
||||
if len(extentions["bindname"]) > 0 {
|
||||
ldapBindDN, err = url.QueryUnescape(extentions["bindname"])
|
||||
if err != nil {
|
||||
fmt.Println("Unable to parse DEX_TEST_LDAP_URI")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if len(extentions["X-BINDPW"]) > 0 {
|
||||
ldapBindPw = extentions["X-BINDPW"]
|
||||
}
|
||||
}
|
||||
|
||||
func TestLDAPConnect(t *testing.T) {
|
||||
fmt.Println("ldapHost: ", ldapHost)
|
||||
fmt.Println("ldapPort: ", ldapPort)
|
||||
fmt.Println("ldapBindDN: ", ldapBindDN)
|
||||
fmt.Println("ldapBindPw: ", ldapBindPw)
|
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapHost, ldapPort))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = l.Bind(ldapBindDN, ldapBindPw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
l.Close()
|
||||
}
|
||||
|
||||
func TestConnectorLDAPConnectFail(t *testing.T) {
|
||||
var tx repo.Transaction
|
||||
var lf oidc.LoginFunc
|
||||
var ns url.URL
|
||||
|
||||
templates := template.New(connector.LDAPLoginPageTemplateName)
|
||||
|
||||
ccr := connector.NewConnectorConfigRepoFromConfigs(
|
||||
[]connector.ConnectorConfig{&connector.LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
ServerHost: ldapHost,
|
||||
ServerPort: ldapPort + 1,
|
||||
}},
|
||||
)
|
||||
cc, err := ccr.GetConnectorByID(tx, "ldap")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := cc.Connector(ns, lf, templates)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = c.Healthy()
|
||||
if err == nil {
|
||||
t.Fatal(fmt.Errorf("LDAPConnector.Healty() supposed to fail, but succeeded!"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectorLDAPConnectSuccess(t *testing.T) {
|
||||
var tx repo.Transaction
|
||||
var lf oidc.LoginFunc
|
||||
var ns url.URL
|
||||
|
||||
templates := template.New(connector.LDAPLoginPageTemplateName)
|
||||
|
||||
ccr := connector.NewConnectorConfigRepoFromConfigs(
|
||||
[]connector.ConnectorConfig{&connector.LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
ServerHost: ldapHost,
|
||||
ServerPort: ldapPort,
|
||||
}},
|
||||
)
|
||||
cc, err := ccr.GetConnectorByID(tx, "ldap")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := cc.Connector(ns, lf, templates)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = c.Healthy()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectorLDAPcaFilecertFileConnectTLS(t *testing.T) {
|
||||
var tx repo.Transaction
|
||||
var lf oidc.LoginFunc
|
||||
var ns url.URL
|
||||
|
||||
templates := template.New(connector.LDAPLoginPageTemplateName)
|
||||
|
||||
ccr := connector.NewConnectorConfigRepoFromConfigs(
|
||||
[]connector.ConnectorConfig{&connector.LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
ServerHost: ldapHost,
|
||||
ServerPort: ldapPort,
|
||||
UseTLS: true,
|
||||
CertFile: "/tmp/ldap.crt",
|
||||
KeyFile: "/tmp/ldap.key",
|
||||
CaFile: "/tmp/openldap-ca.pem",
|
||||
}},
|
||||
)
|
||||
cc, err := ccr.GetConnectorByID(tx, "ldap")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := cc.Connector(ns, lf, templates)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = c.Healthy()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectorLDAPcaFilecertFileConnectSSL(t *testing.T) {
|
||||
var tx repo.Transaction
|
||||
var lf oidc.LoginFunc
|
||||
var ns url.URL
|
||||
|
||||
templates := template.New(connector.LDAPLoginPageTemplateName)
|
||||
|
||||
ccr := connector.NewConnectorConfigRepoFromConfigs(
|
||||
[]connector.ConnectorConfig{&connector.LDAPConnectorConfig{
|
||||
ID: "ldap",
|
||||
ServerHost: ldapHost,
|
||||
ServerPort: ldapPort + 247, // 636
|
||||
UseSSL: true,
|
||||
CertFile: "/tmp/ldap.crt",
|
||||
KeyFile: "/tmp/ldap.key",
|
||||
CaFile: "/tmp/openldap-ca.pem",
|
||||
}},
|
||||
)
|
||||
cc, err := ccr.GetConnectorByID(tx, "ldap")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := cc.Connector(ns, lf, templates)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = c.Healthy()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -31,5 +31,24 @@
|
|||
"id": "bitbucket",
|
||||
"clientID": "${CLIENT_ID}",
|
||||
"clientSecret": "${CLIENT_SECRET}"
|
||||
},
|
||||
{
|
||||
"type": "ldap",
|
||||
"id": "ldap",
|
||||
"serverHost": "127.0.0.1",
|
||||
"serverPort": 389,
|
||||
"useTLS": true,
|
||||
"useSSL": false,
|
||||
"caFile": "/etc/ssl/certs/example_com_root.crt",
|
||||
"skipCertVerification": false,
|
||||
"baseDN": "ou=People,dc=example,dc=com",
|
||||
"nameAttribute": "cn",
|
||||
"emailAttribute": "mail",
|
||||
"searchBeforeAuth": true,
|
||||
"searchFilter": "(mail=%u)",
|
||||
"searchScope": "one",
|
||||
"searchBindDN": "searchuser",
|
||||
"searchBindPw": "supersecret",
|
||||
"bindTemplate": "uid=%u,%b"
|
||||
}
|
||||
]
|
||||
|
|
30
static/html/ldap-login.html
Normal file
30
static/html/ldap-login.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{{ template "header.html" }}
|
||||
|
||||
<div class="panel">
|
||||
<h2 class="heading">Log in to Your Account</h2>
|
||||
<form method="post" action="{{.PostURL}}">
|
||||
<div class="form-row">
|
||||
LDAP
|
||||
<div class="input-desc">
|
||||
<label for="userid">Username</label>
|
||||
</div>
|
||||
<input tabindex="1" required id="userid" name="userid" type="text" class="input-box" placeholder="username" autofocus/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="input-desc">
|
||||
<label for="password">Password</label>
|
||||
<span class="subtle-text input-label-right">Forgot? <a href="/send-reset-password?session_key={{ .SessionKey }}">Reset Password</a></span>
|
||||
</div>
|
||||
<input tabindex="2" required id="password" name="password" type="password" class="input-box" placeholder="password"/>
|
||||
</div>
|
||||
|
||||
{{ if .Error }}
|
||||
<div class="error-box">{{ .Message }}</div>
|
||||
{{ end }}
|
||||
|
||||
<button tabindex="3" type="submit" class="btn btn-primary">Login</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{ template "footer.html" }}
|
Reference in a new issue