Some refactoring

This commit is contained in:
Christopher Speller
2015-12-08 13:38:43 -05:00
parent 4f881046bf
commit 58358ddd7c
114 changed files with 6761 additions and 170 deletions

10
Godeps/Godeps.json generated
View File

@@ -29,6 +29,11 @@
"Comment": "v1.7-148-gc0e2e1a",
"Rev": "c0e2e1a7adaa00e47c4f6ae8faaad6991a4570ac"
},
{
"ImportPath": "github.com/go-ldap/ldap",
"Comment": "v2.2",
"Rev": "e9a325d64989e2844be629682cb085d2c58eef8d"
},
{
"ImportPath": "github.com/go-sql-driver/mysql",
"Comment": "v1.2-125-gd512f20",
@@ -113,6 +118,11 @@
"ImportPath": "golang.org/x/image/tiff",
"Rev": "baddd3465a05d84a6d8d3507547a91cb188c81ea"
},
{
"ImportPath": "gopkg.in/asn1-ber.v1",
"Comment": "v1.1",
"Rev": "4e86f4367175e39f69d9358a5f17b4dda270378d"
},
{
"ImportPath": "gopkg.in/fsnotify.v1",
"Comment": "v1.2.5",

View File

View 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/github.com/go-ldap/ldap/LICENSE generated vendored Normal file
View 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.

View 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/github.com/go-ldap/ldap/add.go generated vendored Normal file
View 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/github.com/go-ldap/ldap/bind.go generated vendored Normal file
View 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
}

View 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)
}

View 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/github.com/go-ldap/ldap/conn.go generated vendored Normal file
View 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
}
}
}

View 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/github.com/go-ldap/ldap/debug.go generated vendored Normal file
View 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/github.com/go-ldap/ldap/del.go generated vendored Normal file
View 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/github.com/go-ldap/ldap/dn.go generated vendored Normal file
View 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
}

View File

@@ -0,0 +1,70 @@
package ldap_test
import (
"reflect"
"testing"
"gopkg.in/ldap.v2"
)
func TestSuccessfulDNParsing(t *testing.T) {
testcases := map[string]ldap.DN{
"": ldap.DN{[]*ldap.RelativeDN{}},
"cn=Jim\\2C \\22Hasse Hö\\22 Hansson!,dc=dummy,dc=com": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"cn", "Jim, \"Hasse Hö\" Hansson!"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "dummy"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "com"}}}}},
"UID=jsmith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"UID", "jsmith"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"OU=Sales+CN=J. Smith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{
&ldap.AttributeTypeAndValue{"OU", "Sales"},
&ldap.AttributeTypeAndValue{"CN", "J. Smith"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"1.3.6.1.4.1.1466.0=#04024869": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}}}},
"1.3.6.1.4.1.1466.0=#04024869,DC=net": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"CN=Lu\\C4\\8Di\\C4\\87": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}},
}
for test, answer := range testcases {
dn, err := ldap.ParseDN(test)
if err != nil {
t.Errorf(err.Error())
continue
}
if !reflect.DeepEqual(dn, &answer) {
t.Errorf("Parsed DN %s is not equal to the expected structure", test)
for _, rdn := range dn.RDNs {
for _, attribs := range rdn.Attributes {
t.Logf("#%v\n", attribs)
}
}
}
}
}
func TestErrorDNParsing(t *testing.T) {
testcases := map[string]string{
"*": "DN ended with incomplete type, value pair",
"cn=Jim\\0Test": "Failed to decode escaped character: encoding/hex: invalid byte: U+0054 'T'",
"cn=Jim\\0": "Got corrupted escaped character",
"DC=example,=net": "DN ended with incomplete type, value pair",
"1=#0402486": "Failed to decode BER encoding: encoding/hex: odd length hex string",
}
for test, answer := range testcases {
_, err := ldap.ParseDN(test)
if err == nil {
t.Errorf("Expected %s to fail parsing but succeeded\n", test)
} else if err.Error() != answer {
t.Errorf("Unexpected error on %s:\n%s\nvs.\n%s\n", test, answer, err.Error())
}
}
}

4
Godeps/_workspace/src/github.com/go-ldap/ldap/doc.go generated vendored Normal file
View File

@@ -0,0 +1,4 @@
/*
Package ldap provides basic LDAP v3 functionality.
*/
package ldap

137
Godeps/_workspace/src/github.com/go-ldap/ldap/error.go generated vendored Normal file
View 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
}

View File

@@ -0,0 +1,305 @@
package ldap_test
import (
"crypto/tls"
"fmt"
"log"
"gopkg.in/ldap.v2"
)
// ExampleConn_Bind demonstrates how to bind a connection to an ldap user
// allowing access to restricted attrabutes that user has access to
func ExampleConn_Bind() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
err = l.Bind("cn=read-only-admin,dc=example,dc=com", "password")
if err != nil {
log.Fatal(err)
}
}
// ExampleConn_Search demonstrates how to use the search interface
func ExampleConn_Search() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
searchRequest := ldap.NewSearchRequest(
"dc=example,dc=com", // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
"(&(objectClass=organizationalPerson))", // The filter to apply
[]string{"dn", "cn"}, // A list attributes to retrieve
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
log.Fatal(err)
}
for _, entry := range sr.Entries {
fmt.Printf("%s: %v\n", entry.DN, entry.GetAttributeValue("cn"))
}
}
// ExampleStartTLS demonstrates how to start a TLS connection
func ExampleConn_StartTLS() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
// Reconnect with TLS
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
log.Fatal(err)
}
// Opertations via l are now encrypted
}
// ExampleConn_Compare demonstrates how to comapre an attribute with a value
func ExampleConn_Compare() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
matched, err := l.Compare("cn=user,dc=example,dc=com", "uid", "someuserid")
if err != nil {
log.Fatal(err)
}
fmt.Println(matched)
}
func ExampleConn_PasswordModify_admin() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
err = l.Bind("cn=admin,dc=example,dc=com", "password")
if err != nil {
log.Fatal(err)
}
passwordModifyRequest := ldap.NewPasswordModifyRequest("cn=user,dc=example,dc=com", "", "NewPassword")
_, err = l.PasswordModify(passwordModifyRequest)
if err != nil {
log.Fatalf("Password could not be changed: %s", err.Error())
}
}
func ExampleConn_PasswordModify_generatedPassword() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
err = l.Bind("cn=user,dc=example,dc=com", "password")
if err != nil {
log.Fatal(err)
}
passwordModifyRequest := ldap.NewPasswordModifyRequest("", "OldPassword", "")
passwordModifyResponse, err := l.PasswordModify(passwordModifyRequest)
if err != nil {
log.Fatalf("Password could not be changed: %s", err.Error())
}
generatedPassword := passwordModifyResponse.GeneratedPassword
log.Printf("Generated password: %s\n", generatedPassword)
}
func ExampleConn_PasswordModify_setNewPassword() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
err = l.Bind("cn=user,dc=example,dc=com", "password")
if err != nil {
log.Fatal(err)
}
passwordModifyRequest := ldap.NewPasswordModifyRequest("", "OldPassword", "NewPassword")
_, err = l.PasswordModify(passwordModifyRequest)
if err != nil {
log.Fatalf("Password could not be changed: %s", err.Error())
}
}
func ExampleConn_Modify() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
// Add a description, and replace the mail attributes
modify := ldap.NewModifyRequest("cn=user,dc=example,dc=com")
modify.Add("description", []string{"An example user"})
modify.Replace("mail", []string{"user@example.org"})
err = l.Modify(modify)
if err != nil {
log.Fatal(err)
}
}
// Example User Authentication shows how a typical application can verify a login attempt
func Example_userAuthentication() {
// The username and password we want to check
username := "someuser"
password := "userpassword"
bindusername := "readonly"
bindpassword := "password"
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
// Reconnect with TLS
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
log.Fatal(err)
}
// First bind with a read only user
err = l.Bind(bindusername, bindpassword)
if err != nil {
log.Fatal(err)
}
// Search for the given username
searchRequest := ldap.NewSearchRequest(
"dc=example,dc=com",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=organizationalPerson)&(uid=%s))", username),
[]string{"dn"},
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
log.Fatal(err)
}
if len(sr.Entries) != 1 {
log.Fatal("User does not exist or too many entries returned")
}
userdn := sr.Entries[0].DN
// Bind as the user to verify their password
err = l.Bind(userdn, password)
if err != nil {
log.Fatal(err)
}
// Rebind as the read only user for any futher queries
err = l.Bind(bindusername, bindpassword)
if err != nil {
log.Fatal(err)
}
}
func Example_beherappolicy() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
controls := []ldap.Control{}
controls = append(controls, ldap.NewControlBeheraPasswordPolicy())
bindRequest := ldap.NewSimpleBindRequest("cn=admin,dc=example,dc=com", "password", controls)
r, err := l.SimpleBind(bindRequest)
ppolicyControl := ldap.FindControl(r.Controls, ldap.ControlTypeBeheraPasswordPolicy)
var ppolicy *ldap.ControlBeheraPasswordPolicy
if ppolicyControl != nil {
ppolicy = ppolicyControl.(*ldap.ControlBeheraPasswordPolicy)
} else {
log.Printf("ppolicyControl response not avaliable.\n")
}
if err != nil {
errStr := "ERROR: Cannot bind: " + err.Error()
if ppolicy != nil && ppolicy.Error >= 0 {
errStr += ":" + ppolicy.ErrorString
}
log.Print(errStr)
} else {
logStr := "Login Ok"
if ppolicy != nil {
if ppolicy.Expire >= 0 {
logStr += fmt.Sprintf(". Password expires in %d seconds\n", ppolicy.Expire)
} else if ppolicy.Grace >= 0 {
logStr += fmt.Sprintf(". Password expired, %d grace logins remain\n", ppolicy.Grace)
}
}
log.Print(logStr)
}
}
func Example_vchuppolicy() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
l.Debug = true
bindRequest := ldap.NewSimpleBindRequest("cn=admin,dc=example,dc=com", "password", nil)
r, err := l.SimpleBind(bindRequest)
passwordMustChangeControl := ldap.FindControl(r.Controls, ldap.ControlTypeVChuPasswordMustChange)
var passwordMustChange *ldap.ControlVChuPasswordMustChange
if passwordMustChangeControl != nil {
passwordMustChange = passwordMustChangeControl.(*ldap.ControlVChuPasswordMustChange)
}
if passwordMustChange != nil && passwordMustChange.MustChange {
log.Printf("Password Must be changed.\n")
}
passwordWarningControl := ldap.FindControl(r.Controls, ldap.ControlTypeVChuPasswordWarning)
var passwordWarning *ldap.ControlVChuPasswordWarning
if passwordWarningControl != nil {
passwordWarning = passwordWarningControl.(*ldap.ControlVChuPasswordWarning)
} else {
log.Printf("ppolicyControl response not available.\n")
}
if err != nil {
log.Print("ERROR: Cannot bind: " + err.Error())
} else {
logStr := "Login Ok"
if passwordWarning != nil {
if passwordWarning.Expire >= 0 {
logStr += fmt.Sprintf(". Password expires in %d seconds\n", passwordWarning.Expire)
}
}
log.Print(logStr)
}
}

456
Godeps/_workspace/src/github.com/go-ldap/ldap/filter.go generated vendored Normal file
View 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
}

View File

@@ -0,0 +1,248 @@
package ldap_test
import (
"strings"
"testing"
"gopkg.in/asn1-ber.v1"
"gopkg.in/ldap.v2"
)
type compileTest struct {
filterStr string
expectedFilter string
expectedType int
expectedErr string
}
var testFilters = []compileTest{
compileTest{
filterStr: "(&(sn=Miller)(givenName=Bob))",
expectedFilter: "(&(sn=Miller)(givenName=Bob))",
expectedType: ldap.FilterAnd,
},
compileTest{
filterStr: "(|(sn=Miller)(givenName=Bob))",
expectedFilter: "(|(sn=Miller)(givenName=Bob))",
expectedType: ldap.FilterOr,
},
compileTest{
filterStr: "(!(sn=Miller))",
expectedFilter: "(!(sn=Miller))",
expectedType: ldap.FilterNot,
},
compileTest{
filterStr: "(sn=Miller)",
expectedFilter: "(sn=Miller)",
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: "(sn=Mill*)",
expectedFilter: "(sn=Mill*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*Mill)",
expectedFilter: "(sn=*Mill)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*Mill*)",
expectedFilter: "(sn=*Mill*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*i*le*)",
expectedFilter: "(sn=*i*le*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=Mi*l*r)",
expectedFilter: "(sn=Mi*l*r)",
expectedType: ldap.FilterSubstrings,
},
// substring filters escape properly
compileTest{
filterStr: `(sn=Mi*함*r)`,
expectedFilter: `(sn=Mi*\ed\95\a8*r)`,
expectedType: ldap.FilterSubstrings,
},
// already escaped substring filters don't get double-escaped
compileTest{
filterStr: `(sn=Mi*\ed\95\a8*r)`,
expectedFilter: `(sn=Mi*\ed\95\a8*r)`,
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=Mi*le*)",
expectedFilter: "(sn=Mi*le*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*i*ler)",
expectedFilter: "(sn=*i*ler)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn>=Miller)",
expectedFilter: "(sn>=Miller)",
expectedType: ldap.FilterGreaterOrEqual,
},
compileTest{
filterStr: "(sn<=Miller)",
expectedFilter: "(sn<=Miller)",
expectedType: ldap.FilterLessOrEqual,
},
compileTest{
filterStr: "(sn=*)",
expectedFilter: "(sn=*)",
expectedType: ldap.FilterPresent,
},
compileTest{
filterStr: "(sn~=Miller)",
expectedFilter: "(sn~=Miller)",
expectedType: ldap.FilterApproxMatch,
},
compileTest{
filterStr: `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`,
expectedFilter: `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`,
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: `(objectGUID=абвгдеёжзийклмнопрстуфхцчшщъыьэюя)`,
expectedFilter: `(objectGUID=\d0\b0\d0\b1\d0\b2\d0\b3\d0\b4\d0\b5\d1\91\d0\b6\d0\b7\d0\b8\d0\b9\d0\ba\d0\bb\d0\bc\d0\bd\d0\be\d0\bf\d1\80\d1\81\d1\82\d1\83\d1\84\d1\85\d1\86\d1\87\d1\88\d1\89\d1\8a\d1\8b\d1\8c\d1\8d\d1\8e\d1\8f)`,
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: `(objectGUID=함수목록)`,
expectedFilter: `(objectGUID=\ed\95\a8\ec\88\98\eb\aa\a9\eb\a1\9d)`,
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: `(objectGUID=`,
expectedFilter: ``,
expectedType: 0,
expectedErr: "unexpected end of filter",
},
compileTest{
filterStr: `(objectGUID=함수목록`,
expectedFilter: ``,
expectedType: 0,
expectedErr: "unexpected end of filter",
},
compileTest{
filterStr: `(&(objectclass=inetorgperson)(cn=中文))`,
expectedFilter: `(&(objectclass=inetorgperson)(cn=\e4\b8\ad\e6\96\87))`,
expectedType: 0,
},
// attr extension
compileTest{
filterStr: `(memberOf:=foo)`,
expectedFilter: `(memberOf:=foo)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+named matching rule extension
compileTest{
filterStr: `(memberOf:test:=foo)`,
expectedFilter: `(memberOf:test:=foo)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+oid matching rule extension
compileTest{
filterStr: `(cn:1.2.3.4.5:=Fred Flintstone)`,
expectedFilter: `(cn:1.2.3.4.5:=Fred Flintstone)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+dn+oid matching rule extension
compileTest{
filterStr: `(sn:dn:2.4.6.8.10:=Barney Rubble)`,
expectedFilter: `(sn:dn:2.4.6.8.10:=Barney Rubble)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+dn extension
compileTest{
filterStr: `(o:dn:=Ace Industry)`,
expectedFilter: `(o:dn:=Ace Industry)`,
expectedType: ldap.FilterExtensibleMatch,
},
// dn extension
compileTest{
filterStr: `(:dn:2.4.6.8.10:=Dino)`,
expectedFilter: `(:dn:2.4.6.8.10:=Dino)`,
expectedType: ldap.FilterExtensibleMatch,
},
compileTest{
filterStr: `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`,
expectedFilter: `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`,
expectedType: ldap.FilterExtensibleMatch,
},
// compileTest{ filterStr: "()", filterType: FilterExtensibleMatch },
}
var testInvalidFilters = []string{
`(objectGUID=\zz)`,
`(objectGUID=\a)`,
}
func TestFilter(t *testing.T) {
// Test Compiler and Decompiler
for _, i := range testFilters {
filter, err := ldap.CompileFilter(i.filterStr)
if err != nil {
if i.expectedErr == "" || !strings.Contains(err.Error(), i.expectedErr) {
t.Errorf("Problem compiling '%s' - '%v' (expected error to contain '%v')", i.filterStr, err, i.expectedErr)
}
} else if filter.Tag != ber.Tag(i.expectedType) {
t.Errorf("%q Expected %q got %q", i.filterStr, ldap.FilterMap[uint64(i.expectedType)], ldap.FilterMap[uint64(filter.Tag)])
} else {
o, err := ldap.DecompileFilter(filter)
if err != nil {
t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error())
} else if i.expectedFilter != o {
t.Errorf("%q expected, got %q", i.expectedFilter, o)
}
}
}
}
func TestInvalidFilter(t *testing.T) {
for _, filterStr := range testInvalidFilters {
if _, err := ldap.CompileFilter(filterStr); err == nil {
t.Errorf("Problem compiling %s - expected err", filterStr)
}
}
}
func BenchmarkFilterCompile(b *testing.B) {
b.StopTimer()
filters := make([]string, len(testFilters))
// Test Compiler and Decompiler
for idx, i := range testFilters {
filters[idx] = i.filterStr
}
maxIdx := len(filters)
b.StartTimer()
for i := 0; i < b.N; i++ {
ldap.CompileFilter(filters[i%maxIdx])
}
}
func BenchmarkFilterDecompile(b *testing.B) {
b.StopTimer()
filters := make([]*ber.Packet, len(testFilters))
// Test Compiler and Decompiler
for idx, i := range testFilters {
filters[idx], _ = ldap.CompileFilter(i.filterStr)
}
maxIdx := len(filters)
b.StartTimer()
for i := 0; i < b.N; i++ {
ldap.DecompileFilter(filters[i%maxIdx])
}
}

