Implemented LDAP config and matrix auth security enforcements.
diff --git a/README.rst b/README.rst
index 19a7565..f74f006 100644
--- a/README.rst
+++ b/README.rst
@@ -320,6 +320,56 @@
             enabled: false
             restart: true
 
+LDAP configuration (depends on LDAP plugin)
+
+.. code-block:: yaml
+
+    jenkins:
+      client:
+        security:
+          ldap:
+            server: 1.2.3.4
+            root_dn: dc=foo,dc=com
+            user_search_base: cn=users,cn=accounts
+            manager_dn: ""
+            manager_password: password
+            user_search: ""
+            group_search_base: ""
+            inhibit_infer_root_dn: false
+
+
+Matrix configuration (depends on auth-matrix plugin)
+
+.. code-block:: yaml
+
+    jenkins:
+      client:
+        security:
+          matrix:
+            permissions:
+              Jenkins:
+                # administrator access
+                ADMINISTER:
+                  - admin
+                # read access (anonymous too)
+                READ:
+                  - anonymous
+                  - user1
+                  - user2
+                # agents permissions
+                MasterComputer: 
+                  BUILD: 
+                    - user3
+              # jobs permissions
+              hudson: 
+                model:
+                  Item:
+                    BUILD: 
+                      - user4
+
+`Common matrix strategies <https://github.com/arbabnazar/configuration/blob/c08a5eaf4e04a68d2481375502a926517097b253/playbooks/roles/tools_jenkins/templates/projectBasedMatrixSecurity.groovy.j2>`_
+
+
 Credentials enforcing from client
 
 .. code-block:: yaml
diff --git a/_modules/jenkins_common.py b/_modules/jenkins_common.py
index 493aebe..50ed64d 100644
--- a/_modules/jenkins_common.py
+++ b/_modules/jenkins_common.py
@@ -44,9 +44,9 @@
                         auth=(jenkins_user, jenkins_password),
                         data=req_data)
     ret["code"] = req.status_code
+    ret["msg"] = req.text
     if req.status_code in success_status_codes:
         ret["status"] = "SUCCESS"
-        ret["msg"] = req.text
         logger.debug("Jenkins script API call success: %s", ret)
     else:
         logger.error("Jenkins script API call failed. \
diff --git a/_states/jenkins_security.py b/_states/jenkins_security.py
new file mode 100644
index 0000000..24a8a67
--- /dev/null
+++ b/_states/jenkins_security.py
@@ -0,0 +1,156 @@
+import logging
+logger = logging.getLogger(__name__)
+
+set_ldap_groovy = """\
+import jenkins.model.*
+import hudson.security.*
+import org.jenkinsci.plugins.*
+
+def server = 'ldap://{server}'
+def rootDN = '{rootDN}'
+def userSearchBase = '{userSearchBase}'
+def userSearch = '{userSearch}'
+def groupSearchBase = '{groupSearchBase}'
+def managerDN = '{managerDN}'
+def managerPassword = '{managerPassword}'
+boolean inhibitInferRootDN = {inhibitInferRootDN}
+
+try{{
+ldapRealm = Class.forName("hudson.security.LDAPSecurityRealm").getConstructor(String.class, String.class, String.class, String.class, String.class, String.class, String.class, Boolean.TYPE)
+.newInstance(server, rootDN, userSearchBase, userSearch, groupSearchBase, managerDN, managerPassword, inhibitInferRootDN) 
+Jenkins.instance.setSecurityRealm(ldapRealm)
+Jenkins.instance.save()
+print("SUCCESS")
+}}catch(ClassNotFoundException e){{
+    print("Cannot instantiate LDAPSecurityRealm, maybe ldap plugin not installed")
+}}
+"""  # noqa
+
+set_matrix_groovy = """\
+import jenkins.model.*
+import hudson.security.*
+import com.cloudbees.plugins.credentials.*
+
+def instance = Jenkins.getInstance()
+try{{
+def strategy = Class.forName("hudson.security.ProjectMatrixAuthorizationStrategy").newInstance()
+{strategies}
+instance.setAuthorizationStrategy(strategy)
+instance.save()
+print("SUCCESS")
+}}catch(ClassNotFoundException e){{
+    print("Cannot instantiate ProjectMatrixAuthorizationStrategy, maybe auth-matrix plugin not installed")
+}}
+""" # noqa
+
+
+def ldap(name, server, root_dn, user_search_base, manager_dn, manager_password, user_search="", group_search_base="", inhibit_infer_root_dn=False):
+    """
+    Jenkins ldap state method
+
+    :param name: ldap state name
+    :param server: ldap server host (without ldap://)
+    :param root_dn: root domain names
+    :param user_search_base:
+    :param manager_dn:
+    :param manager_password:
+    :param user_search: optional, default empty string
+    :param group_search_base: optional, default empty string
+    :param inhibit_infer_root_dn: optional, default false
+    :returns: salt-specified state dict
+    """
+    test = __opts__['test']  # noqa
+    ret = {
+        'name': name,
+        'changes': {},
+        'result': False,
+        'comment': '',
+    }
+    result = False
+    if test:
+        status = 'CREATED'
+        ret['changes'][name] = status
+        ret['comment'] = 'LDAP setup %s %s' % (name, status.lower())
+    else:
+        call_result = __salt__['jenkins_common.call_groovy_script'](
+            set_ldap_groovy, {"name": name, "server": server, "rootDN": root_dn,
+                              "userSearchBase": user_search_base, "managerDN": manager_dn, "managerPassword": manager_password, "userSearch": user_search if user_search else "", "groupSearchBase": group_search_base if group_search_base else "", "inhibitInferRootDN": "true" if inhibit_infer_root_dn else "false"})
+        if call_result["code"] == 200 and call_result["msg"] == "SUCCESS":
+            status = call_result["msg"]
+            ret['changes'][name] = status
+            ret['comment'] = 'Jenkins LDAP setting %s %s' % (name, status.lower())
+            result = True
+        else:
+            status = 'FAILED'
+            logger.error(
+                "Jenkins security API call failure: %s", call_result["msg"])
+            ret['comment'] = 'Jenkins security API call failure: %s' % (call_result[
+                                                                           "msg"])
+    ret['result'] = None if test else result
+    return ret
+
+def matrix(name, strategies):
+    """
+    Jenkins matrix security state method
+
+    :param name: ldap state name
+    :param strategies: dict with matrix strategies 
+    :returns: salt-specified state dict
+    """
+    test = __opts__['test']  # noqa
+    ret = {
+        'name': name,
+        'changes': {},
+        'result': False,
+        'comment': '',
+    }
+    result = False
+    if test:
+        status = 'CREATED'
+        ret['changes'][name] = status
+        ret['comment'] = 'LDAP setup %s %s' % (name, status.lower())
+    else:
+        call_result = __salt__['jenkins_common.call_groovy_script'](
+            set_matrix_groovy, {"strategies":_build_strategies(strategies)})
+        if call_result["code"] == 200 and call_result["msg"] == "SUCCESS":
+            status = call_result["msg"]
+            ret['changes'][name] = status
+            ret['comment'] = 'Jenkins Matrix security setting %s %s' % (name, status.lower())
+            result = True
+        else:
+            status = 'FAILED'
+            logger.error(
+                "Jenkins security API call failure: %s", call_result["msg"])
+            ret['comment'] = 'Jenkins security API call failure: %s' % (call_result[
+                                                                           "msg"])
+    ret['result'] = None if test else result
+    return ret
+
+
+def _build_strategies(permissions):
+    strategies_str = ""
+    for strategy in _to_strategies_list("strategy.add({},\"{}\")", _to_one_dict(permissions, "")):
+        strategies_str += "{}\n".format(strategy)
+    return strategies_str
+
+
+def _to_strategies_list(strategy_format, strategy_dict):
+    res = []
+    for key, value in strategy_dict.items():
+        if isinstance(value, list):
+            for user in value:
+                res.append(strategy_format.format(key, user))
+        else:
+            res.append(strategy_format.format(key, value))
+    return res
+
+
+def _to_one_dict(input_dict, input_key):
+    res = {}
+    for key, value in input_dict.items():
+        new_key = key if input_key == "" else "{}.{}".format(input_key, key)
+        if isinstance(value, dict):
+            res.update(_to_one_dict(value, new_key))
+        else:
+            res[new_key] = value
+    return res
diff --git a/jenkins/client/init.sls b/jenkins/client/init.sls
index c7f83fb..852aadc 100644
--- a/jenkins/client/init.sls
+++ b/jenkins/client/init.sls
@@ -2,13 +2,28 @@
 {%- if client.enabled %}
 
 include:
+{%- if client.plugin is defined %}
   - jenkins.client.plugin
+{%- endif %}
+{%- if client.security is defined %}
+  - jenkins.client.security
+{%- endif %}
+{%- if client.source is defined %}
   - jenkins.client.source
+{%- endif %}
+{%- if client.job is defined %}
   - jenkins.client.job
+{%- endif %}
+{%- if client.credential is defined %}
   - jenkins.client.credential
+{%- endif %}
+{%- if client.user is defined %}
   - jenkins.client.user
+{%- endif %}
+{%- if client.node is defined %}
   - jenkins.client.node
-  
+{%- endif %}
+
 jenkins_client_install:
   pkg.installed:
   - names: {{ client.pkgs }}
diff --git a/jenkins/client/security.sls b/jenkins/client/security.sls
new file mode 100644
index 0000000..9c66e70
--- /dev/null
+++ b/jenkins/client/security.sls
@@ -0,0 +1,19 @@
+{%- from "jenkins/map.jinja" import client with context %}
+{%- if client.security.ldap is defined %}
+set_jenkins_ldap:
+  jenkins_security.ldap:
+    - server: {{ client.security.ldap.server }}
+    - root_dn: {{ client.security.ldap.get('root_dn','') }}
+    - user_search_base: {{ client.security.ldap.get('user_search_base','') }}
+    - manager_dn: {{ client.security.ldap.get('manager_dn','') }}
+    - manager_password: {{ client.security.ldap.get('manager_password','') }}
+    - user_search: {{ client.security.ldap.get('user_search','') }}
+    - group_search_base: {{ client.security.ldap.get('group_search_base', '') }}
+    - inhibit_infer_root_dn: {{ client.security.ldap.get('inhibit_infer_root_dn', False) }}
+{%- endif %}
+
+{%- if client.security.matrix is defined %}
+set_jenkins_matrix_security:
+  jenkins_security.matrix:
+    - strategies: {{ client.security.matrix.permissions }}
+{%- endif %}
\ No newline at end of file