package main /* * Introduction * ============ * To get a sample of what this backend can do: * - example.com: http://127.0.0.1:8334/login#type=ldap&hostname=ldap://ldap.forumsys.com&bind_cn=uid%3Dtesla,dc%3Dexample,dc%3Dcom&bind_password=password&base_dn=dc%3Dexample,dc%3Dcom * - freeipa: http://127.0.0.1:8334/login#type=ldap&hostname=ldap://ipa.demo1.freeipa.org&bind_cn=uid%3Dadmin,cn%3Dusers,cn%3Daccounts,dc%3Ddemo1,dc%3Dfreeipa,dc%3Dorg&bind_password=Secret123&base_dn=dc%3Ddemo1,dc%3Dfreeipa,dc%3Dorg * * Compilation * =========== * go build -buildmode=plugin -o dist/data/plugin/backend_ldap.so server/plugin/plg_backend_ldap/index.go */ import ( "encoding/json" "fmt" . "github.com/mickael-kerjean/filestash/server/common" "gopkg.in/ldap.v3" "io" "os" "path/filepath" "sort" "strings" ) func Init(config *Configuration) { Backend.Register("ldap", LDAP{}) } type LDAP struct { dial *ldap.Conn baseDN string } func (this LDAP) Init(params map[string]string, app *App) (IBackend, error) { dialURL := func() string { if params["port"] == "" { // default port will be set by the LDAP library return params["hostname"] } return fmt.Sprintf("%s:%s", params["hostname"], params["port"]) }() l, err := ldap.DialURL(dialURL) if err != nil { return nil, err } if err = l.Bind(params["bind_cn"], params["bind_password"]); err != nil { return nil, err } return &LDAP{ baseDN: params["base_dn"], dial: l }, nil } func (this LDAP) LoginForm() Form { return Form{ Elmnts: []FormElement{ FormElement{ Name: "type", Type: "hidden", Value: "ldap", }, FormElement{ Name: "hostname", Type: "text", Placeholder: "Hostname", }, FormElement{ Name: "bind_cn", Type: "text", Placeholder: "bind CN", }, FormElement{ Name: "bind_password", Type: "password", Placeholder: "Bind CN password", }, FormElement{ Name: "base_dn", Type: "text", Placeholder: "Base DN", }, FormElement{ Name: "advanced", Type: "enable", Placeholder: "Advanced", Target: []string{"ldap_path", "ldap_port"}, }, FormElement{ Id: "ldap_path", Name: "path", Type: "text", Placeholder: "Path", }, FormElement{ Id: "ldap_port", Name: "port", Type: "number", Placeholder: "Port", }, }, } } func (this LDAP) Ls(path string) ([]os.FileInfo, error) { baseDN := this.pathToBase(path) files := make([]os.FileInfo, 0) // explore the current folder sr, err := this.dial.Search(ldap.NewSearchRequest( baseDN, ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{"objectClass"}, nil, )) if err != nil { return files, err } for i:=0; i=0; i-- { if err == nil { err = this.dial.Del(&ldap.DelRequest{ DN: sr.Entries[i].DN, }) } } return err } func (this LDAP) Mv(from string, to string) error { toBase := this.pathToBase(to) fromBase := this.pathToBase(from) return this.dial.ModifyDN(&ldap.ModifyDNRequest{ DN: fromBase, NewRDN: func(t string) string { a := strings.Split(t, ",") if len(a) == 0 { return t } return a[0] }(toBase), DeleteOldRDN: true, NewSuperior: func(t string) string { a := strings.Split(t, ",") if len(a) == 0 { return t } return strings.Join(a[1:], ",") }(toBase), }) } func (this LDAP) Touch(path string) error { ldapNode := strings.Split(filepath.Base(path), "=") if len(ldapNode) != 2 { return ErrNotValid } var objectClass []string switch ldapNode[0] { case "cn": objectClass = []string{"inetOrgPerson", "posixAccount"} default: return ErrNotValid } ldapNode[1] = strings.TrimSuffix(ldapNode[1], ".form") var uniqueForms map[string]FormElement = make(map[string]FormElement) for i:=0; i=0; i-- { reversedPath = append(reversedPath, pathArray[i]) } return strings.Join(append(reversedPath, baseArray...), ",") } func (this LDAP) autocompleteLDAP(filter string, value string) []string { val := []string{} sr, err := this.dial.Search(ldap.NewSearchRequest( this.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{value}, nil, )); if err != nil { return val } for i:=0; i