From 0ab1bd202395837215dfd560f0247dca42d1610f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@waks.be>
Date: Tue, 17 Oct 2023 09:47:10 +0200
Subject: [PATCH] fix: change password for builtin users

---
 README.md                         | 11 +++++++++
 library/elasticsearch_user.py     | 40 ++++++++++++++++++++++++++-----
 module_utils/elasticsearch_api.py | 14 +++++++++++
 molecule/default/converge.yml     |  2 ++
 4 files changed, 61 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 2f1897e..f4735e5 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,17 @@ Install and configure Elasticsearch
   path.logs: /var/log/elasticsearch
 ```
 
+* `elasticsearch_users` - hash with the users to managed
+
+```yaml
+  toto:
+    password: supers3cret
+    roles:
+      - viewer
+  kibana_system:
+    password: supertest2
+```
+
 * `elasticsearch_index_templates` - hash with the index templates configuration
 
 ```
diff --git a/library/elasticsearch_user.py b/library/elasticsearch_user.py
index a4f89f0..770f693 100644
--- a/library/elasticsearch_user.py
+++ b/library/elasticsearch_user.py
@@ -17,6 +17,21 @@ class ElasticsearchUser:
         self.exist    = False
         self.data     = {}
 
+    def is_builtin(self):
+        users = [
+            'apm_system',
+            'beats_system',
+            'elastic',
+            'kibana',
+            'kibana_system',
+            'logstash_system',
+            'remote_monitoring_user'
+        ]
+        if self.name in users:
+            return True
+
+        return False
+
     def get_data(self):
         status_code, data = self.api.get('_security/user/{}'.format(self.name))
         if status_code == 200:
@@ -64,6 +79,14 @@ class ElasticsearchUser:
             }
         )
 
+    def change_password(self):
+        self.api.post(
+            '_security/user/{}/_password'.format(self.name),
+            {
+                'password': self.password
+            }
+        )
+
     def delete(self):
         self.api.delete('_security/user/{}'.format(self.name))
 
@@ -91,14 +114,19 @@ def main():
     )
     user.get_data()
 
-    if module.params['state'] == 'present':
-        if not user.exist or user.has_changed():
-            user.create()
+    if user.is_builtin():
+        if user.password_has_changed():
+            user.change_password()
             changed = True
     else:
-        if user.exist:
-            user.delete()
-            changed = True
+        if module.params['state'] == 'present':
+            if not user.exist or user.has_changed():
+                user.create()
+                changed = True
+        else:
+            if user.exist:
+                user.delete()
+                changed = True
 
     module.exit_json(changed=changed)
 
diff --git a/module_utils/elasticsearch_api.py b/module_utils/elasticsearch_api.py
index 02a58c4..5743ed0 100644
--- a/module_utils/elasticsearch_api.py
+++ b/module_utils/elasticsearch_api.py
@@ -20,6 +20,20 @@ class ElasticsearchApi:
       
     return r.status_code, r.json()
 
+  def post(self, path, data):
+    r = requests.post(
+      '{}/{}'.format(self.url, path),
+      auth=self.basic,
+      json=data
+    )
+    
+    if r.status_code == 500:
+      raise Exception('Server return 500 error: {}'.format(r.text))
+    elif r.status_code == 401:
+      raise Exception('Authentification has failed')
+    elif r.status_code != 200:
+      raise Exception('Server return an unknown error: {}'.format(r.text))
+
   def put(self, path, data):
     r = requests.put(
       '{}/{}'.format(self.url, path),
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
index 1e806a7..1a092ba 100644
--- a/molecule/default/converge.yml
+++ b/molecule/default/converge.yml
@@ -11,6 +11,8 @@
         password: supers3cret
         roles:
           - viewer
+      kibana_system:
+        password: supertest2
     elasticsearch_index_templates:
       test:
         index_patterns: