summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Messias Cordeiro <alancordeiro@gmail.com>2013-12-09 15:25:50 +0000
committerAlan Messias Cordeiro <alancordeiro@gmail.com>2013-12-09 15:25:50 +0000
commit64522968bc69d1fa839c64874a543398cf759473 (patch)
treee5b5c3ae9dd0b5e741b3a673d4cffc63e0ea287d
downloadacf-openldap-0.1.tar.bz2
acf-openldap-0.1.tar.xz
Initial commitv0.1
-rw-r--r--openldap-controller.lua26
l---------openldap-expert-html.lsp1
-rw-r--r--openldap-listcerts-html.lsp41
-rw-r--r--openldap-logfile-html.lsp6
-rw-r--r--openldap-model.lua51
-rw-r--r--openldap-model.lua.orig331
l---------openldap-status-html.lsp1
-rw-r--r--openldap-viewconfig-html.lsp77
-rw-r--r--openldap.menu4
-rw-r--r--openldap.roles3
10 files changed, 541 insertions, 0 deletions
diff --git a/openldap-controller.lua b/openldap-controller.lua
new file mode 100644
index 0000000..4ba9a0c
--- /dev/null
+++ b/openldap-controller.lua
@@ -0,0 +1,26 @@
+local mymodule = {}
+
+mymodule.mvc = {}
+mymodule.mvc.on_load = function(self, parent)
+ self.model.set_processname("slapd")
+end
+
+mymodule.default_action = "status"
+
+mymodule.status = function(self)
+ return self.model.getstatus()
+end
+
+mymodule.startstop = function(self)
+ return self.handle_form(self, self.model.get_startstop, self.model.startstop_service, self.clientdata)
+end
+
+mymodule.expert = function(self)
+ return self.handle_form(self, self.model.get_filecontent, self.model.update_filecontent, self.clientdata, "Save", "Edit Config File", "Config File Saved")
+end
+
+mymodule.viewconfig = function(self)
+ return self.model.get_config()
+end
+
+return mymodule
diff --git a/openldap-expert-html.lsp b/openldap-expert-html.lsp
new file mode 120000
index 0000000..207f324
--- /dev/null
+++ b/openldap-expert-html.lsp
@@ -0,0 +1 @@
+../expert-html.lsp \ No newline at end of file
diff --git a/openldap-listcerts-html.lsp b/openldap-listcerts-html.lsp
new file mode 100644
index 0000000..e0043c0
--- /dev/null
+++ b/openldap-listcerts-html.lsp
@@ -0,0 +1,41 @@
+<% local view, viewlibrary, page_info, session = ... %>
+<% htmlviewfunctions = require("htmlviewfunctions") %>
+<% html = require("acf.html") %>
+
+<% htmlviewfunctions.displaycommandresults({"deletecert", "generatedhparams"}, session) %>
+<% htmlviewfunctions.displaycommandresults({"uploadcert"}, session, true) %>
+
+<H1><%= html.html_escape(view.label) %></H1>
+
+<DL>
+<TABLE>
+ <TR style="background:#eee;font-weight:bold;">
+ <TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Action</TD>
+ <TD style="white-space:nowrap;text-align:left;" class="header">Certificate</TD>
+ </TR>
+<% for i,cert in ipairs(view.value) do %>
+ <TR>
+ <TD style="padding-right:20px;white-space:nowrap;">
+ <%= html.link{value=page_info.script..page_info.prefix..page_info.controller.."/deletecert?submit=true&cert="..cert.."&redir="..page_info.orig_action, label="Delete "} %>
+ <% if not string.find(cert, "%-key") then %>
+ <%= html.link{value=page_info.script..page_info.prefix..page_info.controller.."/viewcert?cert="..cert.."&redir="..page_info.orig_action, label="View "} %>
+ <% end %>
+ </TD>
+ <TD style="white-space:nowrap;"><%= html.html_escape(cert) %></TD>
+ </TR>
+<% end %>
+</TABLE></DL>
+
+<% if viewlibrary.dispatch_component and viewlibrary.check_permission("uploadcert") then
+ viewlibrary.dispatch_component("uploadcert")
+end %>
+
+<% if viewlibrary.check_permission("generatedhparams") then %>
+<H1>Diffie Hellman Parameters</H1>
+<DL>
+<form action="<%= html.html_escape(page_info.script .. page_info.prefix .. page_info.controller .. "/generatedhparams") %>">
+<DT>Generate Diffie Hellman parameters</DT>
+<DD><input class="submit" type="submit" name="submit" value="Generate"></DD>
+</form>
+</DL>
+<% end %>
diff --git a/openldap-logfile-html.lsp b/openldap-logfile-html.lsp
new file mode 100644
index 0000000..d320dd1
--- /dev/null
+++ b/openldap-logfile-html.lsp
@@ -0,0 +1,6 @@
+<% local data, viewlibrary = ...
+%>
+
+<% if viewlibrary and viewlibrary.dispatch_component then
+ viewlibrary.dispatch_component("alpine-baselayout/logfiles/view", {filename="/var/log/messages", grep="slapd"})
+end %>
diff --git a/openldap-model.lua b/openldap-model.lua
new file mode 100644
index 0000000..112dedb
--- /dev/null
+++ b/openldap-model.lua
@@ -0,0 +1,51 @@
+local mymodule = {}
+
+modelfunctions = require("modelfunctions")
+posix = require("posix")
+format = require("acf.format")
+fs = require("acf.fs")
+processinfo = require("acf.processinfo")
+validator = require("acf.validator")
+date = require("acf.date")
+
+local processname = "slapd"
+local packagename = "openldap"
+local configfile = "/etc/openldap/slapd.conf"
+
+function mymodule.set_processname(p)
+ processname = p
+ configfile = "/etc/openldap/"..processname..".conf"
+end
+
+local function log_content( f )
+ return fs.read_file(f) or ""
+end
+
+function mymodule.getstatus()
+ return modelfunctions.getstatus(processname, packagename, "OpenLDAP Status")
+end
+
+function mymodule.get_startstop(self, clientdata)
+ return modelfunctions.get_startstop(processname)
+end
+
+function mymodule.startstop_service(self, startstop, action)
+ return modelfunctions.startstop_service(startstop, action)
+end
+
+function mymodule.get_logfile(f)
+ local config = log_content(configfile)
+ return cfe({ value=config.log or "", label="Log file" })
+end
+
+function mymodule.get_filecontent()
+
+ return modelfunctions.getfiledetails(configfile)
+end
+
+function mymodule.update_filecontent(self, filedetails)
+
+ return modelfunctions.setfiledetails(self, filedetails, {configfile})
+end
+
+return mymodule
diff --git a/openldap-model.lua.orig b/openldap-model.lua.orig
new file mode 100644
index 0000000..1b0d830
--- /dev/null
+++ b/openldap-model.lua.orig
@@ -0,0 +1,331 @@
+local mymodule = {}
+
+modelfunctions = require("modelfunctions")
+posix = require("posix")
+format = require("acf.format")
+fs = require("acf.fs")
+processinfo = require("acf.processinfo")
+validator = require("acf.validator")
+date = require("acf.date")
+
+local processname = "slapd"
+local packagename = "openldap"
+local configfile = "/etc/openldap/slapd.conf"
+local baseurl = "/etc/openldap/"
+
+function mymodule.set_processname(p)
+ processname = p
+ configfile = "/etc/openldap/"..processname..".conf"
+end
+
+-- ################################################################################
+-- LOCAL FUNCTIONS
+
+local function config_content( f )
+ local config = {}
+ local lines = format.parse_linesandwords(fs.read_file(f) or "", "[#;]")
+ -- there can be multiple entries
+ for i,linetable in ipairs(lines) do
+ if config[linetable[1]] then
+ config[linetable[1]] = config[linetable[1]] .. "\n" .. (table.concat(linetable, " ", 2) or "")
+ else
+ config[linetable[1]] = table.concat(linetable, " ", 2) or ""
+ end
+ end
+
+ config.name = f
+ if config.remote then
+ config.remoteport = string.match ( config.remote, "^%S+%s+(%S*)" )
+ end
+ if not ( config.log ) then
+ config.log = config["log-append"]
+ end
+ if not ( config["max-clients"] ) then
+ config["max-clients"] = "Unlimited"
+ end
+ if not ( config["local"] ) then
+ config["local"] = "0.0.0.0"
+ end
+ return config
+end
+
+local function check_valid_config (config)
+ config.errtxt = nil
+ if not (config.client) or not (config.ca) or not (config.cert) or not (config.key) or not (config.dev) or not (config.proto) or not (config.remote) then
+ config.errtxt = ""
+ if not (config.ca) then
+ config.errtxt = config.errtxt .. "Check CA; "
+ end
+ if not (config.cert) then
+ config.errtxt = config.errtxt .. "Check CERT; "
+ end
+ if not (config.key) then
+ config.errtxt = config.errtxt .. "Check KEY; "
+ end
+ if not (config.dev) then
+ config.errtxt = config.errtxt .. "Check DEV; "
+ end
+ if not (config.proto) then
+ config.errtxt = config.errtxt .. "Check PROTO; "
+ end
+ if (config.client) or not (config.ca) or not (config.cert) or not (config.key) or not (config.dev) or not (config.proto) or not (config.port) then
+ config.type = nil
+ else
+ config.type = "server"
+ config.errtxt = nil
+ end
+ else
+ config.type = "client"
+ config.errtxt = nil
+ end
+ if not (config.type) then config.type = "unknown" end
+ return config.type, config.errtxt
+end
+
+local function clientlist( statusfile )
+ local clientlist = {}
+ local routinglist = {}
+ local datechange = {}
+ local list = {}
+ if (statusfile) then
+ local f = fs.read_file_as_array( statusfile )
+ local clientlst = false
+ local routinglst = false
+ if ( f ) then
+ for k,v in ipairs(f) do
+ local col = format.string_to_table(v, ",")
+ if ( col[1] == "ROUTING TABLE" ) or ( col[1] == "GLOBAL STATS" ) then
+ clientlst = false
+ routinglst = false
+ end
+ if ( clientlst ) then
+ table.insert(clientlist, { CN=col[1],
+ REALADDR=col[2],
+ BYTESRCV=col[3],
+ BYTESSND=col[4],
+ CONN=col[5] } )
+ end
+ if ( routinglst ) then
+ table.insert(routinglist, { VIRTADDR=col[1],
+ CN=col[2],
+ REALADDR=col[3],
+ LAST=col[4] } )
+ if (col[4]) then
+ local month,day,hour,min,sec,year = string.match(col[4],"^%S+%s+(%S+)%s+(%S+)%s+(%d%d):(%d%d):(%d%d)%s+(%S+)")
+ table.insert(datechange, { year=year,
+ month=date.abr_month_num(month),
+ day=day,
+ hour=hour,
+ min=min,
+ sec=sec } )
+ end
+ end
+ if ( col[1] == "Virtual Address" ) then
+ routinglst = true
+ end
+ if ( col[1] == "Common Name" ) then
+ clientlst = true
+ end
+
+ end
+ end
+ end
+ -- JOIN 'CLIENT_LIST' and 'ROUTING_LIST' TABLES INTO ONE TABLE AND LATER ON PRESENT THIS TABLE
+ for k,v in ipairs(clientlist) do
+ for kk,vv in ipairs(routinglist) do
+ if ( v.CN == vv.CN ) then
+ table.insert(list, { CN=v.CN, REALADDR=v.REALADDR, BYTESRCV=v.BYTESRCV, BYTESSND=v.BYTESSND, VIRTADDR=vv.VIRTADDR, CONN=v.CONN, LAST = LAST } )
+ end
+ end
+ end
+ local lastdatechangetxt, lastdatechangediff
+ if ((#clientlist > 0) and (#datechange > 0)) then
+ local lastdatechange = date.date_to_seconds(datechange)
+ lastdatechangetxt = os.date("%c", lastdatechange[#lastdatechange])
+ lastdatechangediff = os.time() - os.date(lastdatechange[#lastdatechange])
+ if (lastdatechangediff > 60) then
+ lastdatechangediff = math.modf(lastdatechangediff / 60) .. " min"
+ else
+ lastdatechangediff = lastdatechangediff .. " sec"
+ end
+ end
+ return list, #clientlist, lastdatechangetxt, lastdatechangediff
+end
+
+-- ################################################################################
+-- PUBLIC FUNCTIONS
+
+function mymodule.getstatus()
+ return modelfunctions.getstatus(processname, packagename, "OpenLDAP Status")
+end
+
+function mymodule.get_startstop(self, clientdata)
+ return modelfunctions.get_startstop(processname)
+end
+
+function mymodule.startstop_service(self, startstop, action)
+ return modelfunctions.startstop_service(startstop, action)
+end
+
+function mymodule.getclientinfo()
+ local config = config_content(configfile)
+ return cfe({ type="structure", value=clientlist(config.status), label="Client info" })
+end
+
+function mymodule.get_config()
+ local config = config_content(configfile)
+ check_valid_config(config)
+ if config.type == "server" then
+ local clientlist, client_count, client_lastupdate, client_lastdatechangediff = clientlist(config.status)
+ config["client_lastupdate"] = client_lastupdate or "?"
+ config["client_lastdatechangediff"] = client_lastdatechangediff or "? min"
+ config["client_count"] = client_count or 0
+ end
+ return cfe({ type="structure", value=config, label="OpenVPN Config" })
+end
+
+function mymodule.get_logfile(f)
+ local config = config_content(configfile)
+ return cfe({ value=config.log or "", label="Log file" })
+end
+
+function mymodule.get_filecontent()
+ --FIXME validate
+ return modelfunctions.getfiledetails(configfile)
+end
+
+function mymodule.update_filecontent(self, filedetails)
+ --FIXME validate
+ return modelfunctions.setfiledetails(self, filedetails, {configfile})
+end
+
+function mymodule.list_certs()
+ local list = {}
+ for file in fs.find(".*%.pem", certurl) do
+ list[#list+1] = file
+ end
+ return cfe({ type="list", value=list, label="OpenVPN Certificates" })
+end
+
+function mymodule.get_delete_cert(self, clientdata)
+ local retval = {}
+ retval.cert = cfe({ value=clientdata.cert or "", label="Certificate Local Name" })
+ return cfe({ type="group", value=retval, label="Delete Certificate" })
+end
+
+function mymodule.delete_cert(self, delcert)
+ local list = mymodule.list_certs()
+ delcert.value.cert.errtxt = "Invalid cert name"
+ delcert.errtxt = "Failed to delete certificate"
+ for i,cert in ipairs(list.value) do
+ if cert == delcert.value.cert.value then
+ os.remove(cert)
+ delcert.value.cert.errtxt = nil
+ delcert.errtxt = nil
+ break
+ end
+ end
+ return delcert
+end
+
+function mymodule.new_upload_cert()
+ local value = {}
+ value.cert = cfe({ type="raw", value=0, label="Certificate", descr='File must be a password protected ".pfx" file' })
+ value.password = cfe({ label="Certificate Password" })
+ value.name = cfe({ label="Certificate Local Name" })
+ return cfe({ type="group", value=value })
+end
+
+function mymodule.upload_cert(self, newcert)
+ local success = true
+ -- Trying to upload a cert/key
+ -- The way haserl works, cert contains the temporary file name
+ -- First, get the cert
+ local cmd, f, cmdresult, errtxt
+ if validator.is_valid_filename(newcert.value.cert.value, "/tmp/") and fs.is_file(newcert.value.cert.value) then
+ cmdresult, errtxt = modelfunctions.run_executable({"openssl", "pkcs12", "-in", newcert.value.cert.value, "-out", newcert.value.cert.value.."cert.pem", "-password", "pass:"..newcert.value.password.value, "-nokeys", "-clcerts"}, true)
+ local filestats = posix.stat(newcert.value.cert.value.."cert.pem")
+ if not filestats or filestats.size == 0 then
+ newcert.value.cert.errtxt = "Could not open certificate\n"..(errtxt or cmdresult)
+ success = false
+ end
+ else
+ newcert.value.cert.errtxt = "Invalid certificate"
+ success = false
+ end
+
+ -- Now, get the key and the ca certs
+ if success then
+ cmdresult, errtxt = modelfunctions.run_executable({"openssl", "pkcs12", "-in", newcert.value.cert.value, "-out", newcert.value.cert.value.."key.pem", "-password", "pass:"..newcert.value.password.value, "-nocerts", "-nodes"}, true)
+ filestats = posix.stat(newcert.value.cert.value.."key.pem")
+ if not filestats or filestats.size == 0 then
+ newcert.value.cert.errtxt = "Could not find key\n"..(errtxt or cmdresult)
+ success = false
+ end
+
+ cmdresult, errtxt = modelfunctions.run_executable({"openssl", "pkcs12", "-in", newcert.value.cert.value, "-out", newcert.value.cert.value.."ca.pem", "-password", "pass:"..newcert.value.password.value, "-nokeys", "-cacerts"}, true)
+ filestats = posix.stat(newcert.value.cert.value.."ca.pem")
+ if not filestats or filestats.size == 0 then
+ newcert.value.cert.errtxt = "Could not find CA certs\n"..(errtxt or cmdresult)
+ success = false
+ end
+ end
+
+ if newcert.value.name.value == "" then
+ newcert.value.name.errtxt = "Cannot be blank"
+ success = false
+ elseif posix.stat(certurl..newcert.value.name.value.."-cert.pem") or posix.stat(certurl..newcert.value.name.value.."-key.pem") or posix.stat(certurl..newcert.value.name.value.."-ca.pem") then
+ newcert.value.name.errtxt = "Certificate of this name already exists"
+ success = false
+ end
+
+ if success then
+ if not posix.stat(certurl) then
+ fs.create_directory(certurl)
+ end
+ -- copy the keys
+ fs.move_file(newcert.value.cert.value.."cert.pem", certurl..newcert.value.name.value.."-cert.pem")
+ fs.move_file(newcert.value.cert.value.."key.pem", certurl..newcert.value.name.value.."-key.pem")
+ fs.move_file(newcert.value.cert.value.."ca.pem", certurl..newcert.value.name.value.."-ca.pem")
+ posix.chmod(certurl..newcert.value.name.value.."-key.pem", "rw-------")
+ else
+ newcert.errtxt = "Failed to upload certificate"
+ end
+
+ -- Delete the temporary files
+ if validator.is_valid_filename(newcert.value.cert.value, "/tmp/") and fs.is_file(newcert.value.cert.value) then
+ os.remove(newcert.value.cert.value.."cert.pem")
+ os.remove(newcert.value.cert.value.."key.pem")
+ os.remove(newcert.value.cert.value.."ca.pem")
+ end
+
+ return newcert
+end
+
+mymodule.view_cert = function(certname)
+ local cmdresult = "Invalid cert name"
+ local errtxt
+ if not string.find(certname, "/") then
+ certname = certurl..certname
+ end
+ if validator.is_valid_filename(certname, certurl) or validator.is_valid_filename(certname, baseurl) then
+ cmdresult, errtxt = modelfunctions.run_executable({"openssl", "x509", "-in", certname, "-noout", "-text"})
+ cmdresult = cmdresult .. "Content:\n" .. (fs.read_file(certname) or "")
+ end
+ return cfe({ type="table", value={name=certname, value=cmdresult}, label="Certificate", errtxt=errtxt })
+end
+
+mymodule.get_generate_dh_params = function(self, clientdata)
+ local retval = {}
+ return cfe({ type="group", value=retval, label="Generate Diffie Hellman parameters" })
+end
+
+mymodule.generate_dh_params = function(self, gen)
+ if not posix.stat(certurl) then
+ fs.create_directory(certurl)
+ end
+ gen.descr, gen.errtxt = modelfunctions.run_executable({"openssl", "dhparam", "-out", certurl.."dh1024.pem", "1024"}, true)
+ return gen
+end
+
+return mymodule
diff --git a/openldap-status-html.lsp b/openldap-status-html.lsp
new file mode 120000
index 0000000..b2f8480
--- /dev/null
+++ b/openldap-status-html.lsp
@@ -0,0 +1 @@
+../status-html.lsp \ No newline at end of file
diff --git a/openldap-viewconfig-html.lsp b/openldap-viewconfig-html.lsp
new file mode 100644
index 0000000..5b76598
--- /dev/null
+++ b/openldap-viewconfig-html.lsp
@@ -0,0 +1,77 @@
+<% local view, viewlibrary, page_info, session = ... %>
+<% format = require("acf.format") %>
+<% html = require("acf.html") %>
+<% local shortname = string.gsub(view.value.name, "^.*/", "") %>
+
+<h1><%= html.html_escape(format.cap_begin_word(view.value.type)) %> Config</h1>
+
+<h2><%= html.html_escape(format.cap_begin_word(view.value.type)) %> settings</h2>
+<dl>
+<dt>Mode</dt>
+<dd><%= html.html_escape(view.value.type) %></dd>
+
+<dt>User device</dt>
+<dd><%= html.html_escape(view.value.dev) %></dd>
+
+<% if view.value.type == "server" then %>
+<dt>Listens on</dt>
+<dd><%= html.html_escape(view.value["local"]) %>:<%= html.html_escape(view.value.port) %> (<%= html.html_escape(view.value.proto) %>)</dd>
+<% end %>
+
+<% if view.value.type == "client" then %>
+<dt>Remote server</dt>
+<dd><% if string.find(view.value.remote, "%s") then io.write(html.html_escape(string.gsub(view.value.remote, "%s+", ":"))) else io.write(html.html_escape(view.value.remote .. (view.value.rport or view.value.port or "1194"))) end %> (<%= html.html_escape(view.value.proto) %>)</dd>
+<% end %>
+
+<dt>Logfile</dt>
+<dd><% if ( view.value.log ) then %><%= html.link{value = page_info.script .. page_info.prefix .. page_info.controller .. "/logfile?name=" .. view.value.name, label=view.value.log } %><% else %>Syslog<% end %> (Verbosity level: <%= html.html_escape(view.value.verb) %>)</dd>
+</dl>
+
+<% if view.value.type == "server" then %>
+<h3>Connected clients status</h3>
+<dl>
+<dt>Last status was recorded</dt>
+<dd><%= html.html_escape(view.value.client_lastupdate) %> (This was <b><%= html.html_escape(view.value.client_lastdatechangediff) %></b> ago)</dd>
+
+<dt>Maximum clients</dt>
+<dd><%= html.html_escape(view.value["max-clients"]) %></dd>
+
+<dt>Connected clients</dt>
+<dd><%= html.html_escape(view.value.client_count) %></dd>
+</dl>
+<% end %>
+
+<% if view.value.dh or view.value.ca or view.value.cert or view.value.key or view.value.tls or view.value.crl then %>
+<h2>Certificate files</h2>
+<dl>
+<% if (view.value.dh) then %>
+<dt>DH</dt>
+<dd><%= html.link{value = page_info.script .. page_info.prefix .. page_info.controller .. "/viewcert?cert=" .. view.value.dh, label=view.value.dh } %></dd>
+<% end %>
+
+<% if (view.value.ca) then %>
+<dt>CA Certificate</dt>
+<dd><%= html.link{value = page_info.script .. page_info.prefix .. page_info.controller .. "/viewcert?cert=" .. view.value.ca, label=view.value.ca } %></dd>
+<% end %>
+
+<% if (view.value.cert) then %>
+<dt>Certificate</dt>
+<dd><%= html.link{value = page_info.script .. page_info.prefix .. page_info.controller .. "/viewcert?cert=" .. view.value.cert, label=view.value.cert } %></dd>
+<% end %>
+
+<% if (view.value.key) then %>
+<dt>Private Key</dt>
+<dd><%= html.html_escape(view.value.key) %></dd>
+<% end %>
+
+<% if (view.value.tls) then %>
+<dt>TLS Authentication</dt>
+<dd><%= html.html_escape(view.value.tls) %><% -- html.link{value = page_info.script .. page_info.prefix .. page_info.controller .. "/pem_info?name=" .. view.value.tls , label=view.value.tls } %></dd>
+<% end %>
+
+<% if (view.value.crl) then %>
+<dt>CRL Verify File</dt>
+<dd><%= html.html_escape(view.value.crl) %><% -- html.link{value = page_info.script .. page_info.prefix .. page_info.controller .. "/pem_info?name=" .. view.value.crl , label=view.value.crl } %></dd>
+<% end %>
+</dl>
+<% end %>
diff --git a/openldap.menu b/openldap.menu
new file mode 100644
index 0000000..4ebd263
--- /dev/null
+++ b/openldap.menu
@@ -0,0 +1,4 @@
+#CAT GROUP/DESC TAB ACTION
+Networking 65OpenLDAP Status status
+Networking 65OpenLDAP Expert expert
+Networking 65OpenLDAP Log_File logfile
diff --git a/openldap.roles b/openldap.roles
new file mode 100644
index 0000000..6d275ce
--- /dev/null
+++ b/openldap.roles
@@ -0,0 +1,3 @@
+USER=openldap:status,openldap:logfile,openldap:viewconfig,openldap:startstop
+EXPERT=openldap:expert,openldap:listcerts,openldap:deletecert,openldap:uploadcert,openldap:viewcert,openldap:generatedhparams
+ADMIN=openldap:status,openldap:logfile,openldap:viewconfig,openldap:startstop,openldap:expert,openldap:listcerts,openldap:deletecert,openldap:uploadcert,openldap:viewcert,openldap:generatedhparams