286
Godeps/_workspace/src/github.com/go-ldap/ldap/ldap.go generated vendored Normal file
View 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)
}

View File

@@ -0,0 +1,249 @@
package ldap_test
import (
"crypto/tls"
"fmt"
"testing"
"gopkg.in/ldap.v2"
)
var ldapServer = "ldap.itd.umich.edu"
var ldapPort = uint16(389)
var ldapTLSPort = uint16(636)
var baseDN = "dc=umich,dc=edu"
var filter = []string{
"(cn=cis-fac)",
"(&(owner=*)(cn=cis-fac))",
"(&(objectclass=rfc822mailgroup)(cn=*Computer*))",
"(&(objectclass=rfc822mailgroup)(cn=*Mathematics*))"}
var attributes = []string{
"cn",
"description"}
func TestDial(t *testing.T) {
fmt.Printf("TestDial: starting...\n")
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
fmt.Printf("TestDial: finished...\n")
}
func TestDialTLS(t *testing.T) {
fmt.Printf("TestDialTLS: starting...\n")
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
fmt.Printf("TestDialTLS: finished...\n")
}
func TestStartTLS(t *testing.T) {
fmt.Printf("TestStartTLS: starting...\n")
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Errorf(err.Error())
return
}
fmt.Printf("TestStartTLS: finished...\n")
}
func TestSearch(t *testing.T) {
fmt.Printf("TestSearch: starting...\n")
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
searchRequest := ldap.NewSearchRequest(
baseDN,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[0],
attributes,
nil)
sr, err := l.Search(searchRequest)
if err != nil {
t.Errorf(err.Error())
return
}
fmt.Printf("TestSearch: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries))
}
func TestSearchStartTLS(t *testing.T) {
fmt.Printf("TestSearchStartTLS: starting...\n")
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
searchRequest := ldap.NewSearchRequest(
baseDN,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[0],
attributes,
nil)
sr, err := l.Search(searchRequest)
if err != nil {
t.Errorf(err.Error())
return
}
fmt.Printf("TestSearchStartTLS: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries))
fmt.Printf("TestSearchStartTLS: upgrading with startTLS\n")
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Errorf(err.Error())
return
}
sr, err = l.Search(searchRequest)
if err != nil {
t.Errorf(err.Error())
return
}
fmt.Printf("TestSearchStartTLS: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries))
}
func TestSearchWithPaging(t *testing.T) {
fmt.Printf("TestSearchWithPaging: starting...\n")
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
err = l.Bind("", "")
if err != nil {
t.Errorf(err.Error())
return
}
searchRequest := ldap.NewSearchRequest(
baseDN,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[2],
attributes,
nil)
sr, err := l.SearchWithPaging(searchRequest, 5)
if err != nil {
t.Errorf(err.Error())
return
}
fmt.Printf("TestSearchWithPaging: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries))
}
func searchGoroutine(t *testing.T, l *ldap.Conn, results chan *ldap.SearchResult, i int) {
searchRequest := ldap.NewSearchRequest(
baseDN,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[i],
attributes,
nil)
sr, err := l.Search(searchRequest)
if err != nil {
t.Errorf(err.Error())
results <- nil
return
}
results <- sr
}
func testMultiGoroutineSearch(t *testing.T, TLS bool, startTLS bool) {
fmt.Printf("TestMultiGoroutineSearch: starting...\n")
var l *ldap.Conn
var err error
if TLS {
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
} else {
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
if startTLS {
fmt.Printf("TestMultiGoroutineSearch: using StartTLS...\n")
err := l.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Errorf(err.Error())
return
}
}
}
results := make([]chan *ldap.SearchResult, len(filter))
for i := range filter {
results[i] = make(chan *ldap.SearchResult)
go searchGoroutine(t, l, results[i], i)
}
for i := range filter {
sr := <-results[i]
if sr == nil {
t.Errorf("Did not receive results from goroutine for %q", filter[i])
} else {
fmt.Printf("TestMultiGoroutineSearch(%d): %s -> num of entries = %d\n", i, filter[i], len(sr.Entries))
}
}
}
func TestMultiGoroutineSearch(t *testing.T) {
testMultiGoroutineSearch(t, false, false)
testMultiGoroutineSearch(t, true, true)
testMultiGoroutineSearch(t, false, true)
}
func TestEscapeFilter(t *testing.T) {
if got, want := ldap.EscapeFilter("a\x00b(c)d*e\\f"), `a\00b\28c\29d\2ae\5cf`; got != want {
t.Errorf("Got %s, expected %s", want, got)
}
if got, want := ldap.EscapeFilter("Lučić"), `Lu\c4\8di\c4\87`; got != want {
t.Errorf("Got %s, expected %s", want, got)
}
}
func TestCompare(t *testing.T) {
fmt.Printf("TestCompare: starting...\n")
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Fatal(err.Error())
}
defer l.Close()
dn := "cn=math mich,ou=User Groups,ou=Groups,dc=umich,dc=edu"
attribute := "cn"
value := "math mich"
sr, err := l.Compare(dn, attribute, value)
if err != nil {
t.Errorf(err.Error())
return
}
fmt.Printf("TestCompare: -> %v\n", sr)
}

156
Godeps/_workspace/src/github.com/go-ldap/ldap/modify.go generated vendored Normal file
View 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
}

View 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/github.com/go-ldap/ldap/search.go generated vendored Normal file
View 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
}

View File

@@ -0,0 +1,31 @@
package ldap
import (
"reflect"
"testing"
)
// TestNewEntry tests that repeated calls to NewEntry return the same value with the same input
func TestNewEntry(t *testing.T) {
dn := "testDN"
attributes := map[string][]string{
"alpha": {"value"},
"beta": {"value"},
"gamma": {"value"},
"delta": {"value"},
"epsilon": {"value"},
}
exectedEntry := NewEntry(dn, attributes)
iteration := 0
for {
if iteration == 100 {
break
}
testEntry := NewEntry(dn, attributes)
if !reflect.DeepEqual(exectedEntry, testEntry) {
t.Fatalf("consequent calls to NewEntry did not yield the same result:\n\texpected:\n\t%s\n\tgot:\n\t%s\n", exectedEntry, testEntry)
}
iteration = iteration + 1
}
}

15
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/.travis.yml generated vendored Normal file
View 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
View 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
View 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
View 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
}

168
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/ber_test.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
package ber
import (
"bytes"
"math"
"io"
"testing"
)
func TestEncodeDecodeInteger(t *testing.T) {
for _, v := range []int64{0, 10, 128, 1024, math.MaxInt64, -1, -100, -128, -1024, math.MinInt64} {
enc := encodeInteger(v)
dec, err := parseInt64(enc)
if err != nil {
t.Fatalf("Error decoding %d : %s", v, err)
}
if v != dec {
t.Error("TestEncodeDecodeInteger failed for %d (got %d)", v, dec)
}
}
}
func TestBoolean(t *testing.T) {
var value bool = true
packet := NewBoolean(ClassUniversal, TypePrimitive, TagBoolean, value, "first Packet, True")
newBoolean, ok := packet.Value.(bool)
if !ok || newBoolean != value {
t.Error("error during creating packet")
}
encodedPacket := packet.Bytes()
newPacket := DecodePacket(encodedPacket)
newBoolean, ok = newPacket.Value.(bool)
if !ok || newBoolean != value {
t.Error("error during decoding packet")
}
}
func TestInteger(t *testing.T) {
var value int64 = 10
packet := NewInteger(ClassUniversal, TypePrimitive, TagInteger, value, "Integer, 10")
{
newInteger, ok := packet.Value.(int64)
if !ok || newInteger != value {
t.Error("error creating packet")
}
}
encodedPacket := packet.Bytes()
newPacket := DecodePacket(encodedPacket)
{
newInteger, ok := newPacket.Value.(int64)
if !ok || int64(newInteger) != value {
t.Error("error decoding packet")
}
}
}
func TestString(t *testing.T) {
var value string = "Hic sunt dracones"
packet := NewString(ClassUniversal, TypePrimitive, TagOctetString, value, "String")
newValue, ok := packet.Value.(string)
if !ok || newValue != value {
t.Error("error during creating packet")
}
encodedPacket := packet.Bytes()
newPacket := DecodePacket(encodedPacket)
newValue, ok = newPacket.Value.(string)
if !ok || newValue != value {
t.Error("error during decoding packet")
}
}
func TestSequenceAndAppendChild(t *testing.T) {
values := []string{
"HIC SVNT LEONES",
"Iñtërnâtiônàlizætiøn",
"Terra Incognita",
}
sequence := NewSequence("a sequence")
for _, s := range values {
sequence.AppendChild(NewString(ClassUniversal, TypePrimitive, TagOctetString, s, "String"))
}
if len(sequence.Children) != len(values) {
t.Errorf("wrong length for children array should be %d, got %d", len(values), len(sequence.Children))
}
encodedSequence := sequence.Bytes()
decodedSequence := DecodePacket(encodedSequence)
if len(decodedSequence.Children) != len(values) {
t.Errorf("wrong length for children array should be %d => %d", len(values), len(decodedSequence.Children))
}
for i, s := range values {
if decodedSequence.Children[i].Value.(string) != s {
t.Errorf("expected %d to be %q, got %q", i, s, decodedSequence.Children[i].Value.(string))
}
}
}
func TestReadPacket(t *testing.T) {
packet := NewString(ClassUniversal, TypePrimitive, TagOctetString, "Ad impossibilia nemo tenetur", "string")
var buffer io.ReadWriter
buffer = new(bytes.Buffer)
buffer.Write(packet.Bytes())
newPacket, err := ReadPacket(buffer)
if err != nil {
t.Error("error during ReadPacket", err)
}
newPacket.ByteValue = nil
if !bytes.Equal(newPacket.ByteValue, packet.ByteValue) {
t.Error("packets should be the same")
}
}
func TestBinaryInteger(t *testing.T) {
// data src : http://luca.ntop.org/Teaching/Appunti/asn1.html 5.7
var data = []struct {
v int64
e []byte
}{
{v: 0, e: []byte{0x02, 0x01, 0x00}},
{v: 127, e: []byte{0x02, 0x01, 0x7F}},
{v: 128, e: []byte{0x02, 0x02, 0x00, 0x80}},
{v: 256, e: []byte{0x02, 0x02, 0x01, 0x00}},
{v: -128, e: []byte{0x02, 0x01, 0x80}},
{v: -129, e: []byte{0x02, 0x02, 0xFF, 0x7F}},
{v: math.MaxInt64, e: []byte{0x02, 0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
{v: math.MinInt64, e: []byte{0x02, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
}
for _, d := range data {
if b := NewInteger(ClassUniversal, TypePrimitive, TagInteger, int64(d.v), "").Bytes(); !bytes.Equal(d.e, b) {
t.Errorf("Wrong binary generated for %d : got % X, expected % X", d.v, b, d.e)
}
}
}
func TestBinaryOctetString(t *testing.T) {
// data src : http://luca.ntop.org/Teaching/Appunti/asn1.html 5.10
if !bytes.Equal([]byte{0x04, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, NewString(ClassUniversal, TypePrimitive, TagOctetString, "\x01\x23\x45\x67\x89\xab\xcd\xef", "").Bytes()) {
t.Error("wrong binary generated")
}
}

View 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
View 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
}

View File

@@ -0,0 +1,135 @@
package ber
import (
"bytes"
"io"
"testing"
)
func TestReadHeader(t *testing.T) {
testcases := map[string]struct {
Data []byte
ExpectedIdentifier Identifier
ExpectedLength int
ExpectedBytesRead int
ExpectedError string
}{
"empty": {
Data: []byte{},
ExpectedIdentifier: Identifier{},
ExpectedLength: 0,
ExpectedBytesRead: 0,
ExpectedError: io.ErrUnexpectedEOF.Error(),
},
"valid short form": {
Data: []byte{
byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString),
127,
},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypePrimitive,
Tag: TagCharacterString,
},
ExpectedLength: 127,
ExpectedBytesRead: 2,
ExpectedError: "",
},
"valid long form": {
Data: []byte{
// 2-byte encoding of tag
byte(ClassUniversal) | byte(TypePrimitive) | byte(HighTag),
byte(TagCharacterString),
// 2-byte encoding of length
LengthLongFormBitmask | 1,
127,
},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypePrimitive,
Tag: TagCharacterString,
},
ExpectedLength: 127,
ExpectedBytesRead: 4,
ExpectedError: "",
},
"valid indefinite length": {
Data: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString),
LengthLongFormBitmask,
},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagCharacterString,
},
ExpectedLength: LengthIndefinite,
ExpectedBytesRead: 2,
ExpectedError: "",
},
"invalid indefinite length": {
Data: []byte{
byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString),
LengthLongFormBitmask,
},
ExpectedIdentifier: Identifier{},
ExpectedLength: 0,
ExpectedBytesRead: 2,
ExpectedError: "indefinite length used with primitive type",
},
}
for k, tc := range testcases {
reader := bytes.NewBuffer(tc.Data)
identifier, length, read, err := readHeader(reader)
if err != nil {
if tc.ExpectedError == "" {
t.Errorf("%s: unexpected error: %v", k, err)
} else if err.Error() != tc.ExpectedError {
t.Errorf("%s: expected error %v, got %v", k, tc.ExpectedError, err)
}
} else if tc.ExpectedError != "" {
t.Errorf("%s: expected error %v, got none", k, tc.ExpectedError)
continue
}
if read != tc.ExpectedBytesRead {
t.Errorf("%s: expected read %d, got %d", k, tc.ExpectedBytesRead, read)
}
if identifier.ClassType != tc.ExpectedIdentifier.ClassType {
t.Errorf("%s: expected class type %d (%s), got %d (%s)", k,
tc.ExpectedIdentifier.ClassType,
ClassMap[tc.ExpectedIdentifier.ClassType],
identifier.ClassType,
ClassMap[identifier.ClassType],
)
}
if identifier.TagType != tc.ExpectedIdentifier.TagType {
t.Errorf("%s: expected tag type %d (%s), got %d (%s)", k,
tc.ExpectedIdentifier.TagType,
TypeMap[tc.ExpectedIdentifier.TagType],
identifier.TagType,
TypeMap[identifier.TagType],
)
}
if identifier.Tag != tc.ExpectedIdentifier.Tag {
t.Errorf("%s: expected tag %d (%s), got %d (%s)", k,
tc.ExpectedIdentifier.Tag,
tagMap[tc.ExpectedIdentifier.Tag],
identifier.Tag,
tagMap[identifier.Tag],
)
}
if length != tc.ExpectedLength {
t.Errorf("%s: expected length %d, got %d", k, tc.ExpectedLength, length)
}
}
}

View 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
}

View File

