mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-28 12:16:17 +01:00
Remove ldap dep
This commit is contained in:
parent
25d6ae69d1
commit
59a7c7c5a5
24 changed files with 2742 additions and 7 deletions
|
@ -2,6 +2,8 @@
|
||||||
path = github.com/gogits/gogs
|
path = github.com/gogits/gogs
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
github.com/beego/memcache =
|
||||||
|
github.com/beego/redigo =
|
||||||
github.com/Unknwon/cae =
|
github.com/Unknwon/cae =
|
||||||
github.com/Unknwon/com =
|
github.com/Unknwon/com =
|
||||||
github.com/Unknwon/goconfig =
|
github.com/Unknwon/goconfig =
|
||||||
|
@ -14,7 +16,6 @@ github.com/go-xorm/xorm =
|
||||||
github.com/gogits/gfm =
|
github.com/gogits/gfm =
|
||||||
github.com/gogits/git =
|
github.com/gogits/git =
|
||||||
github.com/gogits/oauth2 =
|
github.com/gogits/oauth2 =
|
||||||
github.com/juju2013/goldap =
|
|
||||||
github.com/lib/pq =
|
github.com/lib/pq =
|
||||||
github.com/macaron-contrib/cache =
|
github.com/macaron-contrib/cache =
|
||||||
github.com/macaron-contrib/captcha =
|
github.com/macaron-contrib/captcha =
|
||||||
|
@ -22,6 +23,7 @@ github.com/macaron-contrib/csrf =
|
||||||
github.com/macaron-contrib/i18n =
|
github.com/macaron-contrib/i18n =
|
||||||
github.com/macaron-contrib/session =
|
github.com/macaron-contrib/session =
|
||||||
github.com/macaron-contrib/toolbox =
|
github.com/macaron-contrib/toolbox =
|
||||||
|
github.com/mattn/go-sqlite3 =
|
||||||
github.com/nfnt/resize =
|
github.com/nfnt/resize =
|
||||||
github.com/saintfish/chardet =
|
github.com/saintfish/chardet =
|
||||||
|
|
||||||
|
|
27
modules/asn1-ber/LICENSE
Normal file
27
modules/asn1-ber/LICENSE
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
11
modules/asn1-ber/Makefile
Normal file
11
modules/asn1-ber/Makefile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright 2009 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.
|
||||||
|
|
||||||
|
include $(GOROOT)/src/Make.inc
|
||||||
|
|
||||||
|
TARG=github.com/mmitton/asn1-ber
|
||||||
|
GOFILES=\
|
||||||
|
ber.go\
|
||||||
|
|
||||||
|
include $(GOROOT)/src/Make.pkg
|
14
modules/asn1-ber/README
Normal file
14
modules/asn1-ber/README
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
ASN1 BER Encoding / Decoding Library for the GO programming language.
|
||||||
|
|
||||||
|
Required Librarys:
|
||||||
|
None
|
||||||
|
|
||||||
|
Working:
|
||||||
|
Very basic encoding / decoding needed for LDAP protocol
|
||||||
|
|
||||||
|
Tests Implemented:
|
||||||
|
None
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
Fix all encoding / decoding to conform to ASN1 BER spec
|
||||||
|
Implement Tests / Benchmarks
|
492
modules/asn1-ber/ber.go
Normal file
492
modules/asn1-ber/ber.go
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
package ber
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Packet struct {
|
||||||
|
ClassType uint8
|
||||||
|
TagType uint8
|
||||||
|
Tag uint8
|
||||||
|
Value interface{}
|
||||||
|
ByteValue []byte
|
||||||
|
Data *bytes.Buffer
|
||||||
|
Children []*Packet
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TagEOC = 0x00
|
||||||
|
TagBoolean = 0x01
|
||||||
|
TagInteger = 0x02
|
||||||
|
TagBitString = 0x03
|
||||||
|
TagOctetString = 0x04
|
||||||
|
TagNULL = 0x05
|
||||||
|
TagObjectIdentifier = 0x06
|
||||||
|
TagObjectDescriptor = 0x07
|
||||||
|
TagExternal = 0x08
|
||||||
|
TagRealFloat = 0x09
|
||||||
|
TagEnumerated = 0x0a
|
||||||
|
TagEmbeddedPDV = 0x0b
|
||||||
|
TagUTF8String = 0x0c
|
||||||
|
TagRelativeOID = 0x0d
|
||||||
|
TagSequence = 0x10
|
||||||
|
TagSet = 0x11
|
||||||
|
TagNumericString = 0x12
|
||||||
|
TagPrintableString = 0x13
|
||||||
|
TagT61String = 0x14
|
||||||
|
TagVideotexString = 0x15
|
||||||
|
TagIA5String = 0x16
|
||||||
|
TagUTCTime = 0x17
|
||||||
|
TagGeneralizedTime = 0x18
|
||||||
|
TagGraphicString = 0x19
|
||||||
|
TagVisibleString = 0x1a
|
||||||
|
TagGeneralString = 0x1b
|
||||||
|
TagUniversalString = 0x1c
|
||||||
|
TagCharacterString = 0x1d
|
||||||
|
TagBMPString = 0x1e
|
||||||
|
TagBitmask = 0x1f // xxx11111b
|
||||||
|
)
|
||||||
|
|
||||||
|
var TagMap = map[uint8]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",
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ClassUniversal = 0 // 00xxxxxxb
|
||||||
|
ClassApplication = 64 // 01xxxxxxb
|
||||||
|
ClassContext = 128 // 10xxxxxxb
|
||||||
|
ClassPrivate = 192 // 11xxxxxxb
|
||||||
|
ClassBitmask = 192 // 11xxxxxxb
|
||||||
|
)
|
||||||
|
|
||||||
|
var ClassMap = map[uint8]string{
|
||||||
|
ClassUniversal: "Universal",
|
||||||
|
ClassApplication: "Application",
|
||||||
|
ClassContext: "Context",
|
||||||
|
ClassPrivate: "Private",
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypePrimitive = 0 // xx0xxxxxb
|
||||||
|
TypeConstructed = 32 // xx1xxxxxb
|
||||||
|
TypeBitmask = 32 // xx1xxxxxb
|
||||||
|
)
|
||||||
|
|
||||||
|
var TypeMap = map[uint8]string{
|
||||||
|
TypePrimitive: "Primative",
|
||||||
|
TypeConstructed: "Constructed",
|
||||||
|
}
|
||||||
|
|
||||||
|
var Debug bool = false
|
||||||
|
|
||||||
|
func PrintBytes(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++ {
|
||||||
|
fmt.Print(indent + data_lines[i] + "\n")
|
||||||
|
fmt.Print(indent + num_lines[i] + "\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintPacket(p *Packet) {
|
||||||
|
printPacket(p, 0, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPacket(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.Printf("%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(p.Bytes(), indent_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range p.Children {
|
||||||
|
printPacket(child, indent+1, printBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeBuffer(in []byte, new_size uint64) (out []byte) {
|
||||||
|
out = make([]byte, new_size)
|
||||||
|
|
||||||
|
copy(out, in)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBytes(reader io.Reader, buf []byte) error {
|
||||||
|
idx := 0
|
||||||
|
buflen := len(buf)
|
||||||
|
|
||||||
|
for idx < buflen {
|
||||||
|
n, err := reader.Read(buf[idx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
idx += n
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadPacket(reader io.Reader) (*Packet, error) {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
|
||||||
|
err := readBytes(reader, buf)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := uint64(2)
|
||||||
|
datalen := uint64(buf[1])
|
||||||
|
|
||||||
|
if Debug {
|
||||||
|
fmt.Printf("Read: datalen = %d len(buf) = %d ", datalen, len(buf))
|
||||||
|
|
||||||
|
for _, b := range buf {
|
||||||
|
fmt.Printf("%02X ", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if datalen&128 != 0 {
|
||||||
|
a := datalen - 128
|
||||||
|
|
||||||
|
idx += a
|
||||||
|
buf = resizeBuffer(buf, 2+a)
|
||||||
|
|
||||||
|
err := readBytes(reader, buf[2:])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
datalen = DecodeInteger(buf[2 : 2+a])
|
||||||
|
|
||||||
|
if Debug {
|
||||||
|
fmt.Printf("Read: a = %d idx = %d datalen = %d len(buf) = %d", a, idx, datalen, len(buf))
|
||||||
|
|
||||||
|
for _, b := range buf {
|
||||||
|
fmt.Printf("%02X ", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = resizeBuffer(buf, idx+datalen)
|
||||||
|
err = readBytes(reader, buf[idx:])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if Debug {
|
||||||
|
fmt.Printf("Read: len( buf ) = %d idx=%d datalen=%d idx+datalen=%d\n", len(buf), idx, datalen, idx+datalen)
|
||||||
|
|
||||||
|
for _, b := range buf {
|
||||||
|
fmt.Printf("%02X ", b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := DecodePacket(buf)
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeString(data []byte) (ret string) {
|
||||||
|
for _, c := range data {
|
||||||
|
ret += fmt.Sprintf("%c", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeInteger(data []byte) (ret uint64) {
|
||||||
|
for _, i := range data {
|
||||||
|
ret = ret * 256
|
||||||
|
ret = ret + uint64(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeInteger(val uint64) []byte {
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
found := false
|
||||||
|
|
||||||
|
shift := uint(56)
|
||||||
|
|
||||||
|
mask := uint64(0xFF00000000000000)
|
||||||
|
|
||||||
|
for mask > 0 {
|
||||||
|
if !found && (val&mask != 0) {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if found || (shift == 0) {
|
||||||
|
out.Write([]byte{byte((val & mask) >> shift)})
|
||||||
|
}
|
||||||
|
|
||||||
|
shift -= 8
|
||||||
|
mask = mask >> 8
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodePacket(data []byte) *Packet {
|
||||||
|
p, _ := decodePacket(data)
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePacket(data []byte) (*Packet, []byte) {
|
||||||
|
if Debug {
|
||||||
|
fmt.Printf("decodePacket: enter %d\n", len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
p := new(Packet)
|
||||||
|
|
||||||
|
p.ClassType = data[0] & ClassBitmask
|
||||||
|
p.TagType = data[0] & TypeBitmask
|
||||||
|
p.Tag = data[0] & TagBitmask
|
||||||
|
|
||||||
|
datalen := DecodeInteger(data[1:2])
|
||||||
|
datapos := uint64(2)
|
||||||
|
|
||||||
|
if datalen&128 != 0 {
|
||||||
|
datalen -= 128
|
||||||
|
datapos += datalen
|
||||||
|
datalen = DecodeInteger(data[2 : 2+datalen])
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Data = new(bytes.Buffer)
|
||||||
|
|
||||||
|
p.Children = make([]*Packet, 0, 2)
|
||||||
|
|
||||||
|
p.Value = nil
|
||||||
|
|
||||||
|
value_data := data[datapos : datapos+datalen]
|
||||||
|
|
||||||
|
if p.TagType == TypeConstructed {
|
||||||
|
for len(value_data) != 0 {
|
||||||
|
var child *Packet
|
||||||
|
|
||||||
|
child, value_data = decodePacket(value_data)
|
||||||
|
p.AppendChild(child)
|
||||||
|
}
|
||||||
|
} else if p.ClassType == ClassUniversal {
|
||||||
|
p.Data.Write(data[datapos : datapos+datalen])
|
||||||
|
p.ByteValue = value_data
|
||||||
|
|
||||||
|
switch p.Tag {
|
||||||
|
case TagEOC:
|
||||||
|
case TagBoolean:
|
||||||
|
val := DecodeInteger(value_data)
|
||||||
|
|
||||||
|
p.Value = val != 0
|
||||||
|
case TagInteger:
|
||||||
|
p.Value = DecodeInteger(value_data)
|
||||||
|
case TagBitString:
|
||||||
|
case TagOctetString:
|
||||||
|
p.Value = DecodeString(value_data)
|
||||||
|
case TagNULL:
|
||||||
|
case TagObjectIdentifier:
|
||||||
|
case TagObjectDescriptor:
|
||||||
|
case TagExternal:
|
||||||
|
case TagRealFloat:
|
||||||
|
case TagEnumerated:
|
||||||
|
p.Value = DecodeInteger(value_data)
|
||||||
|
case TagEmbeddedPDV:
|
||||||
|
case TagUTF8String:
|
||||||
|
case TagRelativeOID:
|
||||||
|
case TagSequence:
|
||||||
|
case TagSet:
|
||||||
|
case TagNumericString:
|
||||||
|
case TagPrintableString:
|
||||||
|
p.Value = DecodeString(value_data)
|
||||||
|
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(data[datapos : datapos+datalen])
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, data[datapos+datalen:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Packet) DataLength() uint64 {
|
||||||
|
return uint64(p.Data.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Packet) Bytes() []byte {
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
out.Write([]byte{p.ClassType | p.TagType | p.Tag})
|
||||||
|
packet_length := EncodeInteger(p.DataLength())
|
||||||
|
|
||||||
|
if p.DataLength() > 127 || len(packet_length) > 1 {
|
||||||
|
out.Write([]byte{byte(len(packet_length) | 128)})
|
||||||
|
out.Write(packet_length)
|
||||||
|
} else {
|
||||||
|
out.Write(packet_length)
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Write(p.Data.Bytes())
|
||||||
|
|
||||||
|
return out.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Packet) AppendChild(child *Packet) {
|
||||||
|
p.Data.Write(child.Bytes())
|
||||||
|
|
||||||
|
if len(p.Children) == cap(p.Children) {
|
||||||
|
newChildren := make([]*Packet, cap(p.Children)*2)
|
||||||
|
|
||||||
|
copy(newChildren, p.Children)
|
||||||
|
p.Children = newChildren[0:len(p.Children)]
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Children = p.Children[0 : len(p.Children)+1]
|
||||||
|
p.Children[len(p.Children)-1] = child
|
||||||
|
}
|
||||||
|
|
||||||
|
func Encode(ClassType, TagType, Tag uint8, 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, TypePrimitive, TagSequence, nil, Description)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBoolean(ClassType, TagType, Tag uint8, Value bool, Description string) *Packet {
|
||||||
|
intValue := 0
|
||||||
|
|
||||||
|
if Value {
|
||||||
|
intValue = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Encode(ClassType, TagType, Tag, nil, Description)
|
||||||
|
|
||||||
|
p.Value = Value
|
||||||
|
p.Data.Write(EncodeInteger(uint64(intValue)))
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInteger(ClassType, TagType, Tag uint8, Value uint64, Description string) *Packet {
|
||||||
|
p := Encode(ClassType, TagType, Tag, nil, Description)
|
||||||
|
|
||||||
|
p.Value = Value
|
||||||
|
p.Data.Write(EncodeInteger(Value))
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewString(ClassType, TagType, Tag uint8, Value, Description string) *Packet {
|
||||||
|
p := Encode(ClassType, TagType, Tag, nil, Description)
|
||||||
|
|
||||||
|
p.Value = Value
|
||||||
|
p.Data.Write([]byte(Value))
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ package ldap
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/ldap"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"github.com/gogits/gogs/modules/log"
|
||||||
goldap "github.com/juju2013/goldap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Basic LDAP authentication service
|
// Basic LDAP authentication service
|
||||||
|
@ -68,9 +68,9 @@ func (ls Ldapsource) SearchEntry(name, passwd string) (string, bool) {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
search := goldap.NewSearchRequest(
|
search := ldap.NewSearchRequest(
|
||||||
ls.BaseDN,
|
ls.BaseDN,
|
||||||
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
fmt.Sprintf(ls.Filter, name),
|
fmt.Sprintf(ls.Filter, name),
|
||||||
[]string{ls.Attributes},
|
[]string{ls.Attributes},
|
||||||
nil)
|
nil)
|
||||||
|
@ -87,10 +87,10 @@ func (ls Ldapsource) SearchEntry(name, passwd string) (string, bool) {
|
||||||
return "", true
|
return "", true
|
||||||
}
|
}
|
||||||
|
|
||||||
func ldapDial(ls Ldapsource) (*goldap.Conn, error) {
|
func ldapDial(ls Ldapsource) (*ldap.Conn, error) {
|
||||||
if ls.UseSSL {
|
if ls.UseSSL {
|
||||||
return goldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port), nil)
|
return ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port), nil)
|
||||||
} else {
|
} else {
|
||||||
return goldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port))
|
return ldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
modules/ldap/LICENSE
Normal file
27
modules/ldap/LICENSE
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
33
modules/ldap/README
Normal file
33
modules/ldap/README
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
Basic LDAP v3 functionality for the GO programming language.
|
||||||
|
|
||||||
|
Required Librarys:
|
||||||
|
github.com/johnweldon/asn1-ber
|
||||||
|
|
||||||
|
Working:
|
||||||
|
Connecting to LDAP server
|
||||||
|
Binding to LDAP server
|
||||||
|
Searching for entries
|
||||||
|
Compiling string filters to LDAP filters
|
||||||
|
Paging Search Results
|
||||||
|
Modify Requests / Responses
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
search
|
||||||
|
modify
|
||||||
|
|
||||||
|
Tests Implemented:
|
||||||
|
Filter Compile / Decompile
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
Add Requests / Responses
|
||||||
|
Delete Requests / Responses
|
||||||
|
Modify DN Requests / Responses
|
||||||
|
Compare Requests / Responses
|
||||||
|
Implement Tests / Benchmarks
|
||||||
|
|
||||||
|
This feature is disabled at the moment, because in some cases the "Search Request Done" packet will be handled before the last "Search Request Entry":
|
||||||
|
Mulitple internal goroutines to handle network traffic
|
||||||
|
Makes library goroutine safe
|
||||||
|
Can perform multiple search requests at the same time and return
|
||||||
|
the results to the proper goroutine. All requests are blocking
|
||||||
|
requests, so the goroutine does not need special handling
|
55
modules/ldap/bind.go
Normal file
55
modules/ldap/bind.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
275
modules/ldap/conn.go
Normal file
275
modules/ldap/conn.go
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
// 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"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MessageQuit = 0
|
||||||
|
MessageRequest = 1
|
||||||
|
MessageResponse = 2
|
||||||
|
MessageFinish = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type messagePacket struct {
|
||||||
|
Op int
|
||||||
|
MessageID uint64
|
||||||
|
Packet *ber.Packet
|
||||||
|
Channel chan *ber.Packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn represents an LDAP Connection
|
||||||
|
type Conn struct {
|
||||||
|
conn net.Conn
|
||||||
|
isTLS bool
|
||||||
|
isClosing bool
|
||||||
|
Debug debugging
|
||||||
|
chanConfirm chan bool
|
||||||
|
chanResults map[uint64]chan *ber.Packet
|
||||||
|
chanMessage chan *messagePacket
|
||||||
|
chanMessageID chan uint64
|
||||||
|
wgSender sync.WaitGroup
|
||||||
|
wgClose sync.WaitGroup
|
||||||
|
once sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.Dial(network, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorNetwork, err)
|
||||||
|
}
|
||||||
|
conn := NewConn(c)
|
||||||
|
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) {
|
||||||
|
c, err := tls.Dial(network, addr, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorNetwork, err)
|
||||||
|
}
|
||||||
|
conn := NewConn(c)
|
||||||
|
conn.isTLS = true
|
||||||
|
conn.start()
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn returns a new Conn using conn for network I/O.
|
||||||
|
func NewConn(conn net.Conn) *Conn {
|
||||||
|
return &Conn{
|
||||||
|
conn: conn,
|
||||||
|
chanConfirm: make(chan bool),
|
||||||
|
chanMessageID: make(chan uint64),
|
||||||
|
chanMessage: make(chan *messagePacket, 10),
|
||||||
|
chanResults: map[uint64]chan *ber.Packet{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.conn = nil
|
||||||
|
l.wgClose.Done()
|
||||||
|
})
|
||||||
|
l.wgClose.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the next available messageID
|
||||||
|
func (l *Conn) nextMessageID() uint64 {
|
||||||
|
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)
|
||||||
|
|
||||||
|
_, err := l.conn.Write(packet.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return NewError(ErrorNetwork, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
packet, err = ber.ReadPacket(l.conn)
|
||||||
|
if err != nil {
|
||||||
|
return NewError(ErrorNetwork, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.Children[1].Children[0].Value.(uint64) == 0 {
|
||||||
|
conn := tls.Client(l.conn, config)
|
||||||
|
l.isTLS = true
|
||||||
|
l.conn = conn
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Conn) sendMessage(packet *ber.Packet) (chan *ber.Packet, error) {
|
||||||
|
if l.isClosing {
|
||||||
|
return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
|
||||||
|
}
|
||||||
|
out := make(chan *ber.Packet)
|
||||||
|
message := &messagePacket{
|
||||||
|
Op: MessageRequest,
|
||||||
|
MessageID: packet.Children[0].Value.(uint64),
|
||||||
|
Packet: packet,
|
||||||
|
Channel: out,
|
||||||
|
}
|
||||||
|
l.sendProcessMessage(message)
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Conn) finishMessage(messageID uint64) {
|
||||||
|
if l.isClosing {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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() {
|
||||||
|
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 uint64 = 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() {
|
||||||
|
defer func() {
|
||||||
|
l.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
packet, err := ber.ReadPacket(l.conn)
|
||||||
|
if err != nil {
|
||||||
|
l.Debug.Printf("reader: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addLDAPDescriptions(packet)
|
||||||
|
message := &messagePacket{
|
||||||
|
Op: MessageResponse,
|
||||||
|
MessageID: packet.Children[0].Value.(uint64),
|
||||||
|
Packet: packet,
|
||||||
|
}
|
||||||
|
if !l.sendProcessMessage(message) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
157
modules/ldap/control.go
Normal file
157
modules/ldap/control.go
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ControlTypePaging = "1.2.840.113556.1.4.319"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ControlTypeMap = map[string]string{
|
||||||
|
ControlTypePaging: "Paging",
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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.(uint64))
|
||||||
|
c.Cookie = value.Children[1].Data.Bytes()
|
||||||
|
value.Children[1].Value = c.Cookie
|
||||||
|
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 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
modules/ldap/debug.go
Normal file
24
modules/ldap/debug.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
63
modules/ldap/examples/enterprise.ldif
Normal file
63
modules/ldap/examples/enterprise.ldif
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
dn: dc=enterprise,dc=org
|
||||||
|
objectClass: dcObject
|
||||||
|
objectClass: organization
|
||||||
|
o: acme
|
||||||
|
|
||||||
|
dn: cn=admin,dc=enterprise,dc=org
|
||||||
|
objectClass: person
|
||||||
|
cn: admin
|
||||||
|
sn: admin
|
||||||
|
description: "LDAP Admin"
|
||||||
|
|
||||||
|
dn: ou=crew,dc=enterprise,dc=org
|
||||||
|
ou: crew
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
|
||||||
|
|
||||||
|
dn: cn=kirkj,ou=crew,dc=enterprise,dc=org
|
||||||
|
cn: kirkj
|
||||||
|
sn: Kirk
|
||||||
|
gn: James Tiberius
|
||||||
|
mail: james.kirk@enterprise.org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
|
||||||
|
dn: cn=spock,ou=crew,dc=enterprise,dc=org
|
||||||
|
cn: spock
|
||||||
|
sn: Spock
|
||||||
|
mail: spock@enterprise.org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
|
||||||
|
dn: cn=mccoyl,ou=crew,dc=enterprise,dc=org
|
||||||
|
cn: mccoyl
|
||||||
|
sn: McCoy
|
||||||
|
gn: Leonard
|
||||||
|
mail: leonard.mccoy@enterprise.org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
|
||||||
|
dn: cn=scottm,ou=crew,dc=enterprise,dc=org
|
||||||
|
cn: scottm
|
||||||
|
sn: Scott
|
||||||
|
gn: Montgomery
|
||||||
|
mail: Montgomery.scott@enterprise.org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
|
||||||
|
dn: cn=uhuran,ou=crew,dc=enterprise,dc=org
|
||||||
|
cn: uhuran
|
||||||
|
sn: Uhura
|
||||||
|
gn: Nyota
|
||||||
|
mail: nyota.uhura@enterprise.org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
|
||||||
|
dn: cn=suluh,ou=crew,dc=enterprise,dc=org
|
||||||
|
cn: suluh
|
||||||
|
sn: Sulu
|
||||||
|
gn: Hikaru
|
||||||
|
mail: hikaru.sulu@enterprise.org
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
|
||||||
|
dn: cn=chekovp,ou=crew,dc=enterprise,dc=org
|
||||||
|
cn: chekovp
|
||||||
|
sn: Chekov
|
||||||
|
gn: pavel
|
||||||
|
mail: pavel.chekov@enterprise.org
|
||||||
|
objectClass: inetOrgPerson
|
89
modules/ldap/examples/modify.go
Normal file
89
modules/ldap/examples/modify.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/juju2013/goldap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
LdapServer string = "localhost"
|
||||||
|
LdapPort uint16 = 389
|
||||||
|
BaseDN string = "dc=enterprise,dc=org"
|
||||||
|
BindDN string = "cn=admin,dc=enterprise,dc=org"
|
||||||
|
BindPW string = "enterprise"
|
||||||
|
Filter string = "(cn=kirkj)"
|
||||||
|
)
|
||||||
|
|
||||||
|
func search(l *ldap.Conn, filter string, attributes []string) (*ldap.Entry, *ldap.Error) {
|
||||||
|
search := ldap.NewSearchRequest(
|
||||||
|
BaseDN,
|
||||||
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
|
filter,
|
||||||
|
attributes,
|
||||||
|
nil)
|
||||||
|
|
||||||
|
sr, err := l.Search(search)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries))
|
||||||
|
if len(sr.Entries) == 0 {
|
||||||
|
return nil, ldap.NewError(ldap.ErrorDebugging, errors.New(fmt.Sprintf("no entries found for: %s", filter)))
|
||||||
|
}
|
||||||
|
return sr.Entries[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", LdapServer, LdapPort))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
// l.Debug = true
|
||||||
|
|
||||||
|
l.Bind(BindDN, BindPW)
|
||||||
|
|
||||||
|
log.Printf("The Search for Kirk ... %s\n", Filter)
|
||||||
|
entry, err := search(l, Filter, []string{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("could not get entry")
|
||||||
|
}
|
||||||
|
entry.PrettyPrint(0)
|
||||||
|
|
||||||
|
log.Printf("modify the mail address and add a description ... \n")
|
||||||
|
modify := ldap.NewModifyRequest(entry.DN)
|
||||||
|
modify.Add("description", []string{"Captain of the USS Enterprise"})
|
||||||
|
modify.Replace("mail", []string{"captain@enterprise.org"})
|
||||||
|
if err := l.Modify(modify); err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err = search(l, Filter, []string{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("could not get entry")
|
||||||
|
}
|
||||||
|
entry.PrettyPrint(0)
|
||||||
|
|
||||||
|
log.Printf("reset the entry ... \n")
|
||||||
|
modify = ldap.NewModifyRequest(entry.DN)
|
||||||
|
modify.Delete("description", []string{})
|
||||||
|
modify.Replace("mail", []string{"james.kirk@enterprise.org"})
|
||||||
|
if err := l.Modify(modify); err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err = search(l, Filter, []string{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("could not get entry")
|
||||||
|
}
|
||||||
|
entry.PrettyPrint(0)
|
||||||
|
}
|
52
modules/ldap/examples/search.go
Normal file
52
modules/ldap/examples/search.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/juju2013/goldap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ldapServer string = "adserver"
|
||||||
|
ldapPort uint16 = 3268
|
||||||
|
baseDN string = "dc=*,dc=*"
|
||||||
|
filter string = "(&(objectClass=user)(sAMAccountName=*)(memberOf=CN=*,OU=*,DC=*,DC=*))"
|
||||||
|
Attributes []string = []string{"memberof"}
|
||||||
|
user string = "*"
|
||||||
|
passwd string = "*"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
// l.Debug = true
|
||||||
|
|
||||||
|
err = l.Bind(user, passwd)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: Cannot bind: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
search := ldap.NewSearchRequest(
|
||||||
|
baseDN,
|
||||||
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
|
filter,
|
||||||
|
Attributes,
|
||||||
|
nil)
|
||||||
|
|
||||||
|
sr, err := l.Search(search)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries))
|
||||||
|
sr.PrettyPrint(0)
|
||||||
|
}
|
45
modules/ldap/examples/searchSSL.go
Normal file
45
modules/ldap/examples/searchSSL.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/juju2013/goldap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
LdapServer string = "localhost"
|
||||||
|
LdapPort uint16 = 636
|
||||||
|
BaseDN string = "dc=enterprise,dc=org"
|
||||||
|
Filter string = "(cn=kirkj)"
|
||||||
|
Attributes []string = []string{"mail"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l, err := ldap.DialSSL("tcp", fmt.Sprintf("%s:%d", LdapServer, LdapPort), nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.String())
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
// l.Debug = true
|
||||||
|
|
||||||
|
search := ldap.NewSearchRequest(
|
||||||
|
BaseDN,
|
||||||
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
|
Filter,
|
||||||
|
Attributes,
|
||||||
|
nil)
|
||||||
|
|
||||||
|
sr, err := l.Search(search)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries))
|
||||||
|
sr.PrettyPrint(0)
|
||||||
|
}
|
45
modules/ldap/examples/searchTLS.go
Normal file
45
modules/ldap/examples/searchTLS.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/juju2013/goldap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
LdapServer string = "localhost"
|
||||||
|
LdapPort uint16 = 389
|
||||||
|
BaseDN string = "dc=enterprise,dc=org"
|
||||||
|
Filter string = "(cn=kirkj)"
|
||||||
|
Attributes []string = []string{"mail"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", LdapServer, LdapPort), nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
// l.Debug = true
|
||||||
|
|
||||||
|
search := ldap.NewSearchRequest(
|
||||||
|
BaseDN,
|
||||||
|
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
|
Filter,
|
||||||
|
Attributes,
|
||||||
|
nil)
|
||||||
|
|
||||||
|
sr, err := l.Search(search)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries))
|
||||||
|
sr.PrettyPrint(0)
|
||||||
|
}
|
67
modules/ldap/examples/slapd.conf
Normal file
67
modules/ldap/examples/slapd.conf
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#
|
||||||
|
# See slapd.conf(5) for details on configuration options.
|
||||||
|
# This file should NOT be world readable.
|
||||||
|
#
|
||||||
|
include /private/etc/openldap/schema/core.schema
|
||||||
|
include /private/etc/openldap/schema/cosine.schema
|
||||||
|
include /private/etc/openldap/schema/inetorgperson.schema
|
||||||
|
|
||||||
|
# Define global ACLs to disable default read access.
|
||||||
|
|
||||||
|
# Do not enable referrals until AFTER you have a working directory
|
||||||
|
# service AND an understanding of referrals.
|
||||||
|
#referral ldap://root.openldap.org
|
||||||
|
|
||||||
|
pidfile /private/var/db/openldap/run/slapd.pid
|
||||||
|
argsfile /private/var/db/openldap/run/slapd.args
|
||||||
|
|
||||||
|
# Load dynamic backend modules:
|
||||||
|
# modulepath /usr/libexec/openldap
|
||||||
|
# moduleload back_bdb.la
|
||||||
|
# moduleload back_hdb.la
|
||||||
|
# moduleload back_ldap.la
|
||||||
|
|
||||||
|
# Sample security restrictions
|
||||||
|
# Require integrity protection (prevent hijacking)
|
||||||
|
# Require 112-bit (3DES or better) encryption for updates
|
||||||
|
# Require 63-bit encryption for simple bind
|
||||||
|
# security ssf=1 update_ssf=112 simple_bind=64
|
||||||
|
|
||||||
|
# Sample access control policy:
|
||||||
|
# Root DSE: allow anyone to read it
|
||||||
|
# Subschema (sub)entry DSE: allow anyone to read it
|
||||||
|
# Other DSEs:
|
||||||
|
# Allow self write access
|
||||||
|
# Allow authenticated users read access
|
||||||
|
# Allow anonymous users to authenticate
|
||||||
|
# Directives needed to implement policy:
|
||||||
|
# access to dn.base="" by * read
|
||||||
|
# access to dn.base="cn=Subschema" by * read
|
||||||
|
# access to *
|
||||||
|
# by self write
|
||||||
|
# by users read
|
||||||
|
# by anonymous auth
|
||||||
|
#
|
||||||
|
# if no access controls are present, the default policy
|
||||||
|
# allows anyone and everyone to read anything but restricts
|
||||||
|
# updates to rootdn. (e.g., "access to * by * read")
|
||||||
|
#
|
||||||
|
# rootdn can always read and write EVERYTHING!
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# BDB database definitions
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
database bdb
|
||||||
|
suffix "dc=enterprise,dc=org"
|
||||||
|
rootdn "cn=admin,dc=enterprise,dc=org"
|
||||||
|
# Cleartext passwords, especially for the rootdn, should
|
||||||
|
# be avoid. See slappasswd(8) and slapd.conf(5) for details.
|
||||||
|
# Use of strong authentication encouraged.
|
||||||
|
rootpw {SSHA}laO00HsgszhK1O0Z5qR0/i/US69Osfeu
|
||||||
|
# The database directory MUST exist prior to running slapd AND
|
||||||
|
# should only be accessible by the slapd and slap tools.
|
||||||
|
# Mode 700 recommended.
|
||||||
|
directory /private/var/db/openldap/openldap-data
|
||||||
|
# Indices to maintain
|
||||||
|
index objectClass eq
|
248
modules/ldap/filter.go
Normal file
248
modules/ldap/filter.go
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
|
||||||
|
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 += "="
|
||||||
|
switch packet.Children[1].Children[0].Tag {
|
||||||
|
case FilterSubstringsInitial:
|
||||||
|
ret += ber.DecodeString(packet.Children[1].Children[0].Data.Bytes()) + "*"
|
||||||
|
case FilterSubstringsAny:
|
||||||
|
ret += "*" + ber.DecodeString(packet.Children[1].Children[0].Data.Bytes()) + "*"
|
||||||
|
case FilterSubstringsFinal:
|
||||||
|
ret += "*" + ber.DecodeString(packet.Children[1].Children[0].Data.Bytes())
|
||||||
|
}
|
||||||
|
case FilterEqualityMatch:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "="
|
||||||
|
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
|
||||||
|
case FilterGreaterOrEqual:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += ">="
|
||||||
|
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
|
||||||
|
case FilterLessOrEqual:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "<="
|
||||||
|
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
|
||||||
|
case FilterPresent:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "=*"
|
||||||
|
case FilterApproxMatch:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "~="
|
||||||
|
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
var err error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
newPos := pos
|
||||||
|
switch filter[pos] {
|
||||||
|
case '(':
|
||||||
|
packet, newPos, err = compileFilter(filter, pos+1)
|
||||||
|
newPos++
|
||||||
|
return packet, newPos, err
|
||||||
|
case '&':
|
||||||
|
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd])
|
||||||
|
newPos, err = compileFilterSet(filter, pos+1, packet)
|
||||||
|
return packet, newPos, err
|
||||||
|
case '|':
|
||||||
|
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr])
|
||||||
|
newPos, err = compileFilterSet(filter, pos+1, 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+1)
|
||||||
|
packet.AppendChild(child)
|
||||||
|
return packet, newPos, err
|
||||||
|
default:
|
||||||
|
attribute := ""
|
||||||
|
condition := ""
|
||||||
|
for newPos < len(filter) && filter[newPos] != ')' {
|
||||||
|
switch {
|
||||||
|
case packet != nil:
|
||||||
|
condition += fmt.Sprintf("%c", filter[newPos])
|
||||||
|
case filter[newPos] == '=':
|
||||||
|
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
|
||||||
|
case filter[newPos] == '>' && filter[newPos+1] == '=':
|
||||||
|
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
|
||||||
|
newPos++
|
||||||
|
case filter[newPos] == '<' && filter[newPos+1] == '=':
|
||||||
|
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
|
||||||
|
newPos++
|
||||||
|
case filter[newPos] == '~' && filter[newPos+1] == '=':
|
||||||
|
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterLessOrEqual])
|
||||||
|
newPos++
|
||||||
|
case packet == nil:
|
||||||
|
attribute += fmt.Sprintf("%c", filter[newPos])
|
||||||
|
}
|
||||||
|
newPos++
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
|
||||||
|
switch {
|
||||||
|
case packet.Tag == FilterEqualityMatch && condition == "*":
|
||||||
|
packet.Tag = FilterPresent
|
||||||
|
packet.Description = FilterMap[uint64(packet.Tag)]
|
||||||
|
case packet.Tag == FilterEqualityMatch && condition[0] == '*' && condition[len(condition)-1] == '*':
|
||||||
|
// Any
|
||||||
|
packet.Tag = FilterSubstrings
|
||||||
|
packet.Description = FilterMap[uint64(packet.Tag)]
|
||||||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
|
||||||
|
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsAny, condition[1:len(condition)-1], "Any Substring"))
|
||||||
|
packet.AppendChild(seq)
|
||||||
|
case packet.Tag == FilterEqualityMatch && condition[0] == '*':
|
||||||
|
// Final
|
||||||
|
packet.Tag = FilterSubstrings
|
||||||
|
packet.Description = FilterMap[uint64(packet.Tag)]
|
||||||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
|
||||||
|
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsFinal, condition[1:], "Final Substring"))
|
||||||
|
packet.AppendChild(seq)
|
||||||
|
case packet.Tag == FilterEqualityMatch && condition[len(condition)-1] == '*':
|
||||||
|
// Initial
|
||||||
|
packet.Tag = FilterSubstrings
|
||||||
|
packet.Description = FilterMap[uint64(packet.Tag)]
|
||||||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
|
||||||
|
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsInitial, condition[:len(condition)-1], "Initial Substring"))
|
||||||
|
packet.AppendChild(seq)
|
||||||
|
default:
|
||||||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, condition, "Condition"))
|
||||||
|
}
|
||||||
|
newPos++
|
||||||
|
return packet, newPos, err
|
||||||
|
}
|
||||||
|
}
|
78
modules/ldap/filter_test.go
Normal file
78
modules/ldap/filter_test.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/johnweldon/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
type compileTest struct {
|
||||||
|
filterStr string
|
||||||
|
filterType int
|
||||||
|
}
|
||||||
|
|
||||||
|
var testFilters = []compileTest{
|
||||||
|
compileTest{filterStr: "(&(sn=Miller)(givenName=Bob))", filterType: FilterAnd},
|
||||||
|
compileTest{filterStr: "(|(sn=Miller)(givenName=Bob))", filterType: FilterOr},
|
||||||
|
compileTest{filterStr: "(!(sn=Miller))", filterType: FilterNot},
|
||||||
|
compileTest{filterStr: "(sn=Miller)", filterType: FilterEqualityMatch},
|
||||||
|
compileTest{filterStr: "(sn=Mill*)", filterType: FilterSubstrings},
|
||||||
|
compileTest{filterStr: "(sn=*Mill)", filterType: FilterSubstrings},
|
||||||
|
compileTest{filterStr: "(sn=*Mill*)", filterType: FilterSubstrings},
|
||||||
|
compileTest{filterStr: "(sn>=Miller)", filterType: FilterGreaterOrEqual},
|
||||||
|
compileTest{filterStr: "(sn<=Miller)", filterType: FilterLessOrEqual},
|
||||||
|
compileTest{filterStr: "(sn=*)", filterType: FilterPresent},
|
||||||
|
compileTest{filterStr: "(sn~=Miller)", filterType: FilterApproxMatch},
|
||||||
|
// compileTest{ filterStr: "()", filterType: FilterExtensibleMatch },
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
// Test Compiler and Decompiler
|
||||||
|
for _, i := range testFilters {
|
||||||
|
filter, err := CompileFilter(i.filterStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error())
|
||||||
|
} else if filter.Tag != uint8(i.filterType) {
|
||||||
|
t.Errorf("%q Expected %q got %q", i.filterStr, FilterMap[uint64(i.filterType)], FilterMap[uint64(filter.Tag)])
|
||||||
|
} else {
|
||||||
|
o, err := DecompileFilter(filter)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error())
|
||||||
|
} else if i.filterStr != o {
|
||||||
|
t.Errorf("%q expected, got %q", i.filterStr, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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++ {
|
||||||
|
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], _ = CompileFilter(i.filterStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
maxIdx := len(filters)
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
DecompileFilter(filters[i%maxIdx])
|
||||||
|
}
|
||||||
|
}
|
302
modules/ldap/ldap.go
Normal file
302
modules/ldap/ldap.go
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 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
|
||||||
|
)
|
||||||
|
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 := 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRequestDescriptions(packet *ber.Packet) {
|
||||||
|
packet.Description = "LDAP Request"
|
||||||
|
packet.Children[0].Description = "Message ID"
|
||||||
|
packet.Children[1].Description = ApplicationMap[packet.Children[1].Tag]
|
||||||
|
if len(packet.Children) == 3 {
|
||||||
|
addControlDescriptions(packet.Children[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
|
||||||
|
resultCode := packet.Children[1].Children[0].Value.(uint64)
|
||||||
|
packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[uint8(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(file, "")
|
||||||
|
packet := ber.DecodePacket(file)
|
||||||
|
addLDAPDescriptions(packet)
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 {
|
||||||
|
return uint8(response.Children[0].Value.(uint64)), response.Children[2].Value.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNetwork, "Invalid packet format"
|
||||||
|
}
|
123
modules/ldap/ldap_test.go
Normal file
123
modules/ldap/ldap_test.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ldapServer = "ldap.itd.umich.edu"
|
||||||
|
var ldapPort = uint16(389)
|
||||||
|
var baseDN = "dc=umich,dc=edu"
|
||||||
|
var filter = []string{
|
||||||
|
"(cn=cis-fac)",
|
||||||
|
"(&(objectclass=rfc822mailgroup)(cn=*Computer*))",
|
||||||
|
"(&(objectclass=rfc822mailgroup)(cn=*Mathematics*))"}
|
||||||
|
var attributes = []string{
|
||||||
|
"cn",
|
||||||
|
"description"}
|
||||||
|
|
||||||
|
func TestConnect(t *testing.T) {
|
||||||
|
fmt.Printf("TestConnect: starting...\n")
|
||||||
|
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
fmt.Printf("TestConnect: finished...\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearch(t *testing.T) {
|
||||||
|
fmt.Printf("TestSearch: starting...\n")
|
||||||
|
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
searchRequest := NewSearchRequest(
|
||||||
|
baseDN,
|
||||||
|
ScopeWholeSubtree, 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 TestSearchWithPaging(t *testing.T) {
|
||||||
|
fmt.Printf("TestSearchWithPaging: starting...\n")
|
||||||
|
l, err := 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 := NewSearchRequest(
|
||||||
|
baseDN,
|
||||||
|
ScopeWholeSubtree, DerefAlways, 0, 0, false,
|
||||||
|
filter[1],
|
||||||
|
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 testMultiGoroutineSearch(t *testing.T, l *Conn, results chan *SearchResult, i int) {
|
||||||
|
searchRequest := NewSearchRequest(
|
||||||
|
baseDN,
|
||||||
|
ScopeWholeSubtree, 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) {
|
||||||
|
fmt.Printf("TestMultiGoroutineSearch: starting...\n")
|
||||||
|
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
results := make([]chan *SearchResult, len(filter))
|
||||||
|
for i := range filter {
|
||||||
|
results[i] = make(chan *SearchResult)
|
||||||
|
go testMultiGoroutineSearch(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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
156
modules/ldap/modify.go
Normal file
156
modules/ldap/modify.go
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// File contains Modify functionality
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4511
|
||||||
|
//
|
||||||
|
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||||
|
// object LDAPDN,
|
||||||
|
// changes SEQUENCE OF change SEQUENCE {
|
||||||
|
// operation ENUMERATED {
|
||||||
|
// add (0),
|
||||||
|
// delete (1),
|
||||||
|
// replace (2),
|
||||||
|
// ... },
|
||||||
|
// modification PartialAttribute } }
|
||||||
|
//
|
||||||
|
// PartialAttribute ::= SEQUENCE {
|
||||||
|
// type AttributeDescription,
|
||||||
|
// vals SET OF value AttributeValue }
|
||||||
|
//
|
||||||
|
// AttributeDescription ::= LDAPString
|
||||||
|
// -- Constrained to <attributedescription>
|
||||||
|
// -- [RFC4512]
|
||||||
|
//
|
||||||
|
// AttributeValue ::= OCTET STRING
|
||||||
|
//
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
350
modules/ldap/search.go
Normal file
350
modules/ldap/search.go
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
// 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"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/asn1-ber"
|
||||||
|
)
|
||||||
|
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
|
||||||
|
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) GetAttributeValue(attribute string) string {
|
||||||
|
values := e.GetAttributeValues(attribute)
|
||||||
|
if len(values) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type EntryAttribute struct {
|
||||||
|
Name string
|
||||||
|
Values []string
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in a new issue