UNPKG

7.1 kBMarkdownView Raw
1
2# `ldap_acl(options, [goptions], callback)`
3
4Create new [ACLs](acls) for the OpenLDAP server.
5
6This implementation currently doesn't execute remote SSH commands. Instead, it
7connects directly to the LDAP database and thus requires a specific port to be
8accessible.
9
10## Options
11
12* `to`
13 What to control access to as a string.
14* `by`
15 Who to grant access to and the access to grant as an array
16 (eg: `{..., by:["ssf=64 anonymous auth"]}`).
17* `url`
18 Specify URI referring to the ldap server, alternative to providing an
19 [ldapjs client] instance.
20* `binddn`
21 Distinguished Name to bind to the LDAP directory, alternative to providing
22 an [ldapjs client] instance.
23* `passwd`
24 Password for simple authentication, alternative to providing an
25 [ldapjs client] instance.
26* `ldap`
27 Instance of an [ldapjs client][ldapclt], alternative to providing the `url`,
28 `binddn` and `passwd` connection properties.
29* `unbind`
30 Close the ldap connection, default to false if connection is an
31 [ldapjs client][ldapclt] instance.
32* `name`
33 Distinguish name storing the "olcAccess" property, using the database adress
34 (eg: "olcDatabase={2}bdb,cn=config").
35* `overwrite`
36 Overwrite existing "olcAccess", default is to merge.
37* `log`
38 Function called with a log related messages.
39* `acl`
40 In case of multiple acls, regroup "before", "to" and "by" as an array.
41
42## Example
43
44```js
45require('mecano/alt/ldap_acl')({
46 url: 'ldap://openldap.server/',
47 binddn: 'cn=admin,cn=config',
48 passwd: 'password',
49 name: 'olcDatabase={2}bdb,cn=config',
50 acls: [{
51 before: 'dn.subtree="dc=domain,dc=com"',
52 to: 'dn.subtree="ou=users,dc=domain,dc=com"',
53 by: [
54 'dn.exact="ou=users,dc=domain,dc=com" write',
55 "dn.base='gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth' read",
56 "* none"
57 ]
58 },{
59 to: 'dn.subtree="dc=domain,dc=com"',
60 by: [
61 'dn.exact="ou=kerberos,dc=domain,dc=com" write'
62 ]
63 }]
64}, function(err, modified){
65 console.log(err ? err.message : "ACL modified: " + !!modified);
66});
67```
68
69 module.exports = (goptions, options, callback) ->
70 options.acls ?= [{}]
71 updated = false
72 each(options.acls)
73 .run (acl, next) ->
74 acl.before ?= options.before
75 acl.to ?= options.to
76 acl.by ?= options.by
77 client = null
78 acl.to = acl.to.trim()
79 for b, i in acl.by
80 acl.by[i] = b.trim()
81 connect = ->
82 # if options.ldap instanceof ldap_client
83 if options.ldap?.url?.protocol?.indexOf('ldap') is 0
84 client = options.ldap
85 return search()
86 options.log? 'Open and bind connection'
87 client = ldap.createClient url: options.url
88 client.bind options.binddn, options.passwd, (err) ->
89 return end err if err
90 search()
91 search = ->
92 options.log? 'Search attribute olcAccess'
93 client.search options.name,
94 scope: 'base'
95 attributes: ['olcAccess']
96 , (err, search) ->
97 return unbind err if err
98 olcAccess = null
99 search.on 'searchEntry', (entry) ->
100 options.log? "Found #{JSON.stringify entry.object}"
101 # typeof olcAccess may be undefined, array or string
102 olcAccess = entry.object.olcAccess or []
103 olcAccess = [olcAccess] unless Array.isArray olcAccess
104 search.on 'end', ->
105 options.log? "Attribute olcAccess was #{JSON.stringify olcAccess}"
106 parse olcAccess
107 parse = (_olcAccess) ->
108 olcAccess = []
109 for access, i in _olcAccess
110 to = ''
111 bys = []
112 buftype = 0 # 0: start, 1: to, 2:by
113 buf = ''
114 for c, i in access
115 buf += c
116 if buftype is 0
117 if /to$/.test buf
118 buf = ''
119 buftype = 1
120 if buftype is 1
121 if matches = /^(.*)by$/.exec buf
122 to = matches[1].trim()
123 buf = ''
124 buftype = 2
125 if buftype is 2
126 if matches = /^(.*)by$/.exec buf
127 bys.push matches[1].trim()
128 buf = ''
129 else if i+1 is access.length
130 bys.push buf.trim()
131 olcAccess.push
132 to: to
133 by: bys
134 do_diff olcAccess
135 do_diff = (olcAccess) ->
136 toAlreadyExist = false
137 for access, i in olcAccess
138 continue unless acl.to is access.to
139 toAlreadyExist = true
140 fby = unless options.overwrite then access.by else []
141 for oby in acl.by
142 found = false
143 for aby in access.by
144 if oby is aby
145 found = true
146 break
147 unless found
148 updated = true
149 fby.push oby
150 olcAccess[i].by = fby
151 unless toAlreadyExist
152 updated = true
153 # place before
154 if acl.before
155 found = null
156 for access, i in olcAccess
157 found = i if access.to is acl.before
158 # throw new Error 'Before does not match any "to" rule' unless found?
159 olcAccess.splice found-1, 0, to: acl.to, by: acl.by
160 # place after
161 else if acl.after
162 found = false
163 for access, i in olcAccess
164 found = i if access.to is options.after
165 # throw new Error 'After does not match any "to" rule'
166 olcAccess.splice found, 0, to: acl.to, by: acl.by
167 # append
168 else
169 olcAccess.push to: acl.to, by: acl.by
170 if updated then stringify(olcAccess) else unbind()
171 stringify = (olcAccess) ->
172 for access, i in olcAccess
173 value = "{#{i}}to #{access.to}"
174 for bie in access.by
175 value += " by #{bie}"
176 olcAccess[i] = value
177 save olcAccess
178 save = (olcAccess) ->
179 change = new ldap.Change
180 operation: 'replace'
181 modification: olcAccess: olcAccess
182 client.modify options.name, change, (err) ->
183 unbind err
184 unbind = (err) ->
185 options.log? 'Unbind connection'
186 # return end err if options.ldap instanceof ldap_client and not options.unbind
187 return end err if options.ldap?.url?.protocol?.indexOf('ldap') is 0 and not options.unbind
188 client.unbind (e) ->
189 return next e if e
190 end err
191 end = (err) ->
192 next err
193 connect()
194 .then (err) ->
195 next err, updated
196
197## Dependencies
198
199 each = require 'each'
200 ldap = require 'ldapjs'
201 wrap = require '../misc/wrap'
202
203[acls]: http://www.openldap.org/doc/admin24/access-control.html
204[ldapclt]: http://ldapjs.org/client.html
205
206