@@ -0,0 +1,344 @@
package ber
import (
"bytes"
"io"
"math"
"testing"
)
func TestReadIdentifier(t *testing.T) {
testcases := map[string]struct {
Data []byte
ExpectedIdentifier Identifier
ExpectedBytesRead int
ExpectedError string
}{
"empty": {
Data: []byte{},
ExpectedBytesRead: 0,
ExpectedError: io.ErrUnexpectedEOF.Error(),
},
"universal primitive eoc": {
Data: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagEOC)},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypePrimitive,
Tag: TagEOC,
},
ExpectedBytesRead: 1,
},
"universal primitive character string": {
Data: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString)},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypePrimitive,
Tag: TagCharacterString,
},
ExpectedBytesRead: 1,
},
"universal constructed bit string": {
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBitString)},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagBitString,
},
ExpectedBytesRead: 1,
},
"universal constructed character string": {
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString)},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagCharacterString,
},
ExpectedBytesRead: 1,
},
"application constructed object descriptor": {
Data: []byte{byte(ClassApplication) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
ExpectedIdentifier: Identifier{
ClassType: ClassApplication,
TagType: TypeConstructed,
Tag: TagObjectDescriptor,
},
ExpectedBytesRead: 1,
},
"context constructed object descriptor": {
Data: []byte{byte(ClassContext) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
ExpectedIdentifier: Identifier{
ClassType: ClassContext,
TagType: TypeConstructed,
Tag: TagObjectDescriptor,
},
ExpectedBytesRead: 1,
},
"private constructed object descriptor": {
Data: []byte{byte(ClassPrivate) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
ExpectedIdentifier: Identifier{
ClassType: ClassPrivate,
TagType: TypeConstructed,
Tag: TagObjectDescriptor,
},
ExpectedBytesRead: 1,
},
"high-tag-number tag missing bytes": {
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag)},
ExpectedError: io.ErrUnexpectedEOF.Error(),
ExpectedBytesRead: 1,
},
"high-tag-number tag invalid first byte": {
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), 0x0},
ExpectedError: "invalid first high-tag-number tag byte",
ExpectedBytesRead: 2,
},
"high-tag-number tag invalid first byte with continue bit": {
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), byte(HighTagContinueBitmask)},
ExpectedError: "invalid first high-tag-number tag byte",
ExpectedBytesRead: 2,
},
"high-tag-number tag continuation missing bytes": {
Data: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag), byte(HighTagContinueBitmask | 0x1)},
ExpectedError: io.ErrUnexpectedEOF.Error(),
ExpectedBytesRead: 2,
},
"high-tag-number tag overflow": {
Data: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(HighTagContinueBitmask | 0x1),
byte(0x1),
},
ExpectedError: "high-tag-number tag overflow",
ExpectedBytesRead: 11,
},
"max high-tag-number tag": {
Data: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(0x7f),
},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: Tag(0x7FFFFFFFFFFFFFFF), // 01111111...(63)...11111b
},
ExpectedBytesRead: 10,
},
"high-tag-number encoding of low-tag value": {
Data: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
byte(TagObjectDescriptor),
},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagObjectDescriptor,
},
ExpectedBytesRead: 2,
},
"max high-tag-number tag ignores extra data": {
Data: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(0x7f),
byte(0x01), // extra data, shouldn't be read
byte(0x02), // extra data, shouldn't be read
byte(0x03), // extra data, shouldn't be read
},
ExpectedIdentifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: Tag(0x7FFFFFFFFFFFFFFF), // 01111111...(63)...11111b
},
ExpectedBytesRead: 10,
},
}
for k, tc := range testcases {
reader := bytes.NewBuffer(tc.Data)
identifier, read, err := readIdentifier(reader)
if err != nil {
if tc.ExpectedError == "" {
t.Errorf("%s: unexpected error: %v", k, err)
} else if err.Error() != tc.ExpectedError {
t.Errorf("%s: expected error %v, got %v", k, tc.ExpectedError, err)
}
} else if tc.ExpectedError != "" {
t.Errorf("%s: expected error %v, got none", k, tc.ExpectedError)
continue
}
if read != tc.ExpectedBytesRead {
t.Errorf("%s: expected read %d, got %d", k, tc.ExpectedBytesRead, read)
}
if identifier.ClassType != tc.ExpectedIdentifier.ClassType {
t.Errorf("%s: expected class type %d (%s), got %d (%s)", k,
tc.ExpectedIdentifier.ClassType,
ClassMap[tc.ExpectedIdentifier.ClassType],
identifier.ClassType,
ClassMap[identifier.ClassType],
)
}
if identifier.TagType != tc.ExpectedIdentifier.TagType {
t.Errorf("%s: expected tag type %d (%s), got %d (%s)", k,
tc.ExpectedIdentifier.TagType,
TypeMap[tc.ExpectedIdentifier.TagType],
identifier.TagType,
TypeMap[identifier.TagType],
)
}
if identifier.Tag != tc.ExpectedIdentifier.Tag {
t.Errorf("%s: expected tag %d (%s), got %d (%s)", k,
tc.ExpectedIdentifier.Tag,
tagMap[tc.ExpectedIdentifier.Tag],
identifier.Tag,
tagMap[identifier.Tag],
)
}
}
}
func TestEncodeIdentifier(t *testing.T) {
testcases := map[string]struct {
Identifier Identifier
ExpectedBytes []byte
}{
"universal primitive eoc": {
Identifier: Identifier{
ClassType: ClassUniversal,
TagType: TypePrimitive,
Tag: TagEOC,
},
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagEOC)},
},
"universal primitive character string": {
Identifier: Identifier{
ClassType: ClassUniversal,
TagType: TypePrimitive,
Tag: TagCharacterString,
},
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypePrimitive) | byte(TagCharacterString)},
},
"universal constructed bit string": {
Identifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagBitString,
},
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBitString)},
},
"universal constructed character string": {
Identifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagCharacterString,
},
ExpectedBytes: []byte{byte(ClassUniversal) | byte(TypeConstructed) | byte(TagCharacterString)},
},
"application constructed object descriptor": {
Identifier: Identifier{
ClassType: ClassApplication,
TagType: TypeConstructed,
Tag: TagObjectDescriptor,
},
ExpectedBytes: []byte{byte(ClassApplication) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
},
"context constructed object descriptor": {
Identifier: Identifier{
ClassType: ClassContext,
TagType: TypeConstructed,
Tag: TagObjectDescriptor,
},
ExpectedBytes: []byte{byte(ClassContext) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
},
"private constructed object descriptor": {
Identifier: Identifier{
ClassType: ClassPrivate,
TagType: TypeConstructed,
Tag: TagObjectDescriptor,
},
ExpectedBytes: []byte{byte(ClassPrivate) | byte(TypeConstructed) | byte(TagObjectDescriptor)},
},
"max low-tag-number tag": {
Identifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagBMPString,
},
ExpectedBytes: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(TagBMPString),
},
},
"min high-tag-number tag": {
Identifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: TagBMPString + 1,
},
ExpectedBytes: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
byte(TagBMPString + 1),
},
},
"max high-tag-number tag": {
Identifier: Identifier{
ClassType: ClassUniversal,
TagType: TypeConstructed,
Tag: Tag(math.MaxInt64),
},
ExpectedBytes: []byte{
byte(ClassUniversal) | byte(TypeConstructed) | byte(HighTag),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(HighTagContinueBitmask | 0x7f),
byte(0x7f),
},
},
}
for k, tc := range testcases {
b := encodeIdentifier(tc.Identifier)
if bytes.Compare(tc.ExpectedBytes, b) != 0 {
t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedBytes, b)
}
}
}

71
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/length.go generated vendored Normal file
View 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
}

View File

@@ -0,0 +1,158 @@
package ber
import (
"bytes"
"io"
"math"
"testing"
)
func TestReadLength(t *testing.T) {
testcases := map[string]struct {
Data []byte
ExpectedLength int
ExpectedBytesRead int
ExpectedError string
}{
"empty": {
Data: []byte{},
ExpectedBytesRead: 0,
ExpectedError: io.ErrUnexpectedEOF.Error(),
},
"invalid first byte": {
Data: []byte{0xFF},
ExpectedBytesRead: 1,
ExpectedError: "invalid length byte 0xff",
},
"indefinite form": {
Data: []byte{LengthLongFormBitmask},
ExpectedLength: LengthIndefinite,
ExpectedBytesRead: 1,
},
"short-definite-form zero length": {
Data: []byte{0},
ExpectedLength: 0,
ExpectedBytesRead: 1,
},
"short-definite-form length 1": {
Data: []byte{1},
ExpectedLength: 1,
ExpectedBytesRead: 1,
},
"short-definite-form max length": {
Data: []byte{127},
ExpectedLength: 127,
ExpectedBytesRead: 1,
},
"long-definite-form missing bytes": {
Data: []byte{LengthLongFormBitmask | 1},
ExpectedBytesRead: 1,
ExpectedError: io.ErrUnexpectedEOF.Error(),
},
"long-definite-form overflow": {
Data: []byte{LengthLongFormBitmask | 9},
ExpectedBytesRead: 1,
ExpectedError: "long-form length overflow",
},
"long-definite-form zero length": {
Data: []byte{LengthLongFormBitmask | 1, 0x0},
ExpectedLength: 0,
ExpectedBytesRead: 2,
},
"long-definite-form length 127": {
Data: []byte{LengthLongFormBitmask | 1, 127},
ExpectedLength: 127,
ExpectedBytesRead: 2,
},
"long-definite-form max length": {
Data: []byte{
LengthLongFormBitmask | 8,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
},
ExpectedLength: math.MaxInt64,
ExpectedBytesRead: 9,
},
}
for k, tc := range testcases {
reader := bytes.NewBuffer(tc.Data)
length, read, err := readLength(reader)
if err != nil {
if tc.ExpectedError == "" {
t.Errorf("%s: unexpected error: %v", k, err)
} else if err.Error() != tc.ExpectedError {
t.Errorf("%s: expected error %v, got %v", k, tc.ExpectedError, err)
}
} else if tc.ExpectedError != "" {
t.Errorf("%s: expected error %v, got none", k, tc.ExpectedError)
continue
}
if read != tc.ExpectedBytesRead {
t.Errorf("%s: expected read %d, got %d", k, tc.ExpectedBytesRead, read)
}
if length != tc.ExpectedLength {
t.Errorf("%s: expected length %d, got %d", k, tc.ExpectedLength, length)
}
}
}
func TestEncodeLength(t *testing.T) {
testcases := map[string]struct {
Length int
ExpectedBytes []byte
}{
"0": {
Length: 0,
ExpectedBytes: []byte{0},
},
"1": {
Length: 1,
ExpectedBytes: []byte{1},
},
"max short-form length": {
Length: 127,
ExpectedBytes: []byte{127},
},
"min long-form length": {
Length: 128,
ExpectedBytes: []byte{LengthLongFormBitmask | 1, 128},
},
"max long-form length": {
Length: math.MaxInt64,
ExpectedBytes: []byte{
LengthLongFormBitmask | 8,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
},
},
}
for k, tc := range testcases {
b := encodeLength(tc.Length)
if bytes.Compare(tc.ExpectedBytes, b) != 0 {
t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedBytes, b)
}
}
}

View File

@@ -0,0 +1,182 @@
package ber
import (
"bytes"
"io"
"io/ioutil"
"testing"
)
var errEOF = io.ErrUnexpectedEOF.Error()
// Tests from http://www.strozhevsky.com/free_docs/free_asn1_testsuite_descr.pdf
// Source files and descriptions at http://www.strozhevsky.com/free_docs/TEST_SUITE.zip
var testcases = []struct {
// File contains the path to the BER-encoded file
File string
// Error indicates whether a decoding error is expected
Error string
// AbnormalEncoding indicates whether a normalized re-encoding is expected to differ from the original source
AbnormalEncoding bool
// IndefiniteEncoding indicates the source file used indefinite-length encoding, so the re-encoding is expected to differ (since the length is known)
IndefiniteEncoding bool
}{
// Common blocks
{File: "tests/tc1.ber", Error: "high-tag-number tag overflow"},
{File: "tests/tc2.ber", Error: errEOF},
{File: "tests/tc3.ber", Error: errEOF},
{File: "tests/tc4.ber", Error: "invalid length byte 0xff"},
{File: "tests/tc5.ber", Error: "", AbnormalEncoding: true},
// Real numbers (some expected failures are disabled until support is added)
{File: "tests/tc6.ber", Error: ""}, // Error: "REAL value +0 must be encoded with zero-length value block"},
{File: "tests/tc7.ber", Error: ""}, // Error: "REAL value -0 must be encoded as a special value"},
{File: "tests/tc8.ber", Error: ""},
{File: "tests/tc9.ber", Error: ""}, // Error: "Bits 6 and 5 of information octet for REAL are equal to 11"
{File: "tests/tc10.ber", Error: ""},
{File: "tests/tc11.ber", Error: ""}, // Error: "Incorrect NR form"
{File: "tests/tc12.ber", Error: ""}, // Error: "Encoding of "special value" not from ASN.1 standard"
{File: "tests/tc13.ber", Error: errEOF},
{File: "tests/tc14.ber", Error: errEOF},
{File: "tests/tc15.ber", Error: ""}, // Error: "Too big value of exponent"
{File: "tests/tc16.ber", Error: ""}, // Error: "Too big value of mantissa"
{File: "tests/tc17.ber", Error: ""}, // Error: "Too big values for exponent and mantissa + using of "scaling factor" value"
// Integers
{File: "tests/tc18.ber", Error: ""},
{File: "tests/tc19.ber", Error: errEOF},
{File: "tests/tc20.ber", Error: ""},
// Object identifiers
{File: "tests/tc21.ber", Error: ""},
{File: "tests/tc22.ber", Error: ""},
{File: "tests/tc23.ber", Error: errEOF},
{File: "tests/tc24.ber", Error: ""},
// Booleans
{File: "tests/tc25.ber", Error: ""},
{File: "tests/tc26.ber", Error: ""},
{File: "tests/tc27.ber", Error: errEOF},
{File: "tests/tc28.ber", Error: ""},
{File: "tests/tc29.ber", Error: ""},
// Null
{File: "tests/tc30.ber", Error: ""},
{File: "tests/tc31.ber", Error: errEOF},
{File: "tests/tc32.ber", Error: ""},
// Bitstring (some expected failures are disabled until support is added)
{File: "tests/tc33.ber", Error: ""}, // Error: "Too big value for "unused bits""
{File: "tests/tc34.ber", Error: errEOF},
{File: "tests/tc35.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of different from BIT STRING types as internal types for constructive encoding"
{File: "tests/tc36.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of "unused bits" in internal BIT STRINGs with constructive form of encoding"
{File: "tests/tc37.ber", Error: ""},
{File: "tests/tc38.ber", Error: "", IndefiniteEncoding: true},
{File: "tests/tc39.ber", Error: ""},
{File: "tests/tc40.ber", Error: ""},
// Octet string (some expected failures are disabled until support is added)
{File: "tests/tc41.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of different from OCTET STRING types as internal types for constructive encoding"
{File: "tests/tc42.ber", Error: errEOF},
{File: "tests/tc43.ber", Error: errEOF},
{File: "tests/tc44.ber", Error: ""},
{File: "tests/tc45.ber", Error: ""},
// Bitstring
{File: "tests/tc46.ber", Error: "indefinite length used with primitive type"},
{File: "tests/tc47.ber", Error: "eoc child not allowed with definite length"},
{File: "tests/tc48.ber", Error: "", IndefiniteEncoding: true}, // Error: "Using of more than 7 "unused bits" in BIT STRING with constrictive encoding form"
}
func TestSuiteDecodePacket(t *testing.T) {
// Debug = true
for _, tc := range testcases {
file := tc.File
dataIn, err := ioutil.ReadFile(file)
if err != nil {
t.Errorf("%s: %v", file, err)
continue
}
// fmt.Printf("%s: decode %d\n", file, len(dataIn))
packet, err := DecodePacketErr(dataIn)
if err != nil {
if tc.Error == "" {
t.Errorf("%s: unexpected error during DecodePacket: %v", file, err)
} else if tc.Error != err.Error() {
t.Errorf("%s: expected error %q during DecodePacket, got %q", file, tc.Error, err)
}
continue
}
if tc.Error != "" {
t.Errorf("%s: expected error %q, got none", file, tc.Error)
continue
}
dataOut := packet.Bytes()
if tc.AbnormalEncoding || tc.IndefiniteEncoding {
// Abnormal encodings and encodings that used indefinite length should re-encode differently
if bytes.Equal(dataOut, dataIn) {
t.Errorf("%s: data should have been re-encoded differently", file)
}
} else if !bytes.Equal(dataOut, dataIn) {
// Make sure the serialized data matches the source
t.Errorf("%s: data should be the same", file)
}
packet, err = DecodePacketErr(dataOut)
if err != nil {
t.Errorf("%s: unexpected error: %v", file, err)
continue
}
// Make sure the re-serialized data matches our original serialization
dataOut2 := packet.Bytes()
if !bytes.Equal(dataOut, dataOut2) {
t.Errorf("%s: data should be the same", file)
}
}
}
func TestSuiteReadPacket(t *testing.T) {
for _, tc := range testcases {
file := tc.File
dataIn, err := ioutil.ReadFile(file)
if err != nil {
t.Errorf("%s: %v", file, err)
continue
}
buffer := bytes.NewBuffer(dataIn)
packet, err := ReadPacket(buffer)
if err != nil {
if tc.Error == "" {
t.Errorf("%s: unexpected error during ReadPacket: %v", file, err)
} else if tc.Error != err.Error() {
t.Errorf("%s: expected error %q during ReadPacket, got %q", file, tc.Error, err)
}
continue
}
if tc.Error != "" {
t.Errorf("%s: expected error %q, got none", file, tc.Error)
continue
}
dataOut := packet.Bytes()
if tc.AbnormalEncoding || tc.IndefiniteEncoding {
// Abnormal encodings and encodings that used indefinite length should re-encode differently
if bytes.Equal(dataOut, dataIn) {
t.Errorf("%s: data should have been re-encoded differently", file)
}
} else if !bytes.Equal(dataOut, dataIn) {
// Make sure the serialized data matches the source
t.Errorf("%s: data should be the same", file)
}
packet, err = DecodePacketErr(dataOut)
if err != nil {
t.Errorf("%s: unexpected error: %v", file, err)
continue
}
// Make sure the re-serialized data matches our original serialization
dataOut2 := packet.Bytes()
if !bytes.Equal(dataOut, dataOut2) {
t.Errorf("%s: data should be the same", file)
}
}
}

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@

View File

@@ -0,0 +1 @@
<07><04><><EFBFBD><EFBFBD>

View File

@@ -0,0 +1 @@
 015625

View File

@@ -0,0 +1 @@
I

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<0C> <><7F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

View File

@@ -0,0 +1 @@
<0C><>

View File

@@ -0,0 +1 @@
<14> <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

View File

@@ -0,0 +1 @@
<03><>

View File

@@ -0,0 +1 @@


View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

Binary file not shown.

View File

@@ -0,0 +1 @@
<06><>Q<EFBFBD><51>

View File

@@ -0,0 +1 @@
<10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><0F>

View File

@@ -0,0 +1 @@
<><7F><EFBFBD><EFBFBD><EFBFBD>

View File

@@ -0,0 +1 @@
<15>`<60>H<EFBFBD><48>O <02><><EFBFBD>J<EFBFBD><4A><EFBFBD>c<EFBFBD><63>/

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@


View File

@@ -0,0 +1 @@
<01>

Binary file not shown.

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@


View File

@@ -0,0 +1 @@


Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
$

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@

View File

@@ -0,0 +1 @@
+0.E-5

View File

@@ -0,0 +1 @@
-0.E-5

Binary file not shown.

View File

@@ -0,0 +1 @@
<03><>

24
Godeps/_workspace/src/gopkg.in/asn1-ber.v1/util.go generated vendored Normal file
View 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
}

View File

@@ -6,6 +6,9 @@ BUILD_NUMBER ?= $(BUILD_NUMBER:)
BUILD_DATE = $(shell date -u)
BUILD_HASH = $(shell git rev-parse HEAD)
ENTERPRISE_DIR ?= ../enterprise
BUILD_ENTERPRISE ?= true
GO=$(GOPATH)/bin/godep go
ESLINT=node_modules/eslint/bin/eslint.js
@@ -31,6 +34,10 @@ all: dist-local
dist: | build-server build-client go-test package
mv ./model/version.go.bak ./model/version.go
@if [ "$(BUILD_ENTERPRISE)" = "true" ] && [ -d "$(ENTERPRISE_DIR)" ]; then \
mv ./mattermost.go.bak ./mattermost.go; \
mv ./config/config.json.bak ./config/config.json 2> /dev/null || true; \
fi
dist-local: | start-docker dist
@@ -79,9 +86,21 @@ build-server:
sed -i'.make_mac_work' 's|_BUILD_NUMBER_|$(BUILD_NUMBER)|g' ./model/version.go
sed -i'.make_mac_work' 's|_BUILD_DATE_|$(BUILD_DATE)|g' ./model/version.go
sed -i'.make_mac_work' 's|_BUILD_HASH_|$(BUILD_HASH)|g' ./model/version.go
@if [ "$(BUILD_ENTERPRISE)" = "true" ] && [ -d "$(ENTERPRISE_DIR)" ]; then \
cp ./config/config.json ./config/config.json.bak; \
jq -s '.[0] * .[1]' ./config/config.json $(ENTERPRISE_DIR)/config/enterprise-config-additions.json > config.json.tmp; \
mv config.json.tmp ./config/config.json; \
sed -e '/\/\/ENTERPRISE_IMPORTS/ {' -e 'r $(ENTERPRISE_DIR)/imports' -e 'd' -e '}' -i'.bak' mattermost.go; \
sed -i'.make_mac_work' 's|_BUILD_ENTERPRISE_READY_|true|g' ./model/version.go; \
else \
sed -i'.make_mac_work' 's|_BUILD_ENTERPRISE_READY_|false|g' ./model/version.go; \
fi
rm ./model/version.go.make_mac_work
$(GO) build $(GOFLAGS) ./...
$(GO) generate $(GOFLAGS) ./...
$(GO) install $(GOFLAGS) ./...
package:
@@ -242,6 +261,13 @@ run: start-docker .prepare-go .prepare-jsx
@echo Starting react processo
cd web/react && npm start &
@if [ "$(BUILD_ENTERPRISE)" = "true" ] && [ -d "$(ENTERPRISE_DIR)" ]; then \
cp ./config/config.json ./config/config.json.bak; \
jq -s '.[0] * .[1]' ./config/config.json $(ENTERPRISE_DIR)/config/enterprise-config-additions.json > config.json.tmp; \
mv config.json.tmp ./config/config.json; \
sed -e '/\/\/ENTERPRISE_IMPORTS/ {' -e 'r $(ENTERPRISE_DIR)/imports' -e 'd' -e '}' -i'.bak' mattermost.go; \
fi
@echo Starting go web server
$(GO) run $(GOFLAGS) mattermost.go -config=config.json &
@@ -270,6 +296,11 @@ stop:
docker rm -v ${DOCKER_CONTAINER_NAME} > /dev/null; \
fi
@if [ "$(BUILD_ENTERPRISE)" = "true" ] && [ -d "$(ENTERPRISE_DIR)" ]; then \
mv ./config/config.json.bak ./config/config.json 2> /dev/null || true; \
mv ./mattermost.go.bak ./mattermost.go 2> /dev/null || true; \
fi
setup-mac:
echo $$(boot2docker ip 2> /dev/null) dockerhost | sudo tee -a /etc/hosts

View File

@@ -221,8 +221,9 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
teamSignup.User.TeamId = rteam.Id
teamSignup.User.EmailVerified = true
ruser := CreateUser(c, rteam, &teamSignup.User)
if c.Err != nil {
ruser, err := CreateUser(rteam, &teamSignup.User)
if err != nil {
c.Err = err
return
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/disintegration/imaging"
"github.com/golang/freetype"
"github.com/gorilla/mux"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
@@ -44,6 +45,7 @@ func InitUser(r *mux.Router) {
sr.Handle("/reset_password", ApiAppHandler(resetPassword)).Methods("POST")
sr.Handle("/login", ApiAppHandler(login)).Methods("POST")
sr.Handle("/logout", ApiUserRequired(logout)).Methods("POST")
sr.Handle("/login_ldap", ApiAppHandler(loginLdap)).Methods("POST")
sr.Handle("/revoke_session", ApiUserRequired(revokeSession)).Methods("POST")
sr.Handle("/newimage", ApiUserRequired(uploadProfileImage)).Methods("POST")
@@ -59,7 +61,7 @@ func InitUser(r *mux.Router) {
}
func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.EmailSettings.EnableSignUpWithEmail {
if !utils.Cfg.EmailSettings.EnableSignUpWithEmail || !utils.Cfg.TeamSettings.EnableUserCreation {
c.Err = model.NewAppError("signupTeam", "User sign-up with email is disabled.", "")
c.Err.StatusCode = http.StatusNotImplemented
return
@@ -118,8 +120,9 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
user.EmailVerified = true
}
ruser := CreateUser(c, team, user)
if c.Err != nil {
ruser, err := CreateUser(team, user)
if err != nil {
c.Err = err
return
}
@@ -163,12 +166,7 @@ func IsVerifyHashRequired(user *model.User, team *model.Team, hash string) bool
return shouldVerifyHash
}
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
if !utils.Cfg.TeamSettings.EnableUserCreation {
c.Err = model.NewAppError("CreateUser", "User creation has been disabled. Please ask your systems administrator for details.", "")
return nil
}
func CreateUser(team *model.Team, user *model.User) (*model.User, *model.AppError) {
channelRole := ""
if team.Email == user.Email {
@@ -178,8 +176,7 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
// Below is a speical case where the first user in the entire
// system is granted the system_admin role instead of admin
if result := <-Srv.Store.User().GetTotalUsersCount(); result.Err != nil {
c.Err = result.Err
return nil
return nil, result.Err
} else {
count := result.Data.(int64)
if count <= 0 {
@@ -194,9 +191,8 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
user.MakeNonNil()
if result := <-Srv.Store.User().Save(user); result.Err != nil {
c.Err = result.Err
l4g.Error("Couldn't save the user err=%v", result.Err)
return nil
return nil, result.Err
} else {
ruser := result.Data.(*model.User)
@@ -225,7 +221,7 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
PublishAndForget(message)
return ruser
return ruser, nil
}
}
@@ -313,7 +309,7 @@ func LoginById(c *Context, w http.ResponseWriter, r *http.Request, userId, passw
return nil
} else {
user := result.Data.(*model.User)
if checkUserPassword(c, user, password) {
if checkUserLoginAttempts(c, user) && checkUserPassword(c, user, password) {
Login(c, w, r, user, deviceId)
return user
}
@@ -339,7 +335,7 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam
} else {
user := result.Data.(*model.User)
if checkUserPassword(c, user, password) {
if checkUserLoginAttempts(c, user) && checkUserPassword(c, user, password) {
Login(c, w, r, user, deviceId)
return user
}
@@ -348,8 +344,7 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam
return nil
}
func checkUserPassword(c *Context, user *model.User, password string) bool {
func checkUserLoginAttempts(c *Context, user *model.User) bool {
if user.FailedAttempts >= utils.Cfg.ServiceSettings.MaximumLoginAttempts {
c.LogAuditWithUserId(user.Id, "fail")
c.Err = model.NewAppError("checkUserPassword", "Your account is locked because of too many failed password attempts. Please reset your password.", "user_id="+user.Id)
@@ -357,6 +352,11 @@ func checkUserPassword(c *Context, user *model.User, password string) bool {
return false
}
return true
}
func checkUserPassword(c *Context, user *model.User, password string) bool {
if !model.ComparePassword(user.Password, password) {
c.LogAuditWithUserId(user.Id, "fail")
c.Err = model.NewAppError("checkUserPassword", "Login failed because of invalid password", "user_id="+user.Id)
@@ -500,6 +500,70 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(user.ToJson()))
}
func loginLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if !*utils.Cfg.LdapSettings.Enable {
c.Err = model.NewAppError("loginLdap", "LDAP not enabled on this server", "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
props := model.MapFromJson(r.Body)
password := props["password"]
id := props["id"]
teamName := props["teamName"]
if len(password) == 0 {
c.Err = model.NewAppError("loginLdap", "Password field must not be blank", "")
c.Err.StatusCode = http.StatusForbidden
return
}
if len(id) == 0 {
c.Err = model.NewAppError("loginLdap", "Need an ID", "")
c.Err.StatusCode = http.StatusForbidden
return
}
teamc := Srv.Store.Team().GetByName(teamName)
ldapInterface := einterfaces.GetLdapInterface()
if ldapInterface == nil {
c.Err = model.NewAppError("loginLdap", "LDAP not available on this server", "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
var team *model.Team
if result := <-teamc; result.Err != nil {
c.Err = result.Err
return
} else {
team = result.Data.(*model.Team)
}
user, err := ldapInterface.DoLogin(team, id, password)
if err != nil {
c.Err = err
return
}
if !checkUserLoginAttempts(c, user) {
return
}
// User is authenticated at this point
Login(c, w, r, user, props["device_id"])
if user != nil {
user.Sanitize(map[string]bool{})
} else {
user = &model.User{}
}
w.Write([]byte(user.ToJson()))
}
func revokeSession(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
id := props["id"]

22
einterfaces/ldap.go Normal file
View File

@@ -0,0 +1,22 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package einterfaces
import (
"github.com/mattermost/platform/model"
)
type LdapInterface interface {
DoLogin(team *model.Team, id string, password string) (*model.User, *model.AppError)
}
var theLdapInterface LdapInterface
func RegisterLdapInterface(newInterface LdapInterface) {
theLdapInterface = newInterface
}
func GetLdapInterface() LdapInterface {
return theLdapInterface
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package einterfaces
import (
"github.com/mattermost/platform/model"
"io"
)
type OauthProvider interface {
GetIdentifier() string
GetUserFromJson(data io.Reader) *model.User
GetAuthDataFromJson(data io.Reader) string
}
var oauthProviders = make(map[string]OauthProvider)
func RegisterOauthProvider(name string, newProvider OauthProvider) {
oauthProviders[name] = newProvider
}
func GetOauthProvider(name string) OauthProvider {
provider, ok := oauthProviders[name]
if ok {
return provider
}
return nil
}

View File

@@ -23,8 +23,16 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"github.com/mattermost/platform/web"
// Plugins
_ "github.com/mattermost/platform/model/gitlab"
// Enterprise Deps
_ "github.com/go-ldap/ldap"
)
//ENTERPRISE_IMPORTS
var flagCmdCreateTeam bool
var flagCmdCreateUser bool
var flagCmdAssignRole bool
@@ -51,6 +59,7 @@ func main() {
pwd, _ := os.Getwd()
l4g.Info("Current version is %v (%v/%v/%v)", model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash)
l4g.Info("Enterprise Enabled: %t", model.BuildEnterpriseReady)
l4g.Info("Current working directory is %v", pwd)
l4g.Info("Loaded config file from %v", utils.FindConfigFile(flagConfigFile))
@@ -112,6 +121,7 @@ func runSecurityAndDiagnosticsJobAndForget() {
v.Set(utils.PROP_DIAGNOSTIC_ID, utils.CfgDiagnosticId)
v.Set(utils.PROP_DIAGNOSTIC_BUILD, model.CurrentVersion+"."+model.BuildNumber)
v.Set(utils.PROP_DIAGNOSTIC_ENTERPRISE_READY, model.BuildEnterpriseReady)
v.Set(utils.PROP_DIAGNOSTIC_DATABASE, utils.Cfg.SqlSettings.DriverName)
v.Set(utils.PROP_DIAGNOSTIC_OS, runtime.GOOS)
v.Set(utils.PROP_DIAGNOSTIC_CATEGORY, utils.VAL_DIAGNOSTIC_CATEGORY_DEFAULT)
@@ -283,10 +293,6 @@ func cmdCreateUser() {
os.Exit(1)
}
c := &api.Context{}
c.RequestId = model.NewId()
c.IpAddress = "cmd_line"
var team *model.Team
user := &model.User{}
user.Email = flagEmail
@@ -302,10 +308,10 @@ func cmdCreateUser() {
user.TeamId = team.Id
}
api.CreateUser(c, team, user)
if c.Err != nil {
if c.Err.Message != "An account with that email already exists." {
l4g.Error("%v", c.Err)
_, err := api.CreateUser(team, user)
if err != nil {
if err.Message != "An account with that email already exists." {
l4g.Error("%v", err)
flushLogAndExit(1)
}
}
@@ -320,6 +326,7 @@ func cmdVersion() {
fmt.Fprintln(os.Stderr, "Build Number: "+model.BuildNumber)
fmt.Fprintln(os.Stderr, "Build Date: "+model.BuildDate)
fmt.Fprintln(os.Stderr, "Build Hash: "+model.BuildHash)
fmt.Fprintln(os.Stderr, "Build Enterprise Ready: "+model.BuildEnterpriseReady)
os.Exit(0)
}

View File

@@ -20,6 +20,7 @@ const (
DATABASE_DRIVER_POSTGRES = "postgres"
SERVICE_GITLAB = "gitlab"
SERVICE_GOOGLE = "google"
)
type ServiceSettings struct {
@@ -133,6 +134,26 @@ type TeamSettings struct {
EnableTeamListing *bool
}
type LdapSettings struct {
// Basic
Enable *bool
LdapServer *string
LdapPort *int
BaseDN *string
BindUsername *string
BindPassword *string
// User Mapping
FirstNameAttribute *string
LastNameAttribute *string
EmailAttribute *string
UsernameAttribute *string
IdAttribute *string
// Advansed
QueryTimeout *int
}
type Config struct {
ServiceSettings ServiceSettings
TeamSettings TeamSettings
@@ -144,6 +165,8 @@ type Config struct {
PrivacySettings PrivacySettings
SupportSettings SupportSettings
GitLabSettings SSOSettings
GoogleSettings SSOSettings
LdapSettings LdapSettings
}
func (o *Config) ToJson() string {
@@ -156,8 +179,11 @@ func (o *Config) ToJson() string {
}
func (o *Config) GetSSOService(service string) *SSOSettings {
if service == SERVICE_GITLAB {
switch service {
case SERVICE_GITLAB:
return &o.GitLabSettings
case SERVICE_GOOGLE:
return &o.GoogleSettings
}
return nil
@@ -251,6 +277,21 @@ func (o *Config) SetDefaults() {
o.SupportSettings.SupportEmail = new(string)
*o.SupportSettings.SupportEmail = "feedback@mattermost.com"
}
if o.LdapSettings.LdapPort == nil {
o.LdapSettings.LdapPort = new(int)
*o.LdapSettings.LdapPort = 389
}
if o.LdapSettings.QueryTimeout == nil {
o.LdapSettings.QueryTimeout = new(int)
*o.LdapSettings.QueryTimeout = 60
}
if o.LdapSettings.Enable == nil {
o.LdapSettings.Enable = new(bool)
*o.LdapSettings.Enable = false
}
}
func (o *Config) IsValid() *AppError {

View File

@@ -1,10 +1,12 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
package oauthgitlab
import (
"encoding/json"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
"io"
"strconv"
"strings"
@@ -14,6 +16,9 @@ const (
USER_AUTH_SERVICE_GITLAB = "gitlab"
)
type GitLabProvider struct {
}
type GitLabUser struct {
Id int64 `json:"id"`
Username string `json:"username"`
@@ -22,13 +27,18 @@ type GitLabUser struct {
Name string `json:"name"`
}
func UserFromGitLabUser(glu *GitLabUser) *User {
user := &User{}
func init() {
provider := &GitLabProvider{}
einterfaces.RegisterOauthProvider(USER_AUTH_SERVICE_GITLAB, provider)
}
func userFromGitLabUser(glu *GitLabUser) *model.User {
user := &model.User{}
username := glu.Username
if username == "" {
username = glu.Login
}
user.Username = CleanUsername(username)
user.Username = model.CleanUsername(username)
splitName := strings.Split(glu.Name, " ")
if len(splitName) == 2 {
user.FirstName = splitName[0]
@@ -46,7 +56,7 @@ func UserFromGitLabUser(glu *GitLabUser) *User {
return user
}
func GitLabUserFromJson(data io.Reader) *GitLabUser {
func gitLabUserFromJson(data io.Reader) *GitLabUser {
decoder := json.NewDecoder(data)
var glu GitLabUser
err := decoder.Decode(&glu)
@@ -57,6 +67,18 @@ func GitLabUserFromJson(data io.Reader) *GitLabUser {
}
}
func (glu *GitLabUser) GetAuthData() string {
func (glu *GitLabUser) getAuthData() string {
return strconv.FormatInt(glu.Id, 10)
}
func (m *GitLabProvider) GetIdentifier() string {
return USER_AUTH_SERVICE_GITLAB
}
func (m *GitLabProvider) GetUserFromJson(data io.Reader) *model.User {
return userFromGitLabUser(gitLabUserFromJson(data))
}
func (m *GitLabProvider) GetAuthDataFromJson(data io.Reader) string {
return gitLabUserFromJson(data).getAuthData()
}

View File

@@ -27,6 +27,7 @@ var CurrentVersion string = versions[0]
var BuildNumber = "_BUILD_NUMBER_"
var BuildDate = "_BUILD_DATE_"
var BuildHash = "_BUILD_HASH_"
var BuildEnterpriseReady = "_BUILD_ENTERPRISE_READY_"
func SplitVersion(version string) (int64, int64, int64) {
parts := strings.Split(version, ".")

View File

@@ -540,7 +540,7 @@ func (us SqlUserStore) GetTotalActiveUsersCount() StoreChannel {
go func() {
result := StoreResult{}
time := model.GetMillis() - (1000 * 60 * 60 * 12)
time := model.GetMillis() - (1000 * 60 * 60 * 24)
if count, err := us.GetReplica().SelectInt("SELECT COUNT(Id) FROM Users WHERE LastActivityAt > :Time", map[string]interface{}{"Time": time}); err != nil {
result.Err = model.NewAppError("SqlUserStore.GetTotalActiveUsersCount", "We could not count the users", err.Error())

View File

@@ -180,6 +180,7 @@ func getClientConfig(c *model.Config) map[string]string {
props["BuildNumber"] = model.BuildNumber
props["BuildDate"] = model.BuildDate
props["BuildHash"] = model.BuildHash
props["BuildEnterpriseReady"] = model.BuildEnterpriseReady
props["SiteName"] = c.TeamSettings.SiteName
props["EnableTeamCreation"] = strconv.FormatBool(c.TeamSettings.EnableTeamCreation)
@@ -203,6 +204,7 @@ func getClientConfig(c *model.Config) map[string]string {
props["FeedbackEmail"] = c.EmailSettings.FeedbackEmail
props["EnableSignUpWithGitLab"] = strconv.FormatBool(c.GitLabSettings.Enable)
props["EnableSignUpWithGoogle"] = strconv.FormatBool(c.GoogleSettings.Enable)
props["ShowEmailAddress"] = strconv.FormatBool(c.PrivacySettings.ShowEmailAddress)
@@ -217,5 +219,7 @@ func getClientConfig(c *model.Config) map[string]string {
props["ProfileHeight"] = fmt.Sprintf("%v", c.FileSettings.ProfileHeight)
props["ProfileWidth"] = fmt.Sprintf("%v", c.FileSettings.ProfileWidth)
props["EnableLdap"] = strconv.FormatBool(*c.LdapSettings.Enable)
return props
}

View File

@@ -15,6 +15,7 @@ const (
PROP_DIAGNOSTIC_CATEGORY = "c"
VAL_DIAGNOSTIC_CATEGORY_DEFAULT = "d"
PROP_DIAGNOSTIC_BUILD = "b"
PROP_DIAGNOSTIC_ENTERPRISE_READY = "be"
PROP_DIAGNOSTIC_DATABASE = "db"
PROP_DIAGNOSTIC_OS = "os"
PROP_DIAGNOSTIC_USER_COUNT = "uc"

Some files were not shown because too many files have changed in this diff Show More