Skip to content
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ To build and run from the command line:
* Clone this repo
* Run `dep ensure` (must have [dep](https://github.com/golang/dep) installed )
* Run `go build`
* Run `FLYTE_API_URL=<URL> BIND_USERNAME=<USERNAME> BIND_PASSWORD=<PASSWORD> LDAP_URL=<LDAP_URL> GROUP_ATTRIBUTE=<GROUP_ATTRIBUTE> ATTRIBUTES=<ATTRIBUTES> BASE_DN=<BASE_DN> SEARCH_FILTER=<SEARCH_FILTER> SEARCH_TIMEOUT_IN_SECONDS=<SEARCH_TIMEOUT_IN_SECONDS> ./flyte-ldap`
* Run `FLYTE_API_URL=<URL> BIND_USERNAME=<USERNAME> BIND_PASSWORD=<PASSWORD> LDAP_URL=<LDAP_URL> GROUP_ATTRIBUTE=<GROUP_ATTRIBUTE> ATTRIBUTES=<ATTRIBUTES> BASE_DN=<BASE_DN> SEARCH_FILTER=<SEARCH_FILTER> SEARCH_TIMEOUT_IN_SECONDS=<SEARCH_TIMEOUT_IN_SECONDS> ENABLE_TLS=<True/False> INSECURE_SKIP_VERIFY=<True/False> ./flyte-ldap`
* All of these environment variables need to be provided with the exception of 'SEARCH_TIMEOUT_IN_SECONDS', which has a default (see main.go).
#### Example
* Run `FLYTE_API_URL='http://myflyteapi.com' BIND_USERNAME='someUsername' BIND_PASSWORD='somePassword' LDAP_URL='my.ldap.com:123' GROUP_ATTRIBUTE='cn' ATTRIBUTES='memberOf' BASE_DN='DC=QQ,DC=WOW,DC=XYZ,DC=com' SEARCH_FILTER='(mailNickname={username})' SEARCH_TIMEOUT_IN_SECONDS='20' ./flyte-ldap`
* Run `FLYTE_API_URL='http://myflyteapi.com' BIND_USERNAME='someUsername' BIND_PASSWORD='somePassword' LDAP_URL='my.ldap.com:123' GROUP_ATTRIBUTE='cn' ATTRIBUTES='memberOf' BASE_DN='DC=QQ,DC=WOW,DC=XYZ,DC=com' SEARCH_FILTER='(mailNickname={username})' SEARCH_TIMEOUT_IN_SECONDS='20' ENABLE_TLS=<True/False> INSECURE_SKIP_VERIFY=<True/False> ./flyte-ldap`

### Run tests
To run the unit tests:
Expand All @@ -25,10 +25,10 @@ To run the unit tests:
### Docker
To build and run from docker
* Run `docker build -t flyte-ldap .`
* Run `docker run -e FLYTE_API_URL=<URL> -e BIND_USERNAME=<USERNAME> -e BIND_PASSWORD=<PASSWORD> -e LDAP_URL=<LDAP_URL> -e GROUP_ATTRIBUTE=<GROUP_ATTRIBUTE> -e ATTRIBUTES=<ATTRIBUTES> -e BASE_DN=<BASE_DN> -e SEARCH_FILTER=<SEARCH_FILTER> -e SEARCH_TIMEOUT_IN_SECONDS=<SEARCH_TIMEOUT_IN_SECONDS> flyte-ldap`
* Run `docker run -e FLYTE_API_URL=<URL> -e BIND_USERNAME=<USERNAME> -e BIND_PASSWORD=<PASSWORD> -e LDAP_URL=<LDAP_URL> -e GROUP_ATTRIBUTE=<GROUP_ATTRIBUTE> -e ATTRIBUTES=<ATTRIBUTES> -e BASE_DN=<BASE_DN> -e SEARCH_FILTER=<SEARCH_FILTER> -e SEARCH_TIMEOUT_IN_SECONDS=<SEARCH_TIMEOUT_IN_SECONDS> -e ENABLE_TLS='True' -e INSECURE_SKIP_VERIFY='True' flyte-ldap`
* All of these environment variables need to be provided with the exception of 'SEARCH_TIMEOUT_IN_SECONDS', which has a default (see main.go).
#### Example
* Run `docker run -e FLYTE_API_URL='http://myflyteapi.com' -e BIND_USERNAME='someUsername' -e BIND_PASSWORD='somePassword' -e LDAP_URL='my.ldap.com:123' -e GROUP_ATTRIBUTE='cn' -e ATTRIBUTES='memberOf' -e BASE_DN='DC=QQ,DC=WOW,DC=XYZ,DC=com' -e SEARCH_FILTER='(mailNickname={username})' -e SEARCH_TIMEOUT_IN_SECONDS='20' flyte-ldap`
* Run `docker run -e FLYTE_API_URL='http://myflyteapi.com' -e BIND_USERNAME='someUsername' -e BIND_PASSWORD='somePassword' -e LDAP_URL='my.ldap.com:123' -e GROUP_ATTRIBUTE='cn' -e ATTRIBUTES='memberOf' -e BASE_DN='DC=QQ,DC=WOW,DC=XYZ,DC=com' -e SEARCH_FILTER='(mailNickname={username})' -e SEARCH_TIMEOUT_IN_SECONDS='20' -e ENABLE_TLS='True' -e INSECURE_SKIP_VERIFY='True' flyte-ldap`


#### LDAP Attribute explanation
Expand Down
20 changes: 14 additions & 6 deletions group/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ import (
)

type SearchDetails struct {
Attributes []string // i.e. the attributes to be returned by the group, e.g. 'memberOf'
BaseDn string
SearchFilter string
GroupAttribute string // the attribute that gives the name of the group from the attribute values, e.g. 'cn'
SearchTimeout int
Attributes []string // i.e. the attributes to be returned by the group, e.g. 'memberOf'
BaseDn string
SearchFilter string
GroupAttribute string // the attribute that gives the name of the group from the attribute values, e.g. 'cn'
SearchTimeout int
EnableTLS bool
InsecureSkipVerify bool
}

type Searcher interface {
Expand All @@ -43,7 +45,13 @@ func NewSearcher(client ldap.Client) Searcher {
}

func (searcher *searcher) GetGroupsFor(sd *SearchDetails, username string) ([]string, error) {
if err := searcher.client.Connect(); err != nil {
var err error
if sd.EnableTLS == true {
err = searcher.client.ConnectTls(sd.InsecureSkipVerify)
} else {
err = searcher.client.Connect()
}
if err != nil {
return nil, err
}
defer searcher.client.Close()
Expand Down
39 changes: 30 additions & 9 deletions group/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func TestClientConnectAndCloseAreCalledWhenCallingGetGroupsFor(t *testing.T) {
isClientConnectCalled = true
return nil
},
connectTls: func(insecureSkipVerify bool) error {
isClientConnectCalled = true
return nil
},
close: func() {
isClientCloseCalled = true
},
Expand Down Expand Up @@ -59,6 +63,9 @@ func TestErrorIsReturnedIfClientConnectError(t *testing.T) {
connect: func() error {
return errors.New("Meh")
},
connectTls: func(insecureSkipVerify bool) error {
return errors.New("Meh")
},
}
searcher := NewSearcher(mockClient)
searchDetails := &SearchDetails{}
Expand Down Expand Up @@ -91,7 +98,10 @@ func TestSearchShouldReturnsUserGroups(t *testing.T) {
}
mockClient := &mockClient{
connect: func() error { return nil },
close: func() {},
connectTls: func(insecureSkipVerify bool) error {
return nil
},
close: func() {},
search: func(sr ldap.SearchRequest) (*ldapClient.SearchResult, error) {
return returnedSearchResults, nil
}}
Expand Down Expand Up @@ -120,7 +130,10 @@ func TestSearchShouldReturnsUserGroups(t *testing.T) {
func TestSearchShouldNotReturnsUserGroupsIfNoSearchResultsAreReturned(t *testing.T) {
mockClient := &mockClient{
connect: func() error { return nil },
close: func() {},
connectTls: func(insecureSkipVerify bool) error {
return nil
},
close: func() {},
search: func(sr ldap.SearchRequest) (*ldapClient.SearchResult, error) {
return &ldapClient.SearchResult{}, nil
}}
Expand All @@ -139,8 +152,10 @@ func TestSearchShouldNotReturnsUserGroupsIfNoSearchResultsAreReturned(t *testing

func TestSearchShouldReturnClientSearchError(t *testing.T) {
mockClient := &mockClient{
connect: func() error { return nil },
close: func() {},
connect: func() error { return nil },
connectTls: func(insecureSkipVerify bool) error { return nil },

close: func() {},
search: func(sr ldap.SearchRequest) (*ldapClient.SearchResult, error) {
return nil, errors.New("Some error")
}}
Expand All @@ -160,8 +175,9 @@ func TestSearchShouldReturnClientSearchError(t *testing.T) {
func TestCorrectParametersArePassedToClientSearch(t *testing.T) {
var searchRequest ldap.SearchRequest
mockClient := &mockClient{
connect: func() error { return nil },
close: func() {},
connect: func() error { return nil },
connectTls: func(insecureSkipVerify bool) error { return nil },
close: func() {},
search: func(sr ldap.SearchRequest) (*ldapClient.SearchResult, error) {
searchRequest = sr
return &ldapClient.SearchResult{}, nil
Expand Down Expand Up @@ -239,9 +255,14 @@ func someSearchDetails() *SearchDetails {
}

type mockClient struct {
connect func() error
close func()
search func(sr ldap.SearchRequest) (*ldapClient.SearchResult, error)
connect func() error
connectTls func(insecureSkipVerify bool) error
close func()
search func(sr ldap.SearchRequest) (*ldapClient.SearchResult, error)
}

func (c *mockClient) ConnectTls(insecureSkipVerify bool) error {
return c.connectTls(true)
}

func (c *mockClient) Connect() error {
Expand Down
19 changes: 19 additions & 0 deletions ldap/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ limitations under the License.
package ldap

import (
"crypto/tls"
"fmt"
"gopkg.in/ldap.v2"
)

type Client interface {
Connect() error
ConnectTls(insecureSkipVerify bool) error
Search(sr SearchRequest) (*ldap.SearchResult, error)
Close()
}
Expand Down Expand Up @@ -71,6 +73,23 @@ func (c *ldapClient) Connect() error {
return nil
}

func (c *ldapClient) ConnectTls(insecureSkipVerify bool) error {
ldapConn, err := ldap.DialTLS("tcp", c.ldapServerUrl, &tls.Config{InsecureSkipVerify: insecureSkipVerify})
if err != nil {
return fmt.Errorf("Cannot connect to LDAP: %v", err)
}

err = ldapConn.Bind(c.bindUsername, c.bindPassword)
if err != nil {
ldapConn.Close()
return fmt.Errorf("Cannot bind to LDAP: %v", err)
}

c.ldapSearcher = ldapConn

return nil
}

func (c *ldapClient) Search(sr SearchRequest) (*ldap.SearchResult, error) {
searchRequest := &ldap.SearchRequest{
BaseDN: sr.BaseDn,
Expand Down
20 changes: 15 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,25 @@ func main() {
if err != nil {
logger.Fatalf("LDAP group timeout '%v' not convertible to an integer. Error: %v", configVal("SEARCH_TIMEOUT_IN_SECONDS"), err)
}
tlsEnabledFlag, err := strconv.ParseBool(configVal("ENABLE_TLS"))
if err != nil {
logger.Fatalf("TLS enabled flag is not provided '%v' Error: %v", configVal("ENABLE_TLS"), err)
}
insecureSkipVerify, err := strconv.ParseBool(configVal("INSECURE_SKIP_VERIFY"))
if err != nil {
insecureSkipVerify = false
}

lc := ldap.NewClient(configVal("BIND_USERNAME"), configVal("BIND_PASSWORD"), configVal("LDAP_URL"))
searcher := group.NewSearcher(lc)
searchDetails := &group.SearchDetails{
Attributes: strings.Split(configVal("ATTRIBUTES"), ","),
BaseDn: configVal("BASE_DN"),
SearchFilter: configVal("SEARCH_FILTER"),
SearchTimeout: searchTimeout,
GroupAttribute: configVal("GROUP_ATTRIBUTE"),
Attributes: strings.Split(configVal("ATTRIBUTES"), ","),
BaseDn: configVal("BASE_DN"),
SearchFilter: configVal("SEARCH_FILTER"),
SearchTimeout: searchTimeout,
GroupAttribute: configVal("GROUP_ATTRIBUTE"),
EnableTLS: tlsEnabledFlag,
InsecureSkipVerify: insecureSkipVerify,
}

packDef := flyte.PackDef{
Expand